From 74afc0abc7d7c577c4816f811f791e0c7e602054 Mon Sep 17 00:00:00 2001 From: tauraamui Date: Wed, 5 Nov 2025 10:38:26 +0000 Subject: [PATCH] wip --- internal/tui/components/chat/editor/editor.go | 113 ++++++++++++++++++ .../tui/components/chat/editor/history.go | 14 +++ internal/tui/components/chat/editor/keys.go | 10 ++ 3 files changed, 137 insertions(+) create mode 100644 internal/tui/components/chat/editor/history.go diff --git a/internal/tui/components/chat/editor/editor.go b/internal/tui/components/chat/editor/editor.go index de1a98b34595613594e83063cc12add4ba820c84..2fb9dabc953375e0e8be465b321eafed0b4fb755 100644 --- a/internal/tui/components/chat/editor/editor.go +++ b/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 diff --git a/internal/tui/components/chat/editor/history.go b/internal/tui/components/chat/editor/history.go new file mode 100644 index 0000000000000000000000000000000000000000..3462e1b3f275ad1b8950ea1a1781648219fd3086 --- /dev/null +++ b/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{} +} diff --git a/internal/tui/components/chat/editor/keys.go b/internal/tui/components/chat/editor/keys.go index 0ba4571888e547b1c4a85e7ee9dd73ff07ce13d2..eabf60442c59b2137cae45c3f09d6d352a8969c6 100644 --- a/internal/tui/components/chat/editor/keys.go +++ b/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"), + ), } }