on main help sign in
Merge pull request #114 from charmbracelet/newline

feat(tui): support adding newlines in chat editor

commit72f469e876a94122c4545aa5627cf651e4932728 authorKujtim Hoxha <kujtimii.h@gmail.com> dateThu 10 Jul 2025, 09:53 (1 year ago) parent2941c659afc491
4 files changed+36−2
internal/tui/components/chat/editor/editor.go+8−1 internal/tui/components/chat/editor/keys.go+16 internal/tui/page/chat/chat.go+4 internal/tui/tui.go+8−1
internal/tui/components/chat/editor/editor.go+8−1
@@ -145,6 +145,9 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
145145 var cmd tea.Cmd
146146 var cmds []tea.Cmd
147147 switch msg := msg.(type) {
148+ case tea.KeyboardEnhancementsMsg:
149+ m.keyMap.keyboard = msg
150+ return m, nil
148151 case chat.SessionSelectedMsg:
149152 if msg.ID != m.session.ID {
150153 m.session = msg
@@ -245,7 +248,11 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
245248 m.deleteMode = false
246249 return m, nil
247250 }
248− // Hanlde Enter key
251+ if key.Matches(msg, m.keyMap.Newline) {
252+ m.textarea.InsertRune('\n')
253+ return m, nil
254+ }
255+ // Handle Enter key
249256 if m.textarea.Focused() && key.Matches(msg, m.keyMap.SendMessage) {
250257 value := m.textarea.Value()
251258 if len(value) > 0 && value[len(value)-1] == '\\' {
internal/tui/components/chat/editor/keys.go+16
@@ -2,12 +2,16 @@ package editor
22
33import (
44 "github.com/charmbracelet/bubbles/v2/key"
5+ tea "github.com/charmbracelet/bubbletea/v2"
56)
67
78type EditorKeyMap struct {
89 AddFile key.Binding
910 SendMessage key.Binding
1011 OpenEditor key.Binding
12+ Newline key.Binding
13+
14+ keyboard tea.KeyboardEnhancementsMsg
1115}
1216
1317func DefaultEditorKeyMap() EditorKeyMap {
@@ -24,15 +28,27 @@ func DefaultEditorKeyMap() EditorKeyMap {
2428 key.WithKeys("ctrl+e"),
2529 key.WithHelp("ctrl+e", "open editor"),
2630 ),
31+ Newline: key.NewBinding(
32+ key.WithKeys("shift+enter", "ctrl+j"),
33+ // "ctrl+j" is a common keybinding for newline in many editors. If
34+ // the terminal supports "shift+enter", we substitute the help text
35+ // to reflect that.
36+ key.WithHelp("ctrl+j", "newline"),
37+ ),
2738 }
2839}
2940
3041// KeyBindings implements layout.KeyMapProvider
3142func (k EditorKeyMap) KeyBindings() []key.Binding {
43+ newline := k.Newline
44+ if k.keyboard.SupportsKeyDisambiguation() {
45+ newline.SetHelp("shift+enter", newline.Help().Desc)
46+ }
3247 return []key.Binding{
3348 k.AddFile,
3449 k.SendMessage,
3550 k.OpenEditor,
51+ newline,
3652 }
3753}
3854
internal/tui/page/chat/chat.go+4
@@ -81,6 +81,10 @@ func (p *chatPage) cancelTimerCmd() tea.Cmd {
8181func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
8282 var cmds []tea.Cmd
8383 switch msg := msg.(type) {
84+ case tea.KeyboardEnhancementsMsg:
85+ m, cmd := p.layout.Update(msg)
86+ p.layout = m.(layout.SplitPaneLayout)
87+ return p, cmd
8488 case CancelTimerExpiredMsg:
8589 p.cancelPending = false
8690 return p, nil
internal/tui/tui.go+8−1
@@ -115,7 +115,14 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
115115
116116 switch msg := msg.(type) {
117117 case tea.KeyboardEnhancementsMsg:
118− return a, nil
118+ for id, page := range a.pages {
119+ m, pageCmd := page.Update(msg)
120+ a.pages[id] = m.(util.Model)
121+ if pageCmd != nil {
122+ cmds = append(cmds, pageCmd)
123+ }
124+ }
125+ return a, tea.Batch(cmds...)
119126 case tea.WindowSizeMsg:
120127 return a, a.handleWindowResize(msg.Width, msg.Height)
121128