summaryrefslogtreecommitdiff
path: root/props_table.go
diff options
context:
space:
mode:
authorGrail Finder <wohilas@gmail.com>2025-12-03 13:29:57 +0300
committerGrail Finder <wohilas@gmail.com>2025-12-03 13:29:57 +0300
commitcec10210c284a17cd341b48ee205bcfd278205bd (patch)
tree2976a543026c4ee79ad68bef5d970e104f684c4e /props_table.go
parente5eaba1d5a6c07b3a02d38dcc81dbac49bffcc38 (diff)
Feat: props table instead of form
Diffstat (limited to 'props_table.go')
-rw-r--r--props_table.go292
1 files changed, 292 insertions, 0 deletions
diff --git a/props_table.go b/props_table.go
new file mode 100644
index 0000000..c3dd7b2
--- /dev/null
+++ b/props_table.go
@@ -0,0 +1,292 @@
+package main
+
+import (
+ "fmt"
+ "slices"
+ "strconv"
+
+ "github.com/gdamore/tcell/v2"
+ "github.com/rivo/tview"
+)
+
+// Define constants for cell types
+const (
+ CellTypeCheckbox = "checkbox"
+ CellTypeDropdown = "dropdown"
+ CellTypeInput = "input"
+ CellTypeHeader = "header"
+)
+
+// CellData holds additional data for each cell
+type CellData struct {
+ Type string
+ Options []string
+ OnChange interface{}
+}
+
+// makePropsTable creates a table-based alternative to the props form
+// This allows for better key bindings and immediate effect of changes
+func makePropsTable(props map[string]float32) *tview.Table {
+ // Create a new table
+ table := tview.NewTable().
+ SetBorders(true).
+ SetSelectable(true, false) // Allow row selection but not column selection
+
+ table.SetTitle("Properties Configuration (Press 'x' to exit)").
+ SetTitleAlign(tview.AlignLeft)
+
+ row := 0
+
+ // Add a header or note row
+ headerCell := tview.NewTableCell("Props for llamacpp completion call").
+ SetTextColor(tcell.ColorYellow).
+ SetAlign(tview.AlignLeft).
+ SetSelectable(false)
+ table.SetCell(row, 0, headerCell)
+ table.SetCell(row, 1,
+ tview.NewTableCell("").
+ SetTextColor(tcell.ColorYellow).
+ SetSelectable(false))
+ row++
+
+ // Store cell data for later use in selection functions
+ cellData := make(map[string]*CellData)
+
+ // Helper function to add a checkbox-like row
+ addCheckboxRow := func(label string, initialValue bool, onChange func(bool)) {
+ table.SetCell(row, 0,
+ tview.NewTableCell(label).
+ SetTextColor(tcell.ColorWhite).
+ SetAlign(tview.AlignLeft).
+ SetSelectable(false))
+
+ valueText := "No"
+ if initialValue {
+ valueText = "Yes"
+ }
+
+ valueCell := tview.NewTableCell(valueText).
+ SetTextColor(tcell.ColorGreen).
+ SetAlign(tview.AlignCenter)
+ table.SetCell(row, 1, valueCell)
+
+ // Store cell data
+ cellID := fmt.Sprintf("checkbox_%d", row)
+ cellData[cellID] = &CellData{
+ Type: CellTypeCheckbox,
+ OnChange: onChange,
+ }
+ row++
+ }
+
+ // Helper function to add a dropdown-like row
+ addDropdownRow := func(label string, options []string, initialValue string, onChange func(string)) {
+ table.SetCell(row, 0,
+ tview.NewTableCell(label).
+ SetTextColor(tcell.ColorWhite).
+ SetAlign(tview.AlignLeft).
+ SetSelectable(false))
+
+ valueCell := tview.NewTableCell(initialValue).
+ SetTextColor(tcell.ColorGreen).
+ SetAlign(tview.AlignCenter)
+ table.SetCell(row, 1, valueCell)
+
+ // Store cell data
+ cellID := fmt.Sprintf("dropdown_%d", row)
+ cellData[cellID] = &CellData{
+ Type: CellTypeDropdown,
+ Options: options,
+ OnChange: onChange,
+ }
+ row++
+ }
+
+ // Helper function to add an input field row
+ addInputRow := func(label string, initialValue string, onChange func(string)) {
+ table.SetCell(row, 0,
+ tview.NewTableCell(label).
+ SetTextColor(tcell.ColorWhite).
+ SetAlign(tview.AlignLeft).
+ SetSelectable(false))
+
+ valueCell := tview.NewTableCell(initialValue).
+ SetTextColor(tcell.ColorGreen).
+ SetAlign(tview.AlignCenter)
+ table.SetCell(row, 1, valueCell)
+
+ // Store cell data
+ cellID := fmt.Sprintf("input_%d", row)
+ cellData[cellID] = &CellData{
+ Type: CellTypeInput,
+ OnChange: onChange,
+ }
+ row++
+ }
+
+ // Add checkboxes
+ addCheckboxRow("Insert \U000F0E88 (/completion only)", cfg.ThinkUse, func(checked bool) {
+ cfg.ThinkUse = checked
+ })
+
+ addCheckboxRow("RAG use", cfg.RAGEnabled, func(checked bool) {
+ cfg.RAGEnabled = checked
+ })
+
+ addCheckboxRow("Inject role", injectRole, func(checked bool) {
+ injectRole = checked
+ })
+
+ // Add dropdowns
+ logLevels := []string{"Debug", "Info", "Warn"}
+ addDropdownRow("Set log level", logLevels, GetLogLevel(), func(option string) {
+ setLogLevel(option)
+ })
+
+ // Prepare API links dropdown - insert current API at the beginning
+ apiLinks := slices.Insert(cfg.ApiLinks, 0, cfg.CurrentAPI)
+ addDropdownRow("Select an api", apiLinks, cfg.CurrentAPI, func(option string) {
+ cfg.CurrentAPI = option
+ })
+
+ // Prepare model list dropdown
+ modelList := []string{chatBody.Model, "deepseek-chat", "deepseek-reasoner"}
+ modelList = append(modelList, ORFreeModels...)
+ addDropdownRow("Select a model", modelList, chatBody.Model, func(option string) {
+ chatBody.Model = option
+ })
+
+ // Role selection dropdown
+ addDropdownRow("Write next message as", listRolesWithUser(), cfg.WriteNextMsgAs, func(option string) {
+ cfg.WriteNextMsgAs = option
+ })
+
+ // Add input fields
+ addInputRow("New char to write msg as", "", func(text string) {
+ if text != "" {
+ cfg.WriteNextMsgAs = text
+ }
+ })
+
+ addInputRow("Username", cfg.UserRole, func(text string) {
+ if text != "" {
+ renameUser(cfg.UserRole, text)
+ cfg.UserRole = text
+ }
+ })
+
+ // Add property fields (the float32 values)
+ for propName, value := range props {
+ propName := propName // capture loop variable for closure
+ propValue := fmt.Sprintf("%v", value)
+ addInputRow(propName, propValue, func(text string) {
+ if val, err := strconv.ParseFloat(text, 32); err == nil {
+ props[propName] = float32(val)
+ }
+ })
+ }
+
+ // Set selection function to handle dropdown-like behavior
+ table.SetSelectedFunc(func(selectedRow, selectedCol int) {
+ // Only handle selection on the value column (column 1)
+ if selectedCol != 1 {
+ // If user selects the label column, move to the value column
+ if table.GetRowCount() > selectedRow && table.GetColumnCount() > 1 {
+ table.Select(selectedRow, 1)
+ }
+ return
+ }
+
+ // Get the cell and its corresponding data
+ cell := table.GetCell(selectedRow, selectedCol)
+ cellID := fmt.Sprintf("checkbox_%d", selectedRow)
+
+ // Check if it's a checkbox
+ if cellData[cellID] != nil && cellData[cellID].Type == CellTypeCheckbox {
+ data := cellData[cellID]
+ if onChange, ok := data.OnChange.(func(bool)); ok {
+ // Toggle the checkbox value
+ newValue := cell.Text == "No"
+ onChange(newValue)
+ if newValue {
+ cell.SetText("Yes")
+ } else {
+ cell.SetText("No")
+ }
+ }
+ return
+ }
+
+ // Check for dropdown
+ dropdownCellID := fmt.Sprintf("dropdown_%d", selectedRow)
+ if cellData[dropdownCellID] != nil && cellData[dropdownCellID].Type == CellTypeDropdown {
+ data := cellData[dropdownCellID]
+ if onChange, ok := data.OnChange.(func(string)); ok && data.Options != nil {
+ // Find current option and cycle to next
+ currentValue := cell.Text
+ currentIndex := -1
+ for i, opt := range data.Options {
+ if opt == currentValue {
+ currentIndex = i
+ break
+ }
+ }
+
+ // Move to next option (cycle back to 0 if at end)
+ nextIndex := (currentIndex + 1) % len(data.Options)
+ newValue := data.Options[nextIndex]
+
+ onChange(newValue)
+ cell.SetText(newValue)
+ }
+ return
+ }
+
+ // Handle input fields by creating an input modal on selection
+ inputCellID := fmt.Sprintf("input_%d", selectedRow)
+ if cellData[inputCellID] != nil && cellData[inputCellID].Type == CellTypeInput {
+ data := cellData[inputCellID]
+ if onChange, ok := data.OnChange.(func(string)); ok {
+ // Create an input modal
+ currentValue := cell.Text
+ inputFld := tview.NewInputField()
+ inputFld.SetLabel("Edit value: ")
+ inputFld.SetText(currentValue)
+ inputFld.SetDoneFunc(func(key tcell.Key) {
+ if key == tcell.KeyEnter {
+ newText := inputFld.GetText()
+ onChange(newText)
+ cell.SetText(newText) // Update the table cell
+ }
+ pages.RemovePage("editModal")
+ })
+
+ // Create a simple modal with the input field
+ modalFlex := tview.NewFlex().
+ SetDirection(tview.FlexRow).
+ AddItem(tview.NewBox(), 0, 1, false). // Spacer
+ AddItem(tview.NewFlex().
+ AddItem(tview.NewBox(), 0, 1, false). // Spacer
+ AddItem(inputFld, 30, 1, true). // Input field
+ AddItem(tview.NewBox(), 0, 1, false), // Spacer
+ 0, 1, true).
+ AddItem(tview.NewBox(), 0, 1, false) // Spacer
+
+ // Add modal page and make it visible
+ pages.AddPage("editModal", modalFlex, true, true)
+ }
+ return
+ }
+ })
+
+ // Set input capture to handle 'x' key for exiting
+ table.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
+ if event.Key() == tcell.KeyRune && event.Rune() == 'x' {
+ pages.RemovePage(propsPage)
+ return nil
+ }
+ return event
+ })
+
+ return table
+} \ No newline at end of file