wip

tauraamui created

Change summary

internal/tui/components/chat/editor/editor.go  | 113 ++++++++++++++++++++
internal/tui/components/chat/editor/history.go |  14 ++
internal/tui/components/chat/editor/keys.go    |  10 +
3 files changed, 137 insertions(+)

Detailed changes

internal/tui/components/chat/editor/editor.go 🔗

@@ -67,6 +67,8 @@ type editorCmp struct {
 	currentQuery          string
 	completionsStartIndex int
 	isCompletionsOpen     bool
+
+	history History
 }
 
 var DeleteKeyMaps = DeleteAttachmentKeyMaps{
@@ -89,6 +91,10 @@ const (
 	maxFileResults = 25
 )
 
+type loadHistoryMsg struct{}
+
+type closeHistoryMsg struct{}
+
 type OpenEditorMsg struct {
 	Text string
 }
@@ -172,6 +178,10 @@ func (m *editorCmp) repositionCompletions() tea.Msg {
 	return completions.RepositionCompletionsMsg{X: x, Y: y}
 }
 
+func (m *editorCmp) inHistoryMode() bool {
+	return m.history != nil
+}
+
 func (m *editorCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
 	var cmd tea.Cmd
 	var cmds []tea.Cmd
@@ -260,6 +270,10 @@ func (m *editorCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
 	case commands.ToggleYoloModeMsg:
 		m.setEditorPrompt()
 		return m, nil
+	case loadHistoryMsg:
+		m.history = InitialiseHistory([]string{})
+	case closeHistoryMsg:
+		m.history = nil
 	case tea.KeyPressMsg:
 		cur := m.textarea.Cursor()
 		curIdx := m.textarea.Width()*cur.Y + cur.X
@@ -289,6 +303,16 @@ func (m *editorCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
 			m.attachments = nil
 			return m, nil
 		}
+		// history
+		if m.textarea.Focused() && key.Matches(msg, m.keyMap.Previous) || key.Matches(msg, m.keyMap.Next) {
+			// m.textarea.SetValue(m.stepOverHistory(m.getUserMessagesAsText, m.getDirectionFromKey(msg)))
+			if m.history == nil {
+				return m, util.CmdHandler(loadHistoryMsg{})
+			}
+		}
+		if key.Matches(msg, DeleteKeyMaps.Escape) && m.inHistoryMode() {
+			return m, util.CmdHandler(closeHistoryMsg{})
+		}
 		rune := msg.Code
 		if m.deleteMode && unicode.IsDigit(rune) {
 			num := int(rune - '0')
@@ -444,6 +468,95 @@ func (m *editorCmp) View() string {
 	return content
 }
 
+/*
+func (m *editorCmp) getUserMessagesAsText(ctx context.Context) ([]string, error) {
+	if len(m.historyCache) > 0 {
+		return m.historyCache, nil
+	}
+	allMessages, err := m.app.Messages.List(ctx, m.session.ID)
+	if err != nil {
+		return nil, err
+	}
+
+	var userMessages []string
+	for _, msg := range allMessages {
+		if msg.Role == message.User {
+			userMessages = append(userMessages, msg.Content().Text)
+		}
+	}
+
+	userMessages = append(userMessages, m.textarea.Value())
+	m.historyCache = userMessages
+	return userMessages, nil
+}
+
+type direction int
+
+const (
+	previous = iota
+	next
+)
+
+func (m *editorCmp) getDirectionFromKey(msg tea.KeyPressMsg) func() direction {
+	return func() direction {
+		if key.Matches(msg, m.keyMap.Previous) {
+			return previous
+		}
+		return next
+	}
+}
+
+func (m *editorCmp) stepOverHistory(resolveHistoricMessages func(context.Context) ([]string, error), resolveDirection func() direction) string {
+	// NOTE(tauraamui): the last entry in this list will be the current contents of the input field/box
+	messageHistory, err := resolveHistoricMessages(context.Background())
+	if err != nil {
+		return ""
+	}
+
+	// the list will/should always have at least the current message in the input in the list
+	if len(messageHistory) == 1 {
+		return messageHistory[0]
+	}
+
+	// the first time we invoke scroll we need to start from top of the list
+	if !m.previouslyScrollingPromptHistory {
+		m.promptHistoryIndex = len(messageHistory) - 1
+		m.previouslyScrollingPromptHistory = true
+	}
+
+	switch resolveDirection() {
+	case previous:
+		return m.stepBack(messageHistory)
+	case next:
+		return m.stepForward(messageHistory)
+	}
+	return ""
+}
+
+func (m *editorCmp) stepBack(history []string) string {
+	m.promptHistoryIndex -= 1
+	if m.promptHistoryIndex < 0 {
+		m.promptHistoryIndex = 0
+	}
+	return history[m.promptHistoryIndex]
+}
+
+func (m *editorCmp) stepForward(history []string) string {
+	m.promptHistoryIndex += 1
+	maxIndex := len(history) - 1
+	if m.promptHistoryIndex > maxIndex {
+		m.promptHistoryIndex = maxIndex
+	}
+	return history[m.promptHistoryIndex]
+}
+
+func (m *editorCmp) resetHistory() {
+	m.historyCache = nil
+	m.promptHistoryIndex = 0
+	m.previouslyScrollingPromptHistory = false
+}
+*/
+
 func (m *editorCmp) SetSize(width, height int) tea.Cmd {
 	m.width = width
 	m.height = height

internal/tui/components/chat/editor/history.go 🔗

@@ -0,0 +1,14 @@
+package editor
+
+type historyState struct {
+	previouslyScrollingPromptHistory bool
+	promptHistoryIndex               int
+	historyCache                     []string
+}
+
+type History interface {
+}
+
+func InitialiseHistory(messages []string) History {
+	return historyState{}
+}

internal/tui/components/chat/editor/keys.go 🔗

@@ -9,6 +9,8 @@ type EditorKeyMap struct {
 	SendMessage key.Binding
 	OpenEditor  key.Binding
 	Newline     key.Binding
+	Next        key.Binding
+	Previous    key.Binding
 }
 
 func DefaultEditorKeyMap() EditorKeyMap {
@@ -32,6 +34,14 @@ func DefaultEditorKeyMap() EditorKeyMap {
 			// to reflect that.
 			key.WithHelp("ctrl+j", "newline"),
 		),
+		Next: key.NewBinding(
+			key.WithKeys("shift+down"),
+			key.WithHelp("shift+↓", "down"),
+		),
+		Previous: key.NewBinding(
+			key.WithKeys("shift+up"),
+			key.WithHelp("shift+↑", "up"),
+		),
 	}
 }