From 1d344b08ec0cf3a209346970ea8d9595d7c894b3 Mon Sep 17 00:00:00 2001 From: Andrew Blakeslee Moore Date: Sun, 5 Jul 2026 18:08:12 -0700 Subject: [PATCH] updating canary files --- .../NucleicRemote/Views/Composer.swift | 72 +++++++++---------- VERSION | 2 +- 2 files changed, 34 insertions(+), 40 deletions(-) diff --git a/NucleicRemote/NucleicRemote/Views/Composer.swift b/NucleicRemote/NucleicRemote/Views/Composer.swift index 7bd501a..fc49a3c 100644 --- a/NucleicRemote/NucleicRemote/Views/Composer.swift +++ b/NucleicRemote/NucleicRemote/Views/Composer.swift @@ -1,9 +1,9 @@ import SwiftUI import NucleicProtocol -/// Start a new chat, presented in the `NewChatSheet` that the floating "+" opens. Styled as the -/// same Liquid Glass composer bar the open session carries, for consistency: picks a project (or -/// is scoped to one), types a prompt, optionally toggles Auto. +/// Start a new chat, revealed as a floating bar when the "+" is tapped (see `NewChatAffordance`). +/// Styled as the same Liquid Glass composer bar the open session carries, for consistency: picks a +/// project (or is scoped to one), types a prompt, optionally toggles Auto. struct StartChatComposer: View { @EnvironmentObject var store: RemoteStore /// Called after a chat is kicked off, so the presenting sheet can dismiss itself. @@ -113,8 +113,11 @@ struct StartChatComposer: View { Text("This device is view-only.").font(.caption2).foregroundStyle(.secondary) } } - // No surface of its own — the controls sit directly on the sheet, which is already a - // distinct panel; a glass card here would just be a card-on-card. + // The same Liquid Glass bar the open session carries, so the new-chat entry box is visually + // identical to the in-chat composer. + .padding(.horizontal, 14) + .padding(.vertical, 12) + .glassSurface(cornerRadius: 26) // Switching to a model with a lower effort cap can't leave an unsupported level selected. .onChange(of: model) { _, newModel in let levels = store.modelCatalog.offeredEfforts(forModel: newModel) @@ -126,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 @@ -171,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)) @@ -187,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) } } diff --git a/VERSION b/VERSION index 9e37c72..b979118 100644 --- a/VERSION +++ b/VERSION @@ -10,4 +10,4 @@ # NUCLEIC_MARKETING_VERSION semantic version -> CFBundleShortVersionString # NUCLEIC_BUILD_NUMBER monotonic build -> CFBundleVersion (always +1 per release) NUCLEIC_MARKETING_VERSION=0.2.0 -NUCLEIC_BUILD_NUMBER=13 +NUCLEIC_BUILD_NUMBER=5