mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 10:36:35 +00:00
fix(ios-chat): dismissable keyboard via swipe + toolbar button (#51)
Pre-fix the iOS composer's TextField had no keyboard dismissal: no @FocusState, no scrollDismissesKeyboard, no keyboard accessory. With axis: .vertical + submitLabel: .send the Return key inserts a newline rather than committing, so once the keyboard rose it stayed up — hiding the top-trailing toolbar button on small phones. Three additive changes: - @FocusState private var composerFocused on ChatView, bound to the TextField via .focused($composerFocused). - .scrollDismissesKeyboard(.interactively) on the message list ScrollView so dragging the messages downward collapses the keyboard with the gesture (the standard iOS chat pattern the reporter explicitly named — "swipe away"). - ToolbarItemGroup(placement: .keyboard) accessory with a keyboard.chevron.compact.down "Done" button so dismissal is also available without a scrollable area (e.g. fresh empty-state chat before any messages exist). ScarfGo iOS only. Mac unaffected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,12 @@ struct ChatView: View {
|
|||||||
@State private var controller: ChatController
|
@State private var controller: ChatController
|
||||||
@State private var showProjectPicker = false
|
@State private var showProjectPicker = false
|
||||||
@State private var showSlashCommandsSheet = false
|
@State private var showSlashCommandsSheet = false
|
||||||
|
/// Drives the composer's keyboard. Bound to the TextField via
|
||||||
|
/// `.focused(...)`; cleared by the scroll-to-dismiss gesture on
|
||||||
|
/// the message list AND by an explicit keyboard-toolbar button.
|
||||||
|
/// (issue #51 — pre-fix the keyboard could never be dismissed,
|
||||||
|
/// blocking access to the toolbar nav button on small phones.)
|
||||||
|
@FocusState private var composerFocused: Bool
|
||||||
|
|
||||||
init(config: IOSServerConfig, key: SSHKeyBundle) {
|
init(config: IOSServerConfig, key: SSHKeyBundle) {
|
||||||
self.config = config
|
self.config = config
|
||||||
@@ -234,6 +240,11 @@ struct ChatView: View {
|
|||||||
// which fought with the user's own scroll gestures.
|
// which fought with the user's own scroll gestures.
|
||||||
.defaultScrollAnchor(.bottom)
|
.defaultScrollAnchor(.bottom)
|
||||||
.defaultScrollAnchor(.bottom, for: .sizeChanges)
|
.defaultScrollAnchor(.bottom, for: .sizeChanges)
|
||||||
|
// Drag the messages downward to interactively collapse the
|
||||||
|
// keyboard — the standard iOS chat gesture. Without this the
|
||||||
|
// keyboard could never be dismissed once it rose, hiding the
|
||||||
|
// top-trailing nav button on small phones (issue #51).
|
||||||
|
.scrollDismissesKeyboard(.interactively)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
@@ -311,9 +322,27 @@ struct ChatView: View {
|
|||||||
.lineLimit(1...5)
|
.lineLimit(1...5)
|
||||||
.disabled(controller.state != .ready)
|
.disabled(controller.state != .ready)
|
||||||
.submitLabel(.send)
|
.submitLabel(.send)
|
||||||
|
.focused($composerFocused)
|
||||||
.onSubmit {
|
.onSubmit {
|
||||||
Task { await controller.send() }
|
Task { await controller.send() }
|
||||||
}
|
}
|
||||||
|
// Explicit dismiss-keyboard affordance, complementing the
|
||||||
|
// interactive scroll-to-dismiss on the message list. iOS
|
||||||
|
// shows a keyboard accessory toolbar above the system
|
||||||
|
// keyboard whenever a focused TextField is on screen;
|
||||||
|
// putting a "Done" chevron there is the most-discoverable
|
||||||
|
// dismissal pattern (issue #51).
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .keyboard) {
|
||||||
|
Spacer()
|
||||||
|
Button {
|
||||||
|
composerFocused = false
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "keyboard.chevron.compact.down")
|
||||||
|
}
|
||||||
|
.accessibilityLabel("Hide keyboard")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Task { await controller.send() }
|
Task { await controller.send() }
|
||||||
|
|||||||
Reference in New Issue
Block a user