From e5117af1ea3854497394d2cfe4708ae8cd6099c6 Mon Sep 17 00:00:00 2001 From: Andrew Blakeslee Moore Date: Sun, 5 Jul 2026 18:04:57 -0700 Subject: [PATCH] Bottom Tab Bar Visibility Adjustment Nucleic-Session: 747527BF-AEE9-4939-BE70-A14F1F3BBBA8 Co-authored-by: Nucleic --- .../NucleicRemote/Views/Composer.swift | 59 ++++++++----------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/NucleicRemote/NucleicRemote/Views/Composer.swift b/NucleicRemote/NucleicRemote/Views/Composer.swift index 093bd8b..4060189 100644 --- a/NucleicRemote/NucleicRemote/Views/Composer.swift +++ b/NucleicRemote/NucleicRemote/Views/Composer.swift @@ -129,41 +129,15 @@ struct StartChatComposer: View { } } -/// The sheet the floating "+" opens: the glass `StartChatComposer` in a compact panel. `project` -/// scopes it to one project (a project page's "+"); nil offers the picker (Home, Sessions). -struct NewChatSheet: View { - var project: WireProject? = nil - var dismiss: () -> Void - - var body: some View { - NavigationStack { - ScrollView { - StartChatComposer(onStart: dismiss, lockedProject: project) - .padding() - } - .navigationTitle("New chat") - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .topBarLeading) { - Button("Cancel") { dismiss() } - } - } - } - // The composer needs only a fraction of the screen — a compact height by default, still - // expandable if the advanced options are opened. - .presentationDetents([.height(360), .large]) - .presentationDragIndicator(.visible) - } -} - -/// The floating "+" start-chat affordance: a Liquid Glass circle in the lower-right that opens the -/// `NewChatSheet`. Shared by Home, Sessions, and a project page (which scopes it to `project`), so -/// all three offer the same control instead of an inline composer card. +/// The floating "+" start-chat affordance: a Liquid Glass circle in the lower-right that reveals +/// the glass `StartChatComposer` as a floating bar over the page — the same entry box the open +/// session carries, not a full modal pane. Shared by Home, Sessions, and a project page (which +/// scopes it to `project`), so all three offer the same control. struct NewChatAffordance: ViewModifier { @EnvironmentObject var store: RemoteStore /// Scopes the composer to one project (project page); nil offers the picker (Home, Sessions). var project: WireProject? = nil - /// Owned by the caller so it can also open the sheet programmatically (iPad ⌘N on Home). + /// Owned by the caller so it can also open the composer programmatically (iPad ⌘N on Home). @Binding var isPresented: Bool /// The button only makes sense when this device can control a Mac and there's a project to @@ -174,8 +148,9 @@ struct NewChatAffordance: ViewModifier { func body(content: Content) -> some View { content + // The "+" disc — hidden while the composer is up so it doesn't sit over the bar. .overlay(alignment: .bottomTrailing) { - if canStart { + if canStart && !isPresented { Button { isPresented = true } label: { Image(systemName: "plus") .font(.system(size: 22, weight: .semibold)) @@ -190,12 +165,28 @@ struct NewChatAffordance: ViewModifier { .buttonStyle(.plain) .padding(.trailing, 20) .padding(.bottom, 6) + .transition(.scale.combined(with: .opacity)) .accessibilityLabel("New chat") } } - .sheet(isPresented: $isPresented) { - NewChatSheet(project: project) { isPresented = false } + // The composer itself, floating up from the bottom over a light scrim — no modal pane, + // no nav chrome, just the entry box. Tapping the scrim dismisses it. + .overlay { + if isPresented { + ZStack(alignment: .bottom) { + Rectangle() + .fill(.black.opacity(0.2)) + .ignoresSafeArea() + .transition(.opacity) + .onTapGesture { isPresented = false } + StartChatComposer(onStart: { isPresented = false }, lockedProject: project) + .padding(.horizontal, 12) + .padding(.bottom, 8) + .transition(.move(edge: .bottom).combined(with: .opacity)) + } + } } + .animation(.spring(response: 0.35, dampingFraction: 0.86), value: isPresented) } }