From f7bb5cca2a6945c113123ec85b73b990c8791a1a Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Oct 2026 14:25:30 +0000 Subject: [PATCH] fix(mac): stop the toolbar update loop at launch The expired-session banner sat in a top safe-area inset under the window toolbar, and the study timer had its own toolbar modifier. Together they made SwiftUI merge and rebuild the toolbar endlessly, so the app hung at launch. The banner now sits above the content and the window has one toolbar. Refs #64 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01TmmqmKmX8VDSfoPbzTS1ew --- Presentation/RootView.swift | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/Presentation/RootView.swift b/Presentation/RootView.swift index e0f232b..d90bbb2 100644 --- a/Presentation/RootView.swift +++ b/Presentation/RootView.swift @@ -36,23 +36,16 @@ struct RootView: View { ) .navigationSplitViewColumnWidth(min: 220, ideal: 250, max: 340) } detail: { - detail - .safeAreaInset(edge: .top, spacing: 0) { - if studIPViewModel.sessionExpired, !isSessionBannerDismissed { - StudIPSessionBanner(viewModel: studIPViewModel) { isSessionBannerDismissed = true } - } + // The banner sits above the content instead of in a top safe-area inset: changing that inset + // under the window toolbar made SwiftUI rebuild the toolbar in an endless loop at launch. + VStack(spacing: 0) { + if studIPViewModel.sessionExpired, !isSessionBannerDismissed { + StudIPSessionBanner(viewModel: studIPViewModel) { isSessionBannerDismissed = true } } - } - .toolbar { historyButtons } - .toolbar { - ToolbarItem(placement: .primaryAction) { - StudyTimerToolbarButton( - viewModel: studyTimerViewModel, - modules: viewModel.activeModules, - currentModuleID: navigation.selectedModuleID - ) + detail } } + .toolbar { windowToolbar } .sheet(item: $assignmentsViewModel.editor) { draft in AssignmentEditorSheet( draft: draft, @@ -228,10 +221,11 @@ struct RootView: View { ) } - // MARK: Verlauf + // MARK: Toolbar + /// One toolbar for the whole window; separate `.toolbar` modifiers have to be merged on every update. @ToolbarContentBuilder - private var historyButtons: some ToolbarContent { + private var windowToolbar: some ToolbarContent { ToolbarItemGroup(placement: .navigation) { Button { navigation.goBack() } label: { Label("Zurück", systemImage: "chevron.backward") @@ -251,5 +245,12 @@ struct RootView: View { } .help("Gehe zu Modul oder Notiz (⌘K)") } + ToolbarItem(placement: .primaryAction) { + StudyTimerToolbarButton( + viewModel: studyTimerViewModel, + modules: viewModel.activeModules, + currentModuleID: navigation.selectedModuleID + ) + } } }