Fix the composer scroller flash, edit focus, and Shift+Return
- The composer only enables its scroller once the text is taller than the seven-line max, so it no longer flashes on every growth step. - The composer re-asserts first responder only when canStealFocus is true; it is off while a modal field or the sync overlay has the keyboard, so typing in the edit dialog, search and sync fields no longer bounces back. - The edit dialog is a ComposerView now: Return saves, Shift+Return inserts a newline, Esc cancels, and paste uses the same path as the composer. - Shift+Return never actually worked: returning false from doCommandBy does not fall through to the default newline insertion, so the event died. The newline is inserted explicitly and the Shift state comes from the keyDown.
This commit is contained in:
@@ -19,13 +19,15 @@ struct PanelView: View {
|
||||
@State private var lastSelectedID: UUID?
|
||||
@State private var editingItem: PromptItem?
|
||||
@State private var editText = ""
|
||||
@State private var editFocused = false
|
||||
@State private var editHeight: CGFloat = ComposerView.oneLineHeight
|
||||
@State private var copiedID: UUID?
|
||||
@State private var accessibilityGranted = AXIsProcessTrusted()
|
||||
|
||||
private enum Tab { case workspace, all }
|
||||
@State private var tab: Tab = .workspace
|
||||
|
||||
private enum Field: Hashable { case search, newWorkspace, editor }
|
||||
private enum Field: Hashable { case search, newWorkspace }
|
||||
@FocusState private var focusedField: Field?
|
||||
|
||||
var body: some View {
|
||||
@@ -141,6 +143,7 @@ struct PanelView: View {
|
||||
.onExitCommand {
|
||||
search = ""
|
||||
showSearch = false
|
||||
focusedField = nil
|
||||
composerFocused = true
|
||||
}
|
||||
if !search.isEmpty {
|
||||
@@ -322,8 +325,9 @@ struct PanelView: View {
|
||||
if case .text(let text) = item.content, !multiple {
|
||||
Button("Edit…") {
|
||||
editText = text
|
||||
editHeight = ComposerView.oneLineHeight
|
||||
editingItem = item
|
||||
focusedField = .editor
|
||||
editFocused = true
|
||||
}
|
||||
}
|
||||
if case .image(let filename) = item.content, !multiple {
|
||||
@@ -542,6 +546,9 @@ struct PanelView: View {
|
||||
|
||||
// MARK: - Edit
|
||||
|
||||
/// The editor is a ComposerView too, so it behaves exactly like the main
|
||||
/// input: Return saves, Shift+Return inserts a newline, Esc cancels, and
|
||||
/// pasting goes through the same working path.
|
||||
@ViewBuilder
|
||||
private var editModal: some View {
|
||||
if let item = editingItem {
|
||||
@@ -551,14 +558,18 @@ struct PanelView: View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("Edit")
|
||||
.font(.headline)
|
||||
TextEditor(text: $editText)
|
||||
.font(.callout)
|
||||
.scrollContentBackground(.hidden)
|
||||
.frame(minHeight: 120, maxHeight: 280)
|
||||
.padding(8)
|
||||
.background(Theme.rowSwiftUI)
|
||||
.cornerRadius(8)
|
||||
.focused($focusedField, equals: .editor)
|
||||
ComposerView(
|
||||
text: $editText,
|
||||
isFocused: $editFocused,
|
||||
height: $editHeight,
|
||||
placeholder: "",
|
||||
onSubmit: { saveEdit(item) },
|
||||
onEscape: { dismissEditor() }
|
||||
)
|
||||
.frame(height: editHeight)
|
||||
.padding(8)
|
||||
.background(Theme.rowSwiftUI)
|
||||
.cornerRadius(8)
|
||||
HStack {
|
||||
Spacer()
|
||||
Button("Cancel") { dismissEditor() }
|
||||
@@ -593,6 +604,7 @@ struct PanelView: View {
|
||||
private func dismissEditor() {
|
||||
editingItem = nil
|
||||
editText = ""
|
||||
editFocused = false
|
||||
composerFocused = true
|
||||
}
|
||||
|
||||
@@ -640,6 +652,7 @@ struct PanelView: View {
|
||||
private func dismissNewWorkspace() {
|
||||
showNewWorkspace = false
|
||||
newWorkspaceName = ""
|
||||
focusedField = nil
|
||||
composerFocused = true
|
||||
}
|
||||
|
||||
@@ -713,6 +726,9 @@ struct PanelView: View {
|
||||
isFocused: $composerFocused,
|
||||
height: $composerHeight,
|
||||
placeholder: "Add a note or prompt (\(store.activeSection.name))",
|
||||
// A modal field (search, edit, new workspace) or the sync
|
||||
// overlay owns the keyboard; don't steal focus back mid-edit.
|
||||
canStealFocus: focusedField == nil && !showSync && editingItem == nil,
|
||||
onSubmit: submit,
|
||||
onEscape: {
|
||||
if selection.isEmpty {
|
||||
@@ -835,6 +851,7 @@ struct PanelView: View {
|
||||
focusedField = .search
|
||||
} else {
|
||||
search = ""
|
||||
focusedField = nil
|
||||
composerFocused = true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user