feat: improve textbox editor behaviour

This commit is contained in:
2026-07-31 15:36:23 +02:00
parent b24669e604
commit e36b00f741
15 changed files with 504 additions and 154 deletions

View File

@@ -70,6 +70,7 @@ func (m *App) startAIDiscussion(threadID string) {
return
}
m.aiMode, m.aiInput, m.writeThreadID, m.err = aiDiscussion, "", threadID, nil
m.resetInputEditor(&m.aiInputEditor, "")
if thread := m.threadByID(threadID); thread != nil {
m.folded[threadID] = false
m.focus = threadDetailPane
@@ -298,12 +299,20 @@ func (m App) updateAI(msg tea.Msg) (tea.Model, tea.Cmd, bool) {
if m.aiMode != aiDiscussion {
cancelled = cancelled || keyMatches(raw, m.keybindings.General.Back)
}
if cancelled && m.aiMode == aiDiscussion &&
m.aiInputEditor.Modal && m.aiInputEditor.Mode != textEditorNormal {
m.aiInputEditor.handleKeyAtWidth(key, true, m.threadInputWidth())
m.aiInput = m.aiInputEditor.Text
m.ensureThreadInputCursorVisible()
return m, nil, true
}
if cancelled {
if m.aiCancel != nil {
m.aiCancel()
m.aiCancel = nil
}
m.aiMode, m.aiInput, m.writeThreadID, m.aiEvents = aiNone, "", "", nil
m.aiInputEditor = textEditor{}
return m, nil, true
}
@@ -351,19 +360,12 @@ func (m App) updateAI(msg tea.Msg) (tea.Model, tea.Cmd, bool) {
} else {
return m, m.beginAIPrepare(m.writeThreadID, strings.TrimSpace(m.aiInput)), true
}
case keyMatches(raw, m.keybindings.Input.Newline):
m.aiInput += "\n"
case keyMatches(raw, m.keybindings.Input.DeleteBackward):
runes := []rune(m.aiInput)
if len(runes) > 0 {
m.aiInput = string(runes[:len(runes)-1])
}
default:
if key.Type == tea.KeyRunes || key.Type == tea.KeySpace {
m.aiInput += textInputKeyValue(key)
if m.aiInputEditor.handleKeyAtWidth(key, true, m.threadInputWidth()) {
m.aiInput = m.aiInputEditor.Text
}
}
m.scroll = m.detailMaxScroll()
m.ensureThreadInputCursorVisible()
case aiConfirm:
switch {
case keyMatches(raw, m.keybindings.General.Confirm):
@@ -461,16 +463,17 @@ func (m App) viewAI() string {
lines = append(lines, titleStyle.Render("Local AI discussion"), "",
dimStyle.Render("This message stays local; only the configured model receives it."), "")
lines = append(lines, renderTextInput(
m.aiInput, width-2, m.cursorOutput != nil,
m.aiInputEditor, width-2, m.cursorOutput != nil,
)...)
if m.err != nil {
lines = append(lines, "", badStyle.Render(m.err.Error()))
}
lines = append(lines, "", dimStyle.Render(fmt.Sprintf(
"%s newline • %s prepare • %s cancel",
"%s newline • %s prepare • %s %s",
primaryKeyLabel(m.keybindings.Input.Newline),
primaryKeyLabel(m.keybindings.Input.Submit),
primaryKeyLabel(m.keybindings.Input.Cancel),
m.inputCancelAction(),
)))
case aiPreparing:
lines = m.aiProgressLines(width)
@@ -666,7 +669,9 @@ func (m App) inlineAIDiscussionLines(width int) []detailLine {
}
textWidth := max(1, width-5)
lineIndex := 0
for _, part := range renderTextInput(m.aiInput, textWidth, m.cursorOutput != nil) {
for _, part := range renderTextInput(
m.aiInputEditor, textWidth, m.cursorOutput != nil,
) {
lines = append(lines, detailLine{
rail: rail, anchor: fmt.Sprintf("ai-discussion:body:%d", lineIndex), text: part,
})
@@ -678,10 +683,11 @@ func (m App) inlineAIDiscussionLines(width int) []detailLine {
lines = append(lines, detailLine{
rail: rail,
text: dimStyle.Render(fmt.Sprintf(
"%s newline • %s prepare • %s cancel",
"%s newline • %s prepare • %s %s",
primaryKeyLabel(m.keybindings.Input.Newline),
primaryKeyLabel(m.keybindings.Input.Submit),
primaryKeyLabel(m.keybindings.Input.Cancel),
m.inputCancelAction(),
)),
})
return lines