From 591691979093a064744813540020012ca7f08bb6 Mon Sep 17 00:00:00 2001 From: Andrew Blakeslee Moore Date: Sun, 5 Jul 2026 19:05:14 -0700 Subject: [PATCH] Tab Bar Load Animation Nucleic-Session: EEC60FAE-8368-49CA-9202-7026C12E7F8C Co-authored-by: Nucleic --- .../Views/AdaptiveRootView.swift | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/NucleicRemote/NucleicRemote/Views/AdaptiveRootView.swift b/NucleicRemote/NucleicRemote/Views/AdaptiveRootView.swift index bf1daba..a4c68b3 100644 --- a/NucleicRemote/NucleicRemote/Views/AdaptiveRootView.swift +++ b/NucleicRemote/NucleicRemote/Views/AdaptiveRootView.swift @@ -55,18 +55,20 @@ struct CompactRootView: View { /// chat could actually be kicked off (mirrors `NewChatAffordance.canStart`). private var canStartChat: Bool { store.canControl && !store.dashboard.projects.isEmpty } + /// Height reserved under each tab's content for the floating bar — the measured bar height when + /// it's up, nothing when a session or the composer hides it (so those run full-bleed). + private var contentInset: CGFloat { showsTabBar ? barHeight : 0 } + var body: some View { TabView(selection: $tab) { - HomeView().tag(0).toolbar(.hidden, for: .tabBar) - SessionsView().tag(1).toolbar(.hidden, for: .tabBar) - ProjectsView().tag(2).toolbar(.hidden, for: .tabBar) - SettingsView().tag(3).toolbar(.hidden, for: .tabBar) - } - // Reserve exactly the bar's height so top-level content (and each tab's own bottom chrome) - // lays out above the floating bar rather than under it; collapses to nothing when a session - // hides the bar so the transcript runs full-bleed. - .safeAreaInset(edge: .bottom, spacing: 0) { - Color.clear.frame(height: showsTabBar ? barHeight : 0) + // The inset is applied per tab (not to the `TabView`): a bottom `safeAreaInset` on the + // container does NOT propagate into the tab pages' scroll content, so the list would run + // under the bar. On each tab's `NavigationStack` it reaches the scroll content and pushes + // it up above the bar. + HomeView().modifier(TabBarInset(height: contentInset)).tag(0) + SessionsView().modifier(TabBarInset(height: contentInset)).tag(1) + ProjectsView().modifier(TabBarInset(height: contentInset)).tag(2) + SettingsView().modifier(TabBarInset(height: contentInset)).tag(3) } // The bar itself lives in an always-mounted overlay and slides on `offset`, so it travels // continuously off the bottom of the screen (a fixed distance past the home-indicator curve)