@@ -313,7 +313,9 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
315 case tea.KeyPressMsg:
315 case tea.KeyPressMsg:
316+ if cmd := m.handleKeyPressMsg(msg); cmd != nil {
316− cmds = append(cmds, m.handleKeyPressMsg(msg)...)
317+ cmds = append(cmds, cmd)
317 case tea.PasteMsg:
319 case tea.PasteMsg:
318 if cmd := m.handlePasteMsg(msg); cmd != nil {
320 if cmd := m.handlePasteMsg(msg); cmd != nil {
319 cmds = append(cmds, cmd)
321 cmds = append(cmds, cmd)
@@ -341,7 +343,9 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
341 return m, tea.Batch(cmds...)
343 return m, tea.Batch(cmds...)
344−func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) (cmds []tea.Cmd) {
346+func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) tea.Cmd {
345 handleQuitKeys := func(msg tea.KeyPressMsg) bool {
349 handleQuitKeys := func(msg tea.KeyPressMsg) bool {
347 case key.Matches(msg, m.keyMap.Quit):
351 case key.Matches(msg, m.keyMap.Quit):
@@ -369,6 +373,7 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) (cmds []tea.Cmd) {
369 return true
373 return true
370 case key.Matches(msg, m.keyMap.Models):
374 case key.Matches(msg, m.keyMap.Models):
371 // TODO: Implement me
375 // TODO: Implement me
372 case key.Matches(msg, m.keyMap.Sessions):
377 case key.Matches(msg, m.keyMap.Sessions):
373 if m.dialog.ContainsDialog(dialog.SessionsID) {
378 if m.dialog.ContainsDialog(dialog.SessionsID) {
374 // Bring to front
379 // Bring to front
@@ -385,12 +390,12 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) (cmds []tea.Cmd) {
385 if m.dialog.HasDialogs() {
390 if m.dialog.HasDialogs() {
386 // Always handle quit keys first
391 // Always handle quit keys first
387 if handleQuitKeys(msg) {
392 if handleQuitKeys(msg) {
388− return cmds
393+ return tea.Batch(cmds...)
391 msg := m.dialog.Update(msg)
396 msg := m.dialog.Update(msg)
392 if msg == nil {
397 if msg == nil {
393− return cmds
398+ return tea.Batch(cmds...)
396 switch msg := msg.(type) {
401 switch msg := msg.(type) {
@@ -424,18 +429,21 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) (cmds []tea.Cmd) {
424 cmds = append(cmds, tea.Quit)
429 cmds = append(cmds, tea.Quit)
427− return cmds
432+ return tea.Batch(cmds...)
430 switch m.state {
435 switch m.state {
431 case uiConfigure:
436 case uiConfigure:
432− return cmds
437+ return tea.Batch(cmds...)
433 case uiInitialize:
438 case uiInitialize:
434− return append(cmds, m.updateInitializeView(msg)...)
439+ cmds = append(cmds, m.updateInitializeView(msg)...)
440+ return tea.Batch(cmds...)
435 case uiChat, uiLanding, uiChatCompact:
441 case uiChat, uiLanding, uiChatCompact:
436 switch m.focus {
442 switch m.focus {
437 case uiFocusEditor:
443 case uiFocusEditor:
445+ case key.Matches(msg, m.keyMap.Editor.SendMessage):
446+ // TODO: Implement me
439 case key.Matches(msg, m.keyMap.Tab):
447 case key.Matches(msg, m.keyMap.Tab):
440 m.focus = uiFocusMain
448 m.focus = uiFocusMain
441 m.textarea.Blur()
449 m.textarea.Blur()
@@ -447,6 +455,8 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) (cmds []tea.Cmd) {
449 cmds = append(cmds, m.openEditor(m.textarea.Value()))
457 cmds = append(cmds, m.openEditor(m.textarea.Value()))
458+ case key.Matches(msg, m.keyMap.Editor.Newline):
459+ m.textarea.InsertRune('\n')
451 if handleGlobalKeys(msg) {
461 if handleGlobalKeys(msg) {
452 // Handle global keys first before passing to textarea.
462 // Handle global keys first before passing to textarea.
@@ -509,12 +519,12 @@ func (m *UI) handleKeyPressMsg(msg tea.KeyPressMsg) (cmds []tea.Cmd) {
509 handleGlobalKeys(msg)
519 handleGlobalKeys(msg)
512− return cmds
522+ return tea.Batch(cmds...)
515// Draw implements [tea.Layer] and draws the UI model.
525// Draw implements [tea.Layer] and draws the UI model.
516func (m *UI) Draw(scr uv.Screen, area uv.Rectangle) {
526func (m *UI) Draw(scr uv.Screen, area uv.Rectangle) {
517− layout := generateLayout(m, area.Dx(), area.Dy())
527+ layout := m.generateLayout(area.Dx(), area.Dy())
519 if m.layout != layout {
529 if m.layout != layout {
520 m.layout = layout
530 m.layout = layout
@@ -665,46 +675,58 @@ func (m *UI) View() tea.View {
665func (m *UI) ShortHelp() []key.Binding {
675func (m *UI) ShortHelp() []key.Binding {
666 var binds []key.Binding
676 var binds []key.Binding
667 k := &m.keyMap
677 k := &m.keyMap
679+ commands := k.Commands
680+ if m.focus == uiFocusEditor && m.textarea.LineCount() == 0 {
681+ commands.SetHelp("/ or ctrl+p", "commands")
669 switch m.state {
684 switch m.state {
670 case uiInitialize:
685 case uiInitialize:
671 binds = append(binds, k.Quit)
686 binds = append(binds, k.Quit)
688+ if m.focus == uiFocusEditor {
689+ tab.SetHelp("tab", "focus chat")
691+ tab.SetHelp("tab", "focus editor")
694+ binds = append(binds,
702+ binds = append(binds,
706+ binds = append(binds,
708+ k.Chat.UpDownOneItem,
673 // TODO: other states
715 // TODO: other states
674 // if m.session == nil {
716 // if m.session == nil {
675 // no session selected
717 // no session selected
676 binds = append(binds,
718 binds = append(binds,
677− k.Commands,
719+ commands,
678 k.Models,
720 k.Models,
679 k.Editor.Newline,
721 k.Editor.Newline,
685− // we have a session
691− // binds = append(binds,
692− // k.Editor.AddFile,
693− // k.Editor.SendMessage,
694− // k.Editor.OpenEditor,
695− // k.Editor.Newline,
698− // if len(m.attachments) > 0 {
699− // binds = append(binds,
700− // k.Editor.AttachmentDeleteMode,
701− // k.Editor.DeleteAllAttachments,
725+ binds = append(binds,
708 return binds
730 return binds
@@ -714,6 +736,12 @@ func (m *UI) FullHelp() [][]key.Binding {
714 k := &m.keyMap
736 k := &m.keyMap
715 help := k.Help
737 help := k.Help
716 help.SetHelp("ctrl+g", "less")
738 help.SetHelp("ctrl+g", "less")
739+ hasAttachments := false // TODO: implement attachments
740+ hasSession := m.session != nil && m.session.ID != ""
741+ commands := k.Commands
742+ if m.focus == uiFocusEditor && m.textarea.LineCount() == 0 {
743+ commands.SetHelp("/ or ctrl+p", "commands")
718 switch m.state {
746 switch m.state {
719 case uiInitialize:
747 case uiInitialize:
@@ -721,12 +749,72 @@ func (m *UI) FullHelp() [][]key.Binding {
721 []key.Binding{
749 []key.Binding{
753+ mainBinds := []key.Binding{}
755+ if m.focus == uiFocusEditor {
756+ tab.SetHelp("tab", "focus chat")
758+ tab.SetHelp("tab", "focus editor")
761+ mainBinds = append(mainBinds,
768+ mainBinds = append(mainBinds, k.Chat.NewSession)
771+ binds = append(binds, mainBinds)
775+ binds = append(binds,
779+ k.Editor.MentionFile,
780+ k.Editor.OpenEditor,
784+ binds = append(binds,
786+ k.Editor.AttachmentDeleteMode,
787+ k.Editor.DeleteAllAttachments,
793+ binds = append(binds,
796+ k.Chat.UpDownOneItem,
802+ k.Chat.HalfPageDown,
808+ k.Chat.ClearHighlight,
725 if m.session == nil {
813 if m.session == nil {
726 // no session selected
814 // no session selected
727 binds = append(binds,
815 binds = append(binds,
728 []key.Binding{
816 []key.Binding{
729− k.Commands,
817+ commands,
730 k.Models,
818 k.Models,
731 k.Sessions,
819 k.Sessions,
@@ -741,23 +829,21 @@ func (m *UI) FullHelp() [][]key.Binding {
745− // we have a session
752− // binds = append(binds, m.ShortHelp())
834+ binds = append(binds,
755 return binds
841 return binds
758// updateLayoutAndSize updates the layout and sizes of UI components.
844// updateLayoutAndSize updates the layout and sizes of UI components.
759func (m *UI) updateLayoutAndSize() {
845func (m *UI) updateLayoutAndSize() {
760− m.layout = generateLayout(m, m.width, m.height)
846+ m.layout = m.generateLayout(m.width, m.height)
761 m.updateSize()
847 m.updateSize()
@@ -786,7 +872,7 @@ func (m *UI) updateSize() {
787// generateLayout calculates the layout rectangles for all UI components based
873// generateLayout calculates the layout rectangles for all UI components based
788// on the current UI state and terminal dimensions.
874// on the current UI state and terminal dimensions.
789−func generateLayout(m *UI, w, h int) layout {
875+func (m *UI) generateLayout(w, h int) layout {
790 // The screen area we're working with
876 // The screen area we're working with
791 area := image.Rect(0, 0, w, h)
877 area := image.Rect(0, 0, w, h)