redesign bottom bar
This commit is contained in:
@@ -6,6 +6,7 @@ struct SybilChatTranscriptView: View {
|
|||||||
var isLoading: Bool
|
var isLoading: Bool
|
||||||
var isSending: Bool
|
var isSending: Bool
|
||||||
var topContentInset: CGFloat = 0
|
var topContentInset: CGFloat = 0
|
||||||
|
var bottomContentInset: CGFloat = 0
|
||||||
@State private var hasHandledInitialTranscriptScroll = false
|
@State private var hasHandledInitialTranscriptScroll = false
|
||||||
|
|
||||||
private var hasPendingAssistant: Bool {
|
private var hasPendingAssistant: Bool {
|
||||||
@@ -50,7 +51,7 @@ struct SybilChatTranscriptView: View {
|
|||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.padding(.horizontal, 14)
|
.padding(.horizontal, 14)
|
||||||
.padding(.top, 18 + topContentInset)
|
.padding(.top, 18 + topContentInset)
|
||||||
.padding(.bottom, 18)
|
.padding(.bottom, 18 + bottomContentInset)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.scrollDismissesKeyboard(.interactively)
|
.scrollDismissesKeyboard(.interactively)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ struct SybilSearchResultsView: View {
|
|||||||
var isRunning: Bool
|
var isRunning: Bool
|
||||||
var isStartingChat: Bool = false
|
var isStartingChat: Bool = false
|
||||||
var topContentInset: CGFloat = 0
|
var topContentInset: CGFloat = 0
|
||||||
|
var bottomContentInset: CGFloat = 0
|
||||||
var onStartChat: (() -> Void)? = nil
|
var onStartChat: (() -> Void)? = nil
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@@ -100,7 +101,7 @@ struct SybilSearchResultsView: View {
|
|||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.padding(.horizontal, 14)
|
.padding(.horizontal, 14)
|
||||||
.padding(.top, 20 + topContentInset)
|
.padding(.top, 20 + topContentInset)
|
||||||
.padding(.bottom, 20)
|
.padding(.bottom, 20 + bottomContentInset)
|
||||||
}
|
}
|
||||||
.scrollDismissesKeyboard(.interactively)
|
.scrollDismissesKeyboard(.interactively)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ struct SybilWorkspaceView: View {
|
|||||||
@State private var newChatSwipeFeedbackGenerator: UIImpactFeedbackGenerator?
|
@State private var newChatSwipeFeedbackGenerator: UIImpactFeedbackGenerator?
|
||||||
|
|
||||||
private let customWorkspaceNavigationContentInset: CGFloat = 96
|
private let customWorkspaceNavigationContentInset: CGFloat = 96
|
||||||
|
private let composerOverlayContentInset: CGFloat = 112
|
||||||
|
|
||||||
private var isSettingsSelected: Bool {
|
private var isSettingsSelected: Bool {
|
||||||
if case .settings = viewModel.selectedItem {
|
if case .settings = viewModel.selectedItem {
|
||||||
@@ -149,7 +150,7 @@ struct SybilWorkspaceView: View {
|
|||||||
.overlay(SybilTheme.border)
|
.overlay(SybilTheme.border)
|
||||||
}
|
}
|
||||||
|
|
||||||
Group {
|
ZStack(alignment: .bottom) {
|
||||||
if isSettingsSelected {
|
if isSettingsSelected {
|
||||||
SybilSettingsView(viewModel: viewModel)
|
SybilSettingsView(viewModel: viewModel)
|
||||||
} else if viewModel.isSearchMode {
|
} else if viewModel.isSearchMode {
|
||||||
@@ -158,7 +159,8 @@ struct SybilWorkspaceView: View {
|
|||||||
isLoading: viewModel.isLoadingSelection,
|
isLoading: viewModel.isLoadingSelection,
|
||||||
isRunning: viewModel.isSending,
|
isRunning: viewModel.isSending,
|
||||||
isStartingChat: viewModel.isCreatingSearchChat,
|
isStartingChat: viewModel.isCreatingSearchChat,
|
||||||
topContentInset: showsCustomWorkspaceNavigation ? customWorkspaceNavigationContentInset : 0
|
topContentInset: showsCustomWorkspaceNavigation ? customWorkspaceNavigationContentInset : 0,
|
||||||
|
bottomContentInset: viewModel.showsComposer ? composerOverlayContentInset : 0
|
||||||
) {
|
) {
|
||||||
Task {
|
Task {
|
||||||
await viewModel.startChatFromSelectedSearch()
|
await viewModel.startChatFromSelectedSearch()
|
||||||
@@ -169,10 +171,15 @@ struct SybilWorkspaceView: View {
|
|||||||
messages: viewModel.displayedMessages,
|
messages: viewModel.displayedMessages,
|
||||||
isLoading: viewModel.isLoadingSelection,
|
isLoading: viewModel.isLoadingSelection,
|
||||||
isSending: viewModel.isSending,
|
isSending: viewModel.isSending,
|
||||||
topContentInset: showsCustomWorkspaceNavigation ? customWorkspaceNavigationContentInset : 0
|
topContentInset: showsCustomWorkspaceNavigation ? customWorkspaceNavigationContentInset : 0,
|
||||||
|
bottomContentInset: viewModel.showsComposer ? composerOverlayContentInset : 0
|
||||||
)
|
)
|
||||||
.id(transcriptScrollContextID)
|
.id(transcriptScrollContextID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if viewModel.showsComposer {
|
||||||
|
composerBar
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
.background {
|
.background {
|
||||||
@@ -193,12 +200,6 @@ struct SybilWorkspaceView: View {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if viewModel.showsComposer {
|
|
||||||
Divider()
|
|
||||||
.overlay(SybilTheme.border)
|
|
||||||
composerBar
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,7 +502,7 @@ struct SybilWorkspaceView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TextField(
|
TextField(
|
||||||
viewModel.isSearchMode ? "Search the web" : "Message Sybil",
|
viewModel.isSearchMode ? "Search the web" : "Enter Prompt",
|
||||||
text: $viewModel.composer,
|
text: $viewModel.composer,
|
||||||
axis: .vertical
|
axis: .vertical
|
||||||
)
|
)
|
||||||
@@ -518,10 +519,7 @@ struct SybilWorkspaceView: View {
|
|||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: 12)
|
RoundedRectangle(cornerRadius: 12)
|
||||||
.fill(SybilTheme.composerGradient)
|
.fill(SybilTheme.composerGradient)
|
||||||
.overlay(
|
.opacity(0.98)
|
||||||
RoundedRectangle(cornerRadius: 12)
|
|
||||||
.stroke(SybilTheme.primary.opacity(0.34), lineWidth: 1)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.foregroundStyle(SybilTheme.text)
|
.foregroundStyle(SybilTheme.text)
|
||||||
|
|
||||||
@@ -546,23 +544,19 @@ struct SybilWorkspaceView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 14)
|
.padding(.horizontal, 14)
|
||||||
.padding(.vertical, 12)
|
.padding(.top, 34)
|
||||||
.background(
|
.padding(.bottom, 12)
|
||||||
LinearGradient(
|
.background(alignment: .bottom) {
|
||||||
colors: [
|
SybilComposerFadeBackground()
|
||||||
SybilTheme.background.opacity(0.18),
|
.allowsHitTesting(false)
|
||||||
SybilTheme.background.opacity(0.96)
|
}
|
||||||
],
|
|
||||||
startPoint: .top,
|
|
||||||
endPoint: .bottom
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.overlay {
|
.overlay {
|
||||||
if isComposerDropTargeted && !viewModel.isSearchMode {
|
if isComposerDropTargeted && !viewModel.isSearchMode {
|
||||||
RoundedRectangle(cornerRadius: 18)
|
RoundedRectangle(cornerRadius: 18)
|
||||||
.stroke(SybilTheme.accent.opacity(0.78), style: StrokeStyle(lineWidth: 1.5, dash: [7, 5]))
|
.stroke(SybilTheme.accent.opacity(0.78), style: StrokeStyle(lineWidth: 1.5, dash: [7, 5]))
|
||||||
.padding(.horizontal, 14)
|
.padding(.horizontal, 14)
|
||||||
.padding(.vertical, 10)
|
.padding(.top, 32)
|
||||||
|
.padding(.bottom, 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDrop(of: [UTType.fileURL.identifier, UTType.image.identifier], isTargeted: $isComposerDropTargeted) { providers in
|
.onDrop(of: [UTType.fileURL.identifier, UTType.image.identifier], isTargeted: $isComposerDropTargeted) { providers in
|
||||||
@@ -982,6 +976,60 @@ private extension UIView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct SybilComposerFadeBackground: View {
|
||||||
|
var body: some View {
|
||||||
|
ZStack(alignment: .bottomLeading) {
|
||||||
|
LinearGradient(
|
||||||
|
colors: [
|
||||||
|
Color.clear,
|
||||||
|
SybilTheme.background.opacity(0.30),
|
||||||
|
SybilTheme.background.opacity(0.86),
|
||||||
|
SybilTheme.background.opacity(0.86),
|
||||||
|
SybilTheme.background.opacity(0.98)
|
||||||
|
],
|
||||||
|
startPoint: .top,
|
||||||
|
endPoint: .bottom
|
||||||
|
)
|
||||||
|
|
||||||
|
LinearGradient(
|
||||||
|
colors: [
|
||||||
|
SybilTheme.primary.opacity(0.18),
|
||||||
|
SybilTheme.surface.opacity(0.16),
|
||||||
|
SybilTheme.accent.opacity(0.08)
|
||||||
|
],
|
||||||
|
startPoint: .topLeading,
|
||||||
|
endPoint: .bottomTrailing
|
||||||
|
)
|
||||||
|
.mask(
|
||||||
|
LinearGradient(
|
||||||
|
colors: [
|
||||||
|
Color.clear,
|
||||||
|
Color.black.opacity(0.42),
|
||||||
|
Color.black
|
||||||
|
],
|
||||||
|
startPoint: .top,
|
||||||
|
endPoint: .bottom
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.blendMode(.screen)
|
||||||
|
|
||||||
|
RadialGradient(
|
||||||
|
colors: [
|
||||||
|
SybilTheme.primary.opacity(0.28),
|
||||||
|
SybilTheme.primary.opacity(0.08),
|
||||||
|
Color.clear
|
||||||
|
],
|
||||||
|
center: .bottomLeading,
|
||||||
|
startRadius: 8,
|
||||||
|
endRadius: 180
|
||||||
|
)
|
||||||
|
.blendMode(.screen)
|
||||||
|
.offset(x: -42, y: 42)
|
||||||
|
}
|
||||||
|
.ignoresSafeArea(edges: .bottom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private struct SybilNavigationIcon: View {
|
private struct SybilNavigationIcon: View {
|
||||||
var systemImage: String
|
var systemImage: String
|
||||||
|
|
||||||
@@ -1024,6 +1072,7 @@ private struct SybilNavigationFadeBackground: View {
|
|||||||
.blendMode(.screen)
|
.blendMode(.screen)
|
||||||
.offset(x: -44, y: -46)
|
.offset(x: -44, y: -46)
|
||||||
}
|
}
|
||||||
|
.frame(height: 200.0)
|
||||||
.ignoresSafeArea(edges: .top)
|
.ignoresSafeArea(edges: .top)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user