Fix Enter key semantics and add expandable model descriptions

- Replace TextEditor with NativeTextEditor (NSViewRepresentable) so plain
  Enter sends the message and Shift/Cmd+Enter inserts a newline. The old
  TextEditor passed bare Return directly to NSTextView before SwiftUI's
  onKeyPress could intercept it, accidentally making Cmd+Enter send instead.
- Add More…/Less toggle in ModelInfoView for descriptions longer than 250
  characters, with smooth expand/collapse animation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 14:39:53 +02:00
parent ef1c05c13b
commit b3bb7c4a59
3 changed files with 222 additions and 45 deletions
+39 -44
View File
@@ -44,7 +44,7 @@ struct InputBar: View {
@State private var showCommandDropdown = false
@State private var selectedSuggestionIndex: Int = 0
@FocusState private var isInputFocused: Bool
@State private var isInputFocused: Bool = false
private static let minInputHeight: CGFloat = 56
private static let maxInputHeight: CGFloat = 320
@@ -95,55 +95,50 @@ struct InputBar: View {
}
// Editor fills the fixed-height box, bottom area reserved for globe
TextEditor(text: $text)
.font(.system(size: settings.inputTextSize))
.foregroundColor(.oaiPrimary)
.scrollContentBackground(.hidden)
.padding(.horizontal, 8)
.padding(.top, 6)
.padding(.bottom, 30)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.focused($isInputFocused)
.onChange(of: text) {
showCommandDropdown = text.hasPrefix("/")
selectedSuggestionIndex = 0
}
#if os(macOS)
.onKeyPress(.upArrow) {
if showCommandDropdown && selectedSuggestionIndex > 0 {
selectedSuggestionIndex -= 1
return .handled
}
return .ignored
}
.onKeyPress(.downArrow) {
if showCommandDropdown {
let count = CommandSuggestionsView.filteredCommands(for: text).count
if selectedSuggestionIndex < count - 1 {
selectedSuggestionIndex += 1
return .handled
}
}
return .ignored
}
.onKeyPress(.escape) {
if showCommandDropdown { showCommandDropdown = false; return .handled }
if isGenerating { onCancel(); return .handled }
return .ignored
}
.onKeyPress(.return, phases: .down) { press in
if press.modifiers.contains(.shift) { return .ignored }
NativeTextEditor(
text: $text,
font: .systemFont(ofSize: settings.inputTextSize),
textColor: NSColor(Color.oaiPrimary),
isFocused: isInputFocused,
onReturn: {
if showCommandDropdown {
let suggestions = CommandSuggestionsView.filteredCommands(for: text)
if !suggestions.isEmpty && selectedSuggestionIndex < suggestions.count {
selectCommand(suggestions[selectedSuggestionIndex].command)
return .handled
return true
}
}
if !text.isEmpty { onSend(); return .handled }
return .handled
}
#endif
if !text.isEmpty { onSend(); return true }
return true
},
onEscape: {
if showCommandDropdown { showCommandDropdown = false; return true }
if isGenerating { onCancel(); return true }
return false
},
onUpArrow: {
if showCommandDropdown && selectedSuggestionIndex > 0 {
selectedSuggestionIndex -= 1; return true
}
return false
},
onDownArrow: {
if showCommandDropdown {
let count = CommandSuggestionsView.filteredCommands(for: text).count
if selectedSuggestionIndex < count - 1 {
selectedSuggestionIndex += 1; return true
}
}
return false
},
onFocusChange: { focused in isInputFocused = focused }
)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.onChange(of: text) {
showCommandDropdown = text.hasPrefix("/")
selectedSuggestionIndex = 0
}
.padding(.bottom, 30)
// Online / offline toggle bottom-left of the text box
VStack {