Compare commits
5 Commits
feat/tab-c
...
27fdec1361
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27fdec1361 | ||
|
|
76827a71cc | ||
|
|
3a9a7dbe99 | ||
|
|
d3361c13c5 | ||
|
|
7c1a8b0122 |
2
bot.go
2
bot.go
@@ -1070,7 +1070,7 @@ func findCall(msg, toolCall string) bool {
|
||||
}
|
||||
resp := callToolWithAgent(fc.Name, fc.Args)
|
||||
toolMsg := string(resp) // Remove the "tool response: " prefix and %+v formatting
|
||||
logger.Info("llm used tool call", "tool_resp", toolMsg, "tool_attrs", fc)
|
||||
logger.Info("llm used a tool call", "tool_name", fc.Name, "too_args", fc.Args, "id", fc.ID, "tool_resp", toolMsg)
|
||||
fmt.Fprintf(textView, "%s[-:-:b](%d) <%s>: [-:-:-]\n%s\n",
|
||||
"\n\n", len(chatBody.Messages), cfg.ToolRole, toolMsg)
|
||||
// Create tool response message with the proper tool_call_id
|
||||
|
||||
41
helpfuncs.go
41
helpfuncs.go
@@ -9,6 +9,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"unicode"
|
||||
@@ -706,23 +707,51 @@ func searchPrev() {
|
||||
// == tab completion ==
|
||||
|
||||
func scanFiles(dir, filter string) []string {
|
||||
const maxDepth = 3
|
||||
const maxFiles = 50
|
||||
var files []string
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return files
|
||||
|
||||
var scanRecursive func(currentDir string, currentDepth int, relPath string)
|
||||
scanRecursive = func(currentDir string, currentDepth int, relPath string) {
|
||||
if len(files) >= maxFiles {
|
||||
return
|
||||
}
|
||||
if currentDepth > maxDepth {
|
||||
return
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(currentDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if len(files) >= maxFiles {
|
||||
return
|
||||
}
|
||||
|
||||
name := entry.Name()
|
||||
if strings.HasPrefix(name, ".") {
|
||||
continue
|
||||
}
|
||||
if filter == "" || strings.HasPrefix(strings.ToLower(name), strings.ToLower(filter)) {
|
||||
|
||||
fullPath := name
|
||||
if relPath != "" {
|
||||
fullPath = relPath + "/" + name
|
||||
}
|
||||
|
||||
if entry.IsDir() {
|
||||
files = append(files, name+"/")
|
||||
// Recursively scan subdirectories
|
||||
scanRecursive(filepath.Join(currentDir, name), currentDepth+1, fullPath)
|
||||
} else {
|
||||
files = append(files, name)
|
||||
// Check if file matches filter
|
||||
if filter == "" || strings.HasPrefix(strings.ToLower(fullPath), strings.ToLower(filter)) {
|
||||
files = append(files, fullPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scanRecursive(dir, 0, "")
|
||||
return files
|
||||
}
|
||||
|
||||
120
popups.go
120
popups.go
@@ -387,3 +387,123 @@ func showFileCompletionPopup(filter string) {
|
||||
pages.AddPage("fileCompletionPopup", modal(widget, 80, 20), true, true)
|
||||
app.SetFocus(widget)
|
||||
}
|
||||
|
||||
func updateWidgetColors(theme tview.Theme) {
|
||||
bgColor := theme.PrimitiveBackgroundColor
|
||||
fgColor := theme.PrimaryTextColor
|
||||
borderColor := theme.BorderColor
|
||||
titleColor := theme.TitleColor
|
||||
|
||||
textView.SetBackgroundColor(bgColor)
|
||||
textView.SetTextColor(fgColor)
|
||||
textView.SetBorderColor(borderColor)
|
||||
textView.SetTitleColor(titleColor)
|
||||
|
||||
textArea.SetBackgroundColor(bgColor)
|
||||
textArea.SetBorderColor(borderColor)
|
||||
textArea.SetTitleColor(titleColor)
|
||||
textArea.SetTextStyle(tcell.StyleDefault.Background(bgColor).Foreground(fgColor))
|
||||
textArea.SetPlaceholderStyle(tcell.StyleDefault.Background(bgColor).Foreground(fgColor))
|
||||
// Force textarea refresh by restoring text (SetTextStyle doesn't trigger redraw)
|
||||
textArea.SetText(textArea.GetText(), true)
|
||||
|
||||
editArea.SetBackgroundColor(bgColor)
|
||||
editArea.SetBorderColor(borderColor)
|
||||
editArea.SetTitleColor(titleColor)
|
||||
editArea.SetTextStyle(tcell.StyleDefault.Background(bgColor).Foreground(fgColor))
|
||||
editArea.SetPlaceholderStyle(tcell.StyleDefault.Background(bgColor).Foreground(fgColor))
|
||||
// Force textarea refresh by restoring text (SetTextStyle doesn't trigger redraw)
|
||||
editArea.SetText(editArea.GetText(), true)
|
||||
|
||||
statusLineWidget.SetBackgroundColor(bgColor)
|
||||
statusLineWidget.SetTextColor(fgColor)
|
||||
statusLineWidget.SetBorderColor(borderColor)
|
||||
statusLineWidget.SetTitleColor(titleColor)
|
||||
|
||||
helpView.SetBackgroundColor(bgColor)
|
||||
helpView.SetTextColor(fgColor)
|
||||
helpView.SetBorderColor(borderColor)
|
||||
helpView.SetTitleColor(titleColor)
|
||||
|
||||
searchField.SetBackgroundColor(bgColor)
|
||||
searchField.SetBorderColor(borderColor)
|
||||
searchField.SetTitleColor(titleColor)
|
||||
}
|
||||
|
||||
// showColorschemeSelectionPopup creates a modal popup to select a colorscheme
|
||||
func showColorschemeSelectionPopup() {
|
||||
// Get the list of available colorschemes
|
||||
schemeNames := make([]string, 0, len(colorschemes))
|
||||
for name := range colorschemes {
|
||||
schemeNames = append(schemeNames, name)
|
||||
}
|
||||
slices.Sort(schemeNames)
|
||||
// Check for empty options list
|
||||
if len(schemeNames) == 0 {
|
||||
logger.Warn("no colorschemes available for selection")
|
||||
message := "No colorschemes available."
|
||||
if err := notifyUser("Empty list", message); err != nil {
|
||||
logger.Error("failed to send notification", "error", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
// Create a list primitive
|
||||
schemeListWidget := tview.NewList().ShowSecondaryText(false).
|
||||
SetSelectedBackgroundColor(tcell.ColorGray)
|
||||
schemeListWidget.SetTitle("Select Colorscheme").SetBorder(true)
|
||||
|
||||
currentScheme := "default"
|
||||
for name := range colorschemes {
|
||||
if tview.Styles == colorschemes[name] {
|
||||
currentScheme = name
|
||||
break
|
||||
}
|
||||
}
|
||||
currentSchemeIndex := -1
|
||||
for i, scheme := range schemeNames {
|
||||
if scheme == currentScheme {
|
||||
currentSchemeIndex = i
|
||||
}
|
||||
schemeListWidget.AddItem(scheme, "", 0, nil)
|
||||
}
|
||||
// Set the current selection if found
|
||||
if currentSchemeIndex != -1 {
|
||||
schemeListWidget.SetCurrentItem(currentSchemeIndex)
|
||||
}
|
||||
schemeListWidget.SetSelectedFunc(func(index int, mainText string, secondaryText string, shortcut rune) {
|
||||
// Update the colorscheme
|
||||
if theme, ok := colorschemes[mainText]; ok {
|
||||
tview.Styles = theme
|
||||
go func() {
|
||||
app.QueueUpdateDraw(func() {
|
||||
updateWidgetColors(theme)
|
||||
})
|
||||
}()
|
||||
}
|
||||
// Remove the popup page
|
||||
pages.RemovePage("colorschemeSelectionPopup")
|
||||
})
|
||||
schemeListWidget.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if event.Key() == tcell.KeyEscape {
|
||||
pages.RemovePage("colorschemeSelectionPopup")
|
||||
return nil
|
||||
}
|
||||
if event.Key() == tcell.KeyRune && event.Rune() == 'x' {
|
||||
pages.RemovePage("colorschemeSelectionPopup")
|
||||
return nil
|
||||
}
|
||||
return event
|
||||
})
|
||||
modal := func(p tview.Primitive, width, height int) tview.Primitive {
|
||||
return tview.NewFlex().
|
||||
AddItem(nil, 0, 1, false).
|
||||
AddItem(tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(nil, 0, 1, false).
|
||||
AddItem(p, height, 1, true).
|
||||
AddItem(nil, 0, 1, false), width, 1, true).
|
||||
AddItem(nil, 0, 1, false)
|
||||
}
|
||||
// Add modal page and make it visible
|
||||
pages.AddPage("colorschemeSelectionPopup", modal(schemeListWidget, 40, len(schemeNames)+2), true, true)
|
||||
app.SetFocus(schemeListWidget)
|
||||
}
|
||||
|
||||
13
tui.go
13
tui.go
@@ -96,6 +96,7 @@ var (
|
||||
[yellow]Alt+8[white]: show char img or last picked img
|
||||
[yellow]Alt+9[white]: warm up (load) selected llama.cpp model
|
||||
[yellow]Alt+t[white]: toggle thinking blocks visibility (collapse/expand <think> blocks)
|
||||
[yellow]Alt+c[white]: show colorscheme selection popup
|
||||
|
||||
=== scrolling chat window (some keys similar to vim) ===
|
||||
[yellow]arrows up/down and j/k[white]: scroll up and down
|
||||
@@ -112,7 +113,7 @@ var (
|
||||
[yellow]x[white]: to exit
|
||||
|
||||
=== shell mode ===
|
||||
[yellow]@match->Tab[white]: file completion (type @ in input to get file suggestions)
|
||||
[yellow]@match->Tab[white]: file completion with relative paths (recursive, depth 3, max 50 files)
|
||||
|
||||
=== status line ===
|
||||
%s
|
||||
@@ -134,7 +135,7 @@ Press <Enter> or 'x' to return
|
||||
ContrastSecondaryTextColor: tcell.ColorLime,
|
||||
},
|
||||
"gruvbox": tview.Theme{
|
||||
PrimitiveBackgroundColor: tcell.ColorBlack, // Matches #1e1e2e
|
||||
PrimitiveBackgroundColor: tcell.NewHexColor(0x282828), // Background: #282828 (dark gray)
|
||||
ContrastBackgroundColor: tcell.ColorDarkGoldenrod, // Selected option: warm yellow (#b57614)
|
||||
MoreContrastBackgroundColor: tcell.ColorDarkSlateGray, // Non-selected options: dark grayish-blue (#32302f)
|
||||
BorderColor: tcell.ColorLightGray, // Light gray (#a89984)
|
||||
@@ -147,7 +148,7 @@ Press <Enter> or 'x' to return
|
||||
ContrastSecondaryTextColor: tcell.ColorLightGreen, // Light green (#b8bb26)
|
||||
},
|
||||
"solarized": tview.Theme{
|
||||
PrimitiveBackgroundColor: tcell.NewHexColor(0x1e1e2e), // #1e1e2e for main dropdown box
|
||||
PrimitiveBackgroundColor: tcell.NewHexColor(0x002b36), // Background: #002b36 (base03)
|
||||
ContrastBackgroundColor: tcell.ColorDarkCyan, // Selected option: cyan (#2aa198)
|
||||
MoreContrastBackgroundColor: tcell.ColorDarkSlateGray, // Non-selected options: dark blue (#073642)
|
||||
BorderColor: tcell.ColorLightBlue, // Light blue (#839496)
|
||||
@@ -160,7 +161,7 @@ Press <Enter> or 'x' to return
|
||||
ContrastSecondaryTextColor: tcell.ColorLightCyan, // Light cyan (#93a1a1)
|
||||
},
|
||||
"dracula": tview.Theme{
|
||||
PrimitiveBackgroundColor: tcell.NewHexColor(0x1e1e2e), // #1e1e2e for main dropdown box
|
||||
PrimitiveBackgroundColor: tcell.NewHexColor(0x282a36), // Background: #282a36
|
||||
ContrastBackgroundColor: tcell.ColorDarkMagenta, // Selected option: magenta (#bd93f9)
|
||||
MoreContrastBackgroundColor: tcell.ColorDarkGray, // Non-selected options: dark gray (#44475a)
|
||||
BorderColor: tcell.ColorLightGray, // Light gray (#f8f8f2)
|
||||
@@ -560,6 +561,10 @@ func init() {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if event.Key() == tcell.KeyRune && event.Rune() == 'i' && event.Modifiers()&tcell.ModAlt != 0 {
|
||||
showColorschemeSelectionPopup()
|
||||
return nil
|
||||
}
|
||||
if event.Key() == tcell.KeyF1 {
|
||||
// chatList, err := loadHistoryChats()
|
||||
chatList, err := store.GetChatByChar(cfg.AssistantRole)
|
||||
|
||||
Reference in New Issue
Block a user