updating canary files
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import NucleicProtocol
|
import NucleicProtocol
|
||||||
|
|
||||||
/// Start a new chat, presented in the `NewChatSheet` that the floating "+" opens. Styled as the
|
/// Start a new chat, revealed as a floating bar when the "+" is tapped (see `NewChatAffordance`).
|
||||||
/// same Liquid Glass composer bar the open session carries, for consistency: picks a project (or
|
/// Styled as the same Liquid Glass composer bar the open session carries, for consistency: picks a
|
||||||
/// is scoped to one), types a prompt, optionally toggles Auto.
|
/// project (or is scoped to one), types a prompt, optionally toggles Auto.
|
||||||
struct StartChatComposer: View {
|
struct StartChatComposer: View {
|
||||||
@EnvironmentObject var store: RemoteStore
|
@EnvironmentObject var store: RemoteStore
|
||||||
/// Called after a chat is kicked off, so the presenting sheet can dismiss itself.
|
/// 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)
|
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
|
// The same Liquid Glass bar the open session carries, so the new-chat entry box is visually
|
||||||
// distinct panel; a glass card here would just be a card-on-card.
|
// 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.
|
// Switching to a model with a lower effort cap can't leave an unsupported level selected.
|
||||||
.onChange(of: model) { _, newModel in
|
.onChange(of: model) { _, newModel in
|
||||||
let levels = store.modelCatalog.offeredEfforts(forModel: newModel)
|
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`
|
/// The floating "+" start-chat affordance: a Liquid Glass circle in the lower-right that reveals
|
||||||
/// scopes it to one project (a project page's "+"); nil offers the picker (Home, Sessions).
|
/// the glass `StartChatComposer` as a floating bar over the page — the same entry box the open
|
||||||
struct NewChatSheet: View {
|
/// session carries, not a full modal pane. Shared by Home, Sessions, and a project page (which
|
||||||
var project: WireProject? = nil
|
/// scopes it to `project`), so all three offer the same control.
|
||||||
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.
|
|
||||||
struct NewChatAffordance: ViewModifier {
|
struct NewChatAffordance: ViewModifier {
|
||||||
@EnvironmentObject var store: RemoteStore
|
@EnvironmentObject var store: RemoteStore
|
||||||
/// Scopes the composer to one project (project page); nil offers the picker (Home, Sessions).
|
/// Scopes the composer to one project (project page); nil offers the picker (Home, Sessions).
|
||||||
var project: WireProject? = nil
|
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
|
@Binding var isPresented: Bool
|
||||||
|
|
||||||
/// The button only makes sense when this device can control a Mac and there's a project to
|
/// 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 {
|
func body(content: Content) -> some View {
|
||||||
content
|
content
|
||||||
|
// The "+" disc — hidden while the composer is up so it doesn't sit over the bar.
|
||||||
.overlay(alignment: .bottomTrailing) {
|
.overlay(alignment: .bottomTrailing) {
|
||||||
if canStart {
|
if canStart && !isPresented {
|
||||||
Button { isPresented = true } label: {
|
Button { isPresented = true } label: {
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
.font(.system(size: 22, weight: .semibold))
|
.font(.system(size: 22, weight: .semibold))
|
||||||
@@ -187,14 +165,30 @@ struct NewChatAffordance: ViewModifier {
|
|||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
.padding(.trailing, 20)
|
.padding(.trailing, 20)
|
||||||
.padding(.bottom, 6)
|
.padding(.bottom, 6)
|
||||||
|
.transition(.scale.combined(with: .opacity))
|
||||||
.accessibilityLabel("New chat")
|
.accessibilityLabel("New chat")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $isPresented) {
|
// The composer itself, floating up from the bottom over a light scrim — no modal pane,
|
||||||
NewChatSheet(project: project) { isPresented = false }
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
/// Add the floating "+" start-chat affordance (glass circle → glass composer sheet). See
|
/// Add the floating "+" start-chat affordance (glass circle → glass composer sheet). See
|
||||||
|
|||||||
@@ -10,4 +10,4 @@
|
|||||||
# NUCLEIC_MARKETING_VERSION semantic version -> CFBundleShortVersionString
|
# NUCLEIC_MARKETING_VERSION semantic version -> CFBundleShortVersionString
|
||||||
# NUCLEIC_BUILD_NUMBER monotonic build -> CFBundleVersion (always +1 per release)
|
# NUCLEIC_BUILD_NUMBER monotonic build -> CFBundleVersion (always +1 per release)
|
||||||
NUCLEIC_MARKETING_VERSION=0.2.0
|
NUCLEIC_MARKETING_VERSION=0.2.0
|
||||||
NUCLEIC_BUILD_NUMBER=13
|
NUCLEIC_BUILD_NUMBER=5
|
||||||
|
|||||||
Reference in New Issue
Block a user