Feat: toggle system; edit msg; switch focus
This commit is contained in:
13
README.md
13
README.md
@@ -5,8 +5,15 @@
|
|||||||
- show msg id next to the msg; +
|
- show msg id next to the msg; +
|
||||||
- regen last message; +
|
- regen last message; +
|
||||||
- delete last message; +
|
- delete last message; +
|
||||||
- edit message? (including from bot);
|
- edit message? (including from bot); +
|
||||||
- use chatml template (but do not show it to the user);
|
|
||||||
- ability to copy message;
|
- ability to copy message;
|
||||||
- aility to copy selected text;
|
- aility to copy selected text; (I can do it though vim mode of the terminal, so +)
|
||||||
- menu with old chats (chat files); +
|
- menu with old chats (chat files); +
|
||||||
|
- fullscreen textarea option (for long prompt);
|
||||||
|
- tab to switch selection between textview and textarea (input and chat); +
|
||||||
|
- basic tools: memorize and recall;
|
||||||
|
- stop stream from the bot;
|
||||||
|
|
||||||
|
### FIX:
|
||||||
|
- bot responding (or haninging) blocks everything; +
|
||||||
|
- programm requires history folder, but it is .gitignore;
|
||||||
|
|||||||
33
bot.go
33
bot.go
@@ -310,28 +310,27 @@ func chatToText(showSys bool) string {
|
|||||||
return strings.Join(s, "")
|
return strings.Join(s, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func textToChat(chat []string) []models.MessagesStory {
|
func textToMsg(rawMsg string) models.MessagesStory {
|
||||||
resp := make([]models.MessagesStory, len(chat))
|
msg := models.MessagesStory{}
|
||||||
for i, rawMsg := range chat {
|
|
||||||
// trim icon
|
|
||||||
var (
|
|
||||||
role string
|
|
||||||
msg string
|
|
||||||
)
|
|
||||||
// system and tool?
|
// system and tool?
|
||||||
if strings.HasPrefix(rawMsg, assistantIcon) {
|
if strings.HasPrefix(rawMsg, assistantIcon) {
|
||||||
role = assistantRole
|
msg.Role = assistantRole
|
||||||
msg = strings.TrimPrefix(rawMsg, assistantIcon)
|
msg.Content = strings.TrimPrefix(rawMsg, assistantIcon)
|
||||||
goto messagebuild
|
return msg
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(rawMsg, userIcon) {
|
if strings.HasPrefix(rawMsg, userIcon) {
|
||||||
role = assistantRole
|
msg.Role = userRole
|
||||||
msg = strings.TrimPrefix(rawMsg, userIcon)
|
msg.Content = strings.TrimPrefix(rawMsg, userIcon)
|
||||||
goto messagebuild
|
return msg
|
||||||
}
|
}
|
||||||
messagebuild:
|
return msg
|
||||||
resp[i].Role = role
|
}
|
||||||
resp[i].Content = msg
|
|
||||||
|
func textSliceToChat(chat []string) []models.MessagesStory {
|
||||||
|
resp := make([]models.MessagesStory, len(chat))
|
||||||
|
for i, rawMsg := range chat {
|
||||||
|
msg := textToMsg(rawMsg)
|
||||||
|
resp[i] = msg
|
||||||
}
|
}
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|||||||
57
main.go
57
main.go
@@ -14,9 +14,11 @@ import (
|
|||||||
var (
|
var (
|
||||||
normalMode = false
|
normalMode = false
|
||||||
botRespMode = false
|
botRespMode = false
|
||||||
|
editMode = false
|
||||||
botMsg = "no"
|
botMsg = "no"
|
||||||
selectedIndex = int(-1)
|
selectedIndex = int(-1)
|
||||||
indexLine = "manage chats: F1; regen last: F2; delete msg menu: F3; Row: [yellow]%d[white], Column: [yellow]%d; normal mode: %v"
|
indexLine = "Esc: send msg; Tab: switch focus; F1: manage chats; F2: regen last; F3:delete msg menu; F4: edit msg; F5: toggle system; Row: [yellow]%d[white], Column: [yellow]%d; normal mode: %v"
|
||||||
|
focusSwitcher = map[tview.Primitive]tview.Primitive{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func isASCII(s string) bool {
|
func isASCII(s string) bool {
|
||||||
@@ -41,6 +43,8 @@ func main() {
|
|||||||
app.Draw()
|
app.Draw()
|
||||||
})
|
})
|
||||||
textView.SetBorder(true).SetTitle("chat")
|
textView.SetBorder(true).SetTitle("chat")
|
||||||
|
focusSwitcher[textArea] = textView
|
||||||
|
focusSwitcher[textView] = textArea
|
||||||
position := tview.NewTextView().
|
position := tview.NewTextView().
|
||||||
SetDynamicColors(true).
|
SetDynamicColors(true).
|
||||||
SetTextAlign(tview.AlignCenter)
|
SetTextAlign(tview.AlignCenter)
|
||||||
@@ -53,7 +57,7 @@ func main() {
|
|||||||
if fromRow == toRow && fromColumn == toColumn {
|
if fromRow == toRow && fromColumn == toColumn {
|
||||||
position.SetText(fmt.Sprintf(indexLine, fromRow, fromColumn, normalMode))
|
position.SetText(fmt.Sprintf(indexLine, fromRow, fromColumn, normalMode))
|
||||||
} else {
|
} else {
|
||||||
position.SetText(fmt.Sprintf("manage chats: F1; regen last: F2; delete msg menu: F3; [red]From[white] Row: [yellow]%d[white], Column: [yellow]%d[white] - [red]To[white] Row: [yellow]%d[white], To Column: [yellow]%d; normal mode: %v", fromRow, fromColumn, toRow, toColumn, normalMode))
|
position.SetText(fmt.Sprintf("Esc: send msg; Tab: switch focus; F1: manage chats; F2: regen last; F3:delete msg menu; F4: edit msg; F5: toggle system; Row: [yellow]%d[white], Column: [yellow]%d[white] - [red]To[white] Row: [yellow]%d[white], To Column: [yellow]%d; normal mode: %v", fromRow, fromColumn, toRow, toColumn, normalMode))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chatOpts := []string{"cancel", "new"}
|
chatOpts := []string{"cancel", "new"}
|
||||||
@@ -95,6 +99,24 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
editArea := tview.NewTextArea().
|
||||||
|
SetPlaceholder("Replace msg...")
|
||||||
|
editArea.SetBorder(true).SetTitle("input")
|
||||||
|
editArea.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
|
if event.Key() == tcell.KeyEscape && editMode {
|
||||||
|
editedMsg := editArea.GetText()
|
||||||
|
// TODO: trim msg number and icon
|
||||||
|
chatBody.Messages[selectedIndex].Content = editedMsg
|
||||||
|
// change textarea
|
||||||
|
textView.SetText(chatToText(showSystemMsgs))
|
||||||
|
pages.RemovePage("editArea")
|
||||||
|
editMode = false
|
||||||
|
// panic("do we get here?")
|
||||||
|
// pages.ShowPage("main")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return event
|
||||||
|
})
|
||||||
indexPickWindow := tview.NewInputField().
|
indexPickWindow := tview.NewInputField().
|
||||||
SetLabel("Enter a msg index: ").
|
SetLabel("Enter a msg index: ").
|
||||||
SetFieldWidth(4).
|
SetFieldWidth(4).
|
||||||
@@ -110,6 +132,16 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to convert provided index", "error", err, "si", si)
|
logger.Error("failed to convert provided index", "error", err, "si", si)
|
||||||
}
|
}
|
||||||
|
if len(chatBody.Messages) <= selectedIndex && selectedIndex < 0 {
|
||||||
|
logger.Warn("chosen index is out of bounds", "index", selectedIndex)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pages.AddPage("editArea", editArea, true, true)
|
||||||
|
m := chatBody.Messages[selectedIndex]
|
||||||
|
// editArea.SetText(m.ToText(selectedIndex), true)
|
||||||
|
editArea.SetText(m.Content, true)
|
||||||
|
editMode = true
|
||||||
|
// editArea.SetText(si, true)
|
||||||
}
|
}
|
||||||
return event
|
return event
|
||||||
})
|
})
|
||||||
@@ -119,10 +151,6 @@ func main() {
|
|||||||
textView.SetText(chatToText(showSystemMsgs))
|
textView.SetText(chatToText(showSystemMsgs))
|
||||||
textView.ScrollToEnd()
|
textView.ScrollToEnd()
|
||||||
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
if botRespMode {
|
|
||||||
// do nothing while bot typing
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if event.Key() == tcell.KeyF1 {
|
if event.Key() == tcell.KeyF1 {
|
||||||
fList, err := listHistoryFiles(historyDir)
|
fList, err := listHistoryFiles(historyDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -144,13 +172,22 @@ func main() {
|
|||||||
// modal window with input field
|
// modal window with input field
|
||||||
chatBody.Messages = chatBody.Messages[:len(chatBody.Messages)-1]
|
chatBody.Messages = chatBody.Messages[:len(chatBody.Messages)-1]
|
||||||
textView.SetText(chatToText(showSystemMsgs))
|
textView.SetText(chatToText(showSystemMsgs))
|
||||||
|
botRespMode = false // hmmm; is that correct?
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if event.Key() == tcell.KeyF4 {
|
if event.Key() == tcell.KeyF4 {
|
||||||
// edit msg
|
// edit msg
|
||||||
pages.AddPage("getIndex", indexPickWindow, true, true)
|
pages.AddPage("getIndex", indexPickWindow, true, true)
|
||||||
|
editMode = true
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if event.Key() == tcell.KeyEscape {
|
if event.Key() == tcell.KeyF5 {
|
||||||
|
// switch showSystemMsgs
|
||||||
|
showSystemMsgs = !showSystemMsgs
|
||||||
|
textView.SetText(chatToText(showSystemMsgs))
|
||||||
|
}
|
||||||
|
// cannot send msg in editMode or botRespMode
|
||||||
|
if event.Key() == tcell.KeyEscape && !editMode && !botRespMode {
|
||||||
fromRow, fromColumn, _, _ := textArea.GetCursor()
|
fromRow, fromColumn, _, _ := textArea.GetCursor()
|
||||||
position.SetText(fmt.Sprintf(indexLine, fromRow, fromColumn, normalMode))
|
position.SetText(fmt.Sprintf(indexLine, fromRow, fromColumn, normalMode))
|
||||||
// read all text into buffer
|
// read all text into buffer
|
||||||
@@ -164,7 +201,11 @@ func main() {
|
|||||||
go chatRound(msgText, userRole, textView)
|
go chatRound(msgText, userRole, textView)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if isASCII(string(event.Rune())) {
|
if event.Key() == tcell.KeyTab {
|
||||||
|
currentF := app.GetFocus()
|
||||||
|
app.SetFocus(focusSwitcher[currentF])
|
||||||
|
}
|
||||||
|
if isASCII(string(event.Rune())) && !botRespMode {
|
||||||
// normalMode = false
|
// normalMode = false
|
||||||
// fromRow, fromColumn, _, _ := textArea.GetCursor()
|
// fromRow, fromColumn, _, _ := textArea.GetCursor()
|
||||||
// position.SetText(fmt.Sprintf(indexLine, fromRow, fromColumn, normalMode))
|
// position.SetText(fmt.Sprintf(indexLine, fromRow, fromColumn, normalMode))
|
||||||
|
|||||||
Reference in New Issue
Block a user