ios: share prompt field across chat and agent views
TestFlight / Build and upload (push) Successful in 1m44s
TestFlight / Build and upload (push) Successful in 1m44s
This commit is contained in:
@@ -194,31 +194,31 @@ struct SybilAgentView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private var canSendComposer: Bool {
|
||||
!room.composer.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && room.composer.utf16.count <= 40_000
|
||||
}
|
||||
|
||||
private var composer: some View {
|
||||
HStack(alignment: .bottom, spacing: 10) {
|
||||
TextField("Message Sybil…", text: $room.composer, axis: .vertical)
|
||||
.lineLimit(1...6)
|
||||
.font(.sybil(.body))
|
||||
.focused($composerFocused)
|
||||
SybilPromptField(
|
||||
text: $room.composer,
|
||||
focused: $composerFocused,
|
||||
submit: submitComposer
|
||||
)
|
||||
.accessibilityIdentifier("agent-composer")
|
||||
Button {
|
||||
let content = room.composer
|
||||
if room.send(content) { room.composer = ""; atLatest = true }
|
||||
} label: {
|
||||
Image(systemName: "arrow.up")
|
||||
.font(.system(size: 17, weight: .semibold))
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 38, height: 38)
|
||||
.background(SybilTheme.primaryGradient, in: Circle())
|
||||
}
|
||||
.disabled(room.composer.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || room.composer.utf16.count > 40_000)
|
||||
.accessibilityLabel("Send message")
|
||||
|
||||
SybilPromptSendButton(canSend: canSendComposer, submit: submitComposer)
|
||||
.accessibilityIdentifier("agent-send")
|
||||
}
|
||||
.padding(12)
|
||||
.background(SybilTheme.surfaceStrong, in: RoundedRectangle(cornerRadius: 22))
|
||||
.overlay(RoundedRectangle(cornerRadius: 22).stroke(SybilTheme.border))
|
||||
.padding(.horizontal, 12).padding(.bottom, 10).padding(.top, 6)
|
||||
.padding(.horizontal, 14)
|
||||
.padding(.bottom, 12)
|
||||
.padding(.top, 6)
|
||||
}
|
||||
|
||||
private func submitComposer() {
|
||||
guard canSendComposer, room.send(room.composer) else { return }
|
||||
room.composer = ""
|
||||
atLatest = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import SwiftUI
|
||||
|
||||
struct SybilPromptField: View {
|
||||
var placeholder: String = "Enter Prompt"
|
||||
@Binding var text: String
|
||||
@FocusState.Binding var focused: Bool
|
||||
var selection: Binding<TextSelection?>? = nil
|
||||
var axis: Axis = .vertical
|
||||
var submit: () -> Void
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if let selection {
|
||||
TextField(placeholder, text: $text, selection: selection, axis: axis)
|
||||
} else {
|
||||
TextField(placeholder, text: $text, axis: axis)
|
||||
}
|
||||
}
|
||||
.focused($focused)
|
||||
.font(.sybil(.body))
|
||||
.textInputAutocapitalization(.sentences)
|
||||
.autocorrectionDisabled(false)
|
||||
.lineLimit(1 ... 6)
|
||||
.submitLabel(.send)
|
||||
.onSubmit(submit)
|
||||
.submitOnMacReturn(submit)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 10)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(SybilTheme.composerGradient)
|
||||
.opacity(0.98)
|
||||
)
|
||||
.foregroundStyle(SybilTheme.text)
|
||||
}
|
||||
}
|
||||
|
||||
struct SybilPromptSendButton: View {
|
||||
var systemImage: String = "arrow.up"
|
||||
var canSend: Bool
|
||||
var submit: () -> Void
|
||||
|
||||
var body: some View {
|
||||
Button(action: submit) {
|
||||
Image(systemName: systemImage)
|
||||
.font(.system(size: 17, weight: .semibold))
|
||||
.frame(width: 40, height: 40)
|
||||
.background(
|
||||
Circle()
|
||||
.fill(
|
||||
canSend
|
||||
? AnyShapeStyle(SybilTheme.primaryGradient)
|
||||
: AnyShapeStyle(SybilTheme.surface)
|
||||
)
|
||||
)
|
||||
.foregroundStyle(canSend ? SybilTheme.text : SybilTheme.textMuted)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(!canSend)
|
||||
.accessibilityLabel("Send message")
|
||||
}
|
||||
}
|
||||
@@ -95,46 +95,19 @@ struct SybilQuickQuestionView: View {
|
||||
private var composer: some View {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
HStack(alignment: .bottom, spacing: 10) {
|
||||
TextField(
|
||||
"Ask anything...",
|
||||
SybilPromptField(
|
||||
placeholder: "Ask anything...",
|
||||
text: Binding(
|
||||
get: { viewModel.quickQuestionPrompt },
|
||||
set: { viewModel.updateQuickQuestionPrompt($0) }
|
||||
),
|
||||
focused: $promptFocused,
|
||||
selection: $promptSelection,
|
||||
axis: .horizontal
|
||||
axis: .horizontal,
|
||||
submit: submitQuestion
|
||||
)
|
||||
.focused($promptFocused)
|
||||
.font(.body)
|
||||
.textInputAutocapitalization(.sentences)
|
||||
.autocorrectionDisabled(false)
|
||||
.submitLabel(.send)
|
||||
.onSubmit(submitQuestion)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 10)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(SybilTheme.composerGradient)
|
||||
.opacity(0.98)
|
||||
)
|
||||
.foregroundStyle(SybilTheme.text)
|
||||
|
||||
Button(action: submitQuestion) {
|
||||
Image(systemName: "arrow.up")
|
||||
.font(.body.weight(.semibold))
|
||||
.frame(width: 40, height: 40)
|
||||
.background(
|
||||
Circle()
|
||||
.fill(
|
||||
viewModel.canSendQuickQuestion
|
||||
? AnyShapeStyle(SybilTheme.primaryGradient)
|
||||
: AnyShapeStyle(SybilTheme.surfaceStrong.opacity(0.92))
|
||||
)
|
||||
)
|
||||
.foregroundStyle(viewModel.canSendQuickQuestion ? SybilTheme.text : SybilTheme.textMuted)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(!viewModel.canSendQuickQuestion)
|
||||
SybilPromptSendButton(canSend: viewModel.canSendQuickQuestion, submit: submitQuestion)
|
||||
.accessibilityLabel("Ask quick question")
|
||||
}
|
||||
|
||||
|
||||
@@ -613,47 +613,19 @@ struct SybilWorkspaceView: View {
|
||||
.accessibilityLabel("Attach file")
|
||||
}
|
||||
|
||||
TextField(
|
||||
viewModel.isSearchMode ? "Search the web" : "Enter Prompt",
|
||||
SybilPromptField(
|
||||
placeholder: viewModel.isSearchMode ? "Search the web" : "Enter Prompt",
|
||||
text: $viewModel.composer,
|
||||
axis: .vertical
|
||||
focused: $composerFocused,
|
||||
submit: submitComposer
|
||||
)
|
||||
.focused($composerFocused)
|
||||
.textInputAutocapitalization(.sentences)
|
||||
.autocorrectionDisabled(false)
|
||||
.lineLimit(1 ... 6)
|
||||
.submitLabel(.send)
|
||||
.onSubmit {
|
||||
submitComposer()
|
||||
}
|
||||
.submitOnMacReturn(submitComposer)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 10)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(SybilTheme.composerGradient)
|
||||
.opacity(0.98)
|
||||
)
|
||||
.foregroundStyle(SybilTheme.text)
|
||||
|
||||
Button {
|
||||
submitComposer()
|
||||
} label: {
|
||||
Image(systemName: viewModel.isSearchMode ? "magnifyingglass" : "arrow.up")
|
||||
.font(.system(size: 17, weight: .semibold))
|
||||
.frame(width: 40, height: 40)
|
||||
.background(
|
||||
Circle()
|
||||
.fill(
|
||||
viewModel.canSendComposer
|
||||
? AnyShapeStyle(SybilTheme.primaryGradient)
|
||||
: AnyShapeStyle(SybilTheme.surface)
|
||||
SybilPromptSendButton(
|
||||
systemImage: viewModel.isSearchMode ? "magnifyingglass" : "arrow.up",
|
||||
canSend: viewModel.canSendComposer,
|
||||
submit: submitComposer
|
||||
)
|
||||
)
|
||||
.foregroundStyle(viewModel.canSendComposer ? SybilTheme.text : SybilTheme.textMuted)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(!viewModel.canSendComposer)
|
||||
.accessibilityLabel(viewModel.isSearchMode ? "Search" : "Send message")
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 14)
|
||||
|
||||
Reference in New Issue
Block a user