diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | session.go | 19 | ||||
-rw-r--r-- | tables.go | 76 | ||||
-rw-r--r-- | tui.go | 28 |
4 files changed, 120 insertions, 4 deletions
@@ -9,3 +9,4 @@ sysprompts/* !sysprompts/cluedo.json history_bak/ .aider* +tags @@ -7,6 +7,7 @@ import ( "fmt" "os" "os/exec" + "path/filepath" "strings" "time" ) @@ -34,6 +35,24 @@ func exportChat() error { return os.WriteFile(activeChatName+".json", data, 0666) } +func importChat(filename string) error { + data, err := os.ReadFile(filename) + if err != nil { + return err + } + messages := []models.RoleMsg{} + if err := json.Unmarshal(data, &messages); err != nil { + return err + } + activeChatName = filepath.Base(filename) + chatBody.Messages = messages + cfg.AssistantRole = messages[1].Role + if cfg.AssistantRole == cfg.UserRole { + cfg.AssistantRole = messages[2].Role + } + return nil +} + func updateStorageChat(name string, msgs []models.RoleMsg) error { var err error chat, ok := chatMap[name] @@ -457,3 +457,79 @@ func makeCodeBlockTable(codeBlocks []string) *tview.Table { }) return table } + +func makeImportChatTable(filenames []string) *tview.Table { + actions := []string{"load"} + rows, cols := len(filenames), len(actions)+1 + chatActTable := tview.NewTable(). + SetBorders(true) + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + color := tcell.ColorWhite + if c < 1 { + chatActTable.SetCell(r, c, + tview.NewTableCell(filenames[r]). + SetTextColor(color). + SetAlign(tview.AlignCenter)) + } else { + chatActTable.SetCell(r, c, + tview.NewTableCell(actions[c-1]). + SetTextColor(color). + SetAlign(tview.AlignCenter)) + } + } + } + chatActTable.Select(0, 0).SetFixed(1, 1).SetDoneFunc(func(key tcell.Key) { + if key == tcell.KeyEsc || key == tcell.KeyF1 { + pages.RemovePage(historyPage) + return + } + if key == tcell.KeyEnter { + chatActTable.SetSelectable(true, true) + } + }).SetSelectedFunc(func(row int, column int) { + tc := chatActTable.GetCell(row, column) + tc.SetTextColor(tcell.ColorRed) + chatActTable.SetSelectable(false, false) + selected := filenames[row] + // notification := fmt.Sprintf("chat: %s; action: %s", selectedChat, tc.Text) + switch tc.Text { + case "load": + if err := importChat(selected); err != nil { + logger.Warn("failed to import chat", "filename", selected) + pages.RemovePage(historyPage) + return + } + colorText() + updateStatusLine() + // redraw the text in text area + textView.SetText(chatToText(cfg.ShowSys)) + pages.RemovePage(historyPage) + app.SetFocus(textArea) + return + case "rename": + pages.RemovePage(historyPage) + pages.AddPage(renamePage, renameWindow, true, true) + return + case "delete": + sc, ok := chatMap[selected] + if !ok { + // no chat found + pages.RemovePage(historyPage) + return + } + if err := store.RemoveChat(sc.ID); err != nil { + logger.Error("failed to remove chat from db", "chat_id", sc.ID, "chat_name", sc.Name) + } + if err := notifyUser("chat deleted", selected+" was deleted"); err != nil { + logger.Error("failed to send notification", "error", err) + } + pages.RemovePage(historyPage) + return + default: + pages.RemovePage(historyPage) + return + } + }) + return chatActTable +} @@ -8,6 +8,7 @@ import ( _ "image/jpeg" _ "image/png" "os" + "path" "slices" "strconv" "strings" @@ -54,7 +55,7 @@ var ( [yellow]F8[white]: copy n msg to clipboard (linux xclip) [yellow]F9[white]: table to copy from; with all code blocks [yellow]F10[white]: manage loaded rag files (that already in vector db) -[yellow]F11[white]: switch RAGEnabled boolean +[yellow]F11[white]: import chat file [yellow]F12[white]: show this help page [yellow]Ctrl+w[white]: resume generation on the last msg [yellow]Ctrl+s[white]: load new char/agent @@ -201,7 +202,9 @@ func makePropsForm(props map[string]float32) *tview.Form { AddTextView("Notes", "Props for llamacpp completion call", 40, 2, true, false). AddCheckbox("Insert <think> (/completion only)", cfg.ThinkUse, func(checked bool) { cfg.ThinkUse = checked - }).AddDropDown("Set log level (Enter): ", []string{"Debug", "Info", "Warn"}, 1, + }).AddCheckbox("RAG use", cfg.RAGEnabled, func(checked bool) { + cfg.RAGEnabled = checked + }).AddDropDown("Set log level (Enter): ", []string{"Debug", "Info", "Warn"}, 1, func(option string, optionIndex int) { setLogLevel(option) }).AddDropDown("Select an api: ", slices.Insert(cfg.ApiLinks, 0, cfg.CurrentAPI), 0, @@ -555,8 +558,25 @@ func init() { return nil } if event.Key() == tcell.KeyF11 { - // xor - cfg.RAGEnabled = !cfg.RAGEnabled + // read files in chat_exports + dirname := "chat_exports" + filelist, err := os.ReadDir(dirname) + if err != nil { + if err := notifyUser("failed to load exports", err.Error()); err != nil { + logger.Error("failed to send notification", "error", err) + } + } + fli := []string{} + for _, f := range filelist { + if f.IsDir() || !strings.HasSuffix(f.Name(), ".json") { + continue + } + fpath := path.Join(dirname, f.Name()) + fli = append(fli, fpath) + } + // check error + exportsTable := makeImportChatTable(fli) + pages.AddPage(historyPage, exportsTable, true, true) updateStatusLine() return nil } |