nvrsion: Add: Resolve conflict, reapply tab bar load animation fixes, refactor chat tool call card alignment, fix historical transcript data fetching, fix freezing during exit, refine modal design, adjust spacing, hide bottom tab bar on sessions, adjust “New Chat” button, fix LAN routing issues, implement C fix, remove incorrect backgrounds, remove full modal, refine swiping behavior, adjust Model Picker Label, refine tab bar animation, adjust settings, refine latency, fix keyboard delay, remove Trusted Tester Section, remove Setting Descriptions, fix concurrent session issues in tab bar load animation, fix tab bar load animation overlap at bottom.
Nucleic-Promote: 1 Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
@@ -61,15 +61,16 @@ struct CompactRootView: View {
|
||||
|
||||
var body: some View {
|
||||
TabView(selection: $tab) {
|
||||
// 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)
|
||||
HomeView().toolbar(.hidden, for: .tabBar).tag(0)
|
||||
SessionsView().toolbar(.hidden, for: .tabBar).tag(1)
|
||||
ProjectsView().toolbar(.hidden, for: .tabBar).tag(2)
|
||||
SettingsView().toolbar(.hidden, for: .tabBar).tag(3)
|
||||
}
|
||||
// Each tab's scroll view reads this and reserves room for the floating bar directly on
|
||||
// itself (via `.compactTabBarInset()`). Neither a `safeAreaInset` nor `contentMargins` on
|
||||
// the tab root reaches the pages' scroll content through the `TabView`, so it must be applied
|
||||
// to the `List`/`ScrollView` itself. Zero (its default) on iPad, which has no bar.
|
||||
.environment(\.compactTabBarInset, contentInset)
|
||||
// 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)
|
||||
// instead of a transition that removes it mid-descent and makes it pop.
|
||||
@@ -126,17 +127,33 @@ private struct BarHeightKey: PreferenceKey {
|
||||
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() }
|
||||
}
|
||||
|
||||
/// Per-tab chrome for the compact shell: suppress the system tab bar (we draw our own) and reserve
|
||||
/// `height` at the bottom of this tab's content so its scroll view lays out above the floating bar.
|
||||
/// Applied to each tab rather than the `TabView`, whose container-level inset doesn't reach the tab
|
||||
/// pages' scroll content.
|
||||
private struct TabBarInset: ViewModifier {
|
||||
let height: CGFloat
|
||||
/// Height the compact shell's floating tab bar wants reserved at the bottom of a tab's scroll
|
||||
/// content, published down the environment by `CompactRootView`. Zero on iPad (no bar).
|
||||
private struct CompactTabBarInsetKey: EnvironmentKey {
|
||||
static let defaultValue: CGFloat = 0
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
var compactTabBarInset: CGFloat {
|
||||
get { self[CompactTabBarInsetKey.self] }
|
||||
set { self[CompactTabBarInsetKey.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
/// Reserve room for the compact shell's floating tab bar at the bottom of a scroll view, so its
|
||||
/// content clears the bar when scrolled to the end. Apply this directly to the `List`/`ScrollView`
|
||||
/// inside a tab — a bottom `safeAreaInset` on the scroll view itself reliably insets its content,
|
||||
/// unlike one on the tab root, which the `TabView` doesn't forward. A no-op at regular width
|
||||
/// (iPad), where the environment inset is 0.
|
||||
func compactTabBarInset() -> some View { modifier(CompactTabBarInsetReader()) }
|
||||
}
|
||||
|
||||
private struct CompactTabBarInsetReader: ViewModifier {
|
||||
@Environment(\.compactTabBarInset) private var inset
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.toolbar(.hidden, for: .tabBar)
|
||||
.safeAreaInset(edge: .bottom, spacing: 0) {
|
||||
Color.clear.frame(height: height)
|
||||
content.safeAreaInset(edge: .bottom, spacing: 0) {
|
||||
Color.clear.frame(height: inset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ struct HomeView: View {
|
||||
}
|
||||
.navigationTitle("Home")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.compactTabBarInset()
|
||||
.scrollDismissesKeyboard(.interactively)
|
||||
.refreshable { store.refreshSessions() }
|
||||
.onChange(of: focusNewChat.wrappedValue) { _, want in
|
||||
|
||||
@@ -35,6 +35,7 @@ struct ProjectsView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.compactTabBarInset()
|
||||
}
|
||||
}
|
||||
.navigationTitle("Projects")
|
||||
@@ -71,6 +72,7 @@ struct ProjectDetailView: View {
|
||||
.navigationTitle(project.name)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.scrollDismissesKeyboard(.interactively)
|
||||
.compactTabBarInset()
|
||||
// A floating "+" scoped to this project — the glass composer opens pre-locked to it. Only
|
||||
// at regular width; on compact the shared tab-bar "+" (project picker) covers new chats.
|
||||
.newChatAffordance(project: project, isPresented: $showNewChat, enabled: sizeClass == .regular)
|
||||
|
||||
@@ -59,6 +59,7 @@ struct SessionsView: View {
|
||||
}
|
||||
.listStyle(.insetGrouped)
|
||||
.refreshable { store.refreshSessions() }
|
||||
.compactTabBarInset()
|
||||
}
|
||||
}
|
||||
.navigationTitle("Sessions")
|
||||
|
||||
@@ -158,6 +158,7 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
.navigationTitle("Settings")
|
||||
.compactTabBarInset()
|
||||
.sheet(isPresented: $showScanner) {
|
||||
PairingScannerView { payload in
|
||||
showScanner = false
|
||||
|
||||
Reference in New Issue
Block a user