From f01e19e64a0b1572de3e9d6c7613a6ef5c4b3737 Mon Sep 17 00:00:00 2001 From: Sam McLeod Date: Wed, 5 Jun 2024 19:40:16 +1000 Subject: [PATCH 1/2] chore: linting --- helpers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpers.go b/helpers.go index 91b6216..b0b1c2a 100644 --- a/helpers.go +++ b/helpers.go @@ -17,14 +17,14 @@ import ( func parseAPIResponse(resp *api.ListResponse) []Model { models := make([]Model, len(resp.Models)) for i, modelResp := range resp.Models { + modelName := lipgloss.NewStyle().Foreground(lipgloss.Color("white")).Render(modelResp.Name) models[i] = Model{ - Name: lipgloss.NewStyle().Foreground(lipgloss.Color("white")).Render(modelResp.Name), + Name: modelName, ID: truncate(modelResp.Digest, 7), // Truncate the ID Size: float64(modelResp.Size) / (1024 * 1024 * 1024), // Convert bytes to GB QuantizationLevel: modelResp.Details.QuantizationLevel, Family: modelResp.Details.Family, Modified: modelResp.ModifiedAt, - Selected: false, } } return models From 8942d42487de87fdf9e45e3db090c1725ab3517c Mon Sep 17 00:00:00 2001 From: Sam McLeod Date: Mon, 10 Jun 2024 10:02:30 +1000 Subject: [PATCH 2/2] fix top, help (sort of) --- app_model.go | 89 +++++++++++++++++++++++++++++++++++++++--------- item_delegate.go | 2 -- main.go | 47 ++++++++++++------------- top_view.go | 3 +- 4 files changed, 98 insertions(+), 43 deletions(-) diff --git a/app_model.go b/app_model.go index 67569f8..da89794 100644 --- a/app_model.go +++ b/app_model.go @@ -21,6 +21,7 @@ type View int const ( MainView View = iota TopView + HelpView ) func (m *AppModel) Init() tea.Cmd { @@ -92,12 +93,12 @@ func (m *AppModel) handleKeyMsg(msg tea.KeyMsg) (tea.Model, tea.Cmd) { // Handle other keys switch msg.String() { case "q": - if m.list.FilterState() == list.Filtering { + if m.list.FilterState() == list.FilterApplied { logging.DebugLogger.Println("Clearing filter with 'q' key") m.list.ResetFilter() return m, nil } - if m.view == TopView || m.inspecting { + if m.view == TopView || m.inspecting || m.view == HelpView { m.view = MainView m.inspecting = false m.editing = false @@ -106,18 +107,18 @@ func (m *AppModel) handleKeyMsg(msg tea.KeyMsg) (tea.Model, tea.Cmd) { return m, tea.Quit } case "esc": - if m.list.FilterState() == list.Filtering { + if m.list.FilterState() == list.FilterApplied { logging.DebugLogger.Println("Clearing filter with 'esc' key") m.list.ResetFilter() return m, nil } - if m.view == TopView || m.inspecting { + if m.view == TopView || m.inspecting || m.view == HelpView { m.view = MainView m.inspecting = false m.editing = false return m, nil } else { - return m, tea.Quit + return m, nil } case "ctrl+c": if m.editing { @@ -131,22 +132,22 @@ func (m *AppModel) handleKeyMsg(msg tea.KeyMsg) (tea.Model, tea.Cmd) { switch { case key.Matches(msg, m.keys.ConfirmYes): logging.DebugLogger.Println("ConfirmYes key matched") - for _, selectedModel := range m.selectedForDeletion { + for _, selectedModel := range m.selectedModels { logging.InfoLogger.Printf("Attempting to delete model: %s\n", selectedModel.Name) err := deleteModel(m.client, selectedModel.Name) if err != nil { logging.ErrorLogger.Println("Error deleting model:", err) } } - m.models = removeModels(m.models, m.selectedForDeletion) + m.models = removeModels(m.models, m.selectedModels) m.refreshList() m.confirmDeletion = false - m.selectedForDeletion = nil + m.selectedModels = nil case key.Matches(msg, m.keys.ConfirmNo): logging.DebugLogger.Println("ConfirmNo key matched") logging.InfoLogger.Println("Deletion cancelled by user") m.confirmDeletion = false - m.selectedForDeletion = nil + m.selectedModels = nil } return m, nil } @@ -183,6 +184,10 @@ func (m *AppModel) handleKeyMsg(msg tea.KeyMsg) (tea.Model, tea.Cmd) { return m.handlePushModelKey() case key.Matches(msg, m.keys.InspectModel): return m.handleInspectModelKey() + case key.Matches(msg, m.keys.Top): + return m.handleTopKey() + case key.Matches(msg, m.keys.Help): + return m.handleHelpKey() default: m.list, cmd = m.list.Update(msg) return m, cmd @@ -234,6 +239,20 @@ func (m *AppModel) handleProgressMsg(msg progressMsg) (tea.Model, tea.Cmd) { }) } +func (m *AppModel) handleHelpKey() (tea.Model, tea.Cmd) { + logging.DebugLogger.Println("Help key matched") + if m.view == HelpView { + m.message = "" + m.view = MainView + m.refreshList() + m.clearScreen() + return m, nil + } + m.view = HelpView + m.message = m.printFullHelp() + return m, nil +} + func (m *AppModel) handleEditorFinishedMsg(msg editorFinishedMsg) (tea.Model, tea.Cmd) { if msg.err != nil { m.message = fmt.Sprintf("Error editing modelfile: %v", msg.err) @@ -280,12 +299,12 @@ func (m *AppModel) handleDeleteKey() (tea.Model, tea.Cmd) { } if len(selectedModels) > 0 { - m.selectedForDeletion = selectedModels - logging.InfoLogger.Printf("Selected models for deletion: %+v\n", m.selectedForDeletion) + m.selectedModels = selectedModels + logging.InfoLogger.Printf("Selected models for deletion: %+v\n", m.selectedModels) m.confirmDeletion = true } else if item, ok := m.list.SelectedItem().(Model); ok { - m.selectedForDeletion = []Model{item} - logging.InfoLogger.Printf("Selected model for deletion: %+v\n", m.selectedForDeletion) + m.selectedModels = []Model{item} + logging.InfoLogger.Printf("Selected model for deletion: %+v\n", m.selectedModels) m.confirmDeletion = true } return m, nil @@ -364,6 +383,13 @@ func (m *AppModel) handleClearScreenKey() (tea.Model, tea.Cmd) { return m, nil } +// top view handler +func (m *AppModel) handleTopKey() (tea.Model, tea.Cmd) { + logging.DebugLogger.Println("Top key matched") + m.view = TopView + return m.ToggleTop() +} + func (m *AppModel) handleUpdateModelKey() (tea.Model, tea.Cmd) { logging.DebugLogger.Println("UpdateModel key matched") if item, ok := m.list.SelectedItem().(Model); ok { @@ -563,7 +589,7 @@ func (m *AppModel) filterView() string { func (m *AppModel) selectedModelNames() []string { var names []string - for _, model := range m.selectedForDeletion { + for _, model := range m.selectedModels { names = append(names, model.Name) } return names @@ -641,11 +667,40 @@ func (k KeyMap) FullHelp() [][]key.Binding { // a function that can be called from the man app_model.go file with a hotkey to print the FullHelp as a string func (m *AppModel) printFullHelp() string { - help := lipgloss.NewStyle().Foreground(lipgloss.Color("129")).Render("Help") + // TODO: this borks up the list formatting after exiting the help view + if m.view != HelpView { + m.message = "" + return m.message + } + + // Create a new table and use FullHelp() to populate it + columns := []table.Column{ + {Title: "Key", Width: 10}, + {Title: "Description", Width: 50}, + } + + rows := []table.Row{} for _, column := range m.keys.FullHelp() { for _, key := range column { - help += fmt.Sprintf(" %s: %s\n", key.Help().Key, key.Help().Desc) + rows = append(rows, table.Row{key.Help().Key, key.Help().Desc}) } } - return help + + t := table.New( + table.WithColumns(columns), + table.WithRows(rows), + table.WithFocused(true), + table.WithHeight(len(rows)+1), + ) + + // Set the table styles + s := table.DefaultStyles() + s.Header = s.Header.BorderStyle(lipgloss.NormalBorder()).BorderForeground(lipgloss.Color("240")) + s.Selected = s.Selected.Foreground(lipgloss.Color("229")).Background(lipgloss.Color("57")) + + t.SetStyles(s) + + // Render the table view + return "\n" + t.View() + "\nPress 'q' or `esc` to return to the main view." + } diff --git a/item_delegate.go b/item_delegate.go index 9fada82..9919ca3 100644 --- a/item_delegate.go +++ b/item_delegate.go @@ -35,8 +35,6 @@ func (d itemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { logging.DebugLogger.Printf("Delegate toggling selection for model: %s (before: %v)\n", i.Name, i.Selected) i.Selected = !i.Selected m.SetItem(m.Index(), i) - logging.DebugLogger.Printf("Delegate toggled selection for model: %s (after: %v)\n", i.Name, i.Selected) - // Update the main model list d.appModel.models[m.Index()] = i logging.DebugLogger.Printf("Updated main model list for model: %s (after: %v)\n", i.Name, i.Selected) diff --git a/main.go b/main.go index ea54a80..fef09ac 100644 --- a/main.go +++ b/main.go @@ -23,29 +23,29 @@ import ( ) type AppModel struct { - width int - height int - ollamaModelsDir string - cfg *config.Config - inspectedModel Model - list list.Model - models []Model - selectedForDeletion []Model - confirmDeletion bool - inspecting bool - editing bool - message string - keys KeyMap - client *api.Client - lmStudioModelsDir string - noCleanup bool - table table.Model - filterInput tea.Model - showTop bool - progress progress.Model - altscreenActive bool - view View - showProgress bool + width int + height int + ollamaModelsDir string + cfg *config.Config + inspectedModel Model + list list.Model + models []Model + selectedModels []Model + confirmDeletion bool + inspecting bool + editing bool + message string + keys KeyMap + client *api.Client + lmStudioModelsDir string + noCleanup bool + table table.Model + filterInput tea.Model + showTop bool + progress progress.Model + altscreenActive bool + view View + showProgress bool } type progressMsg struct { @@ -211,6 +211,7 @@ func main() { keys.PushModel, keys.Top, keys.UpdateModel, + keys.Help, } } diff --git a/top_view.go b/top_view.go index 6387471..248da26 100644 --- a/top_view.go +++ b/top_view.go @@ -50,10 +50,11 @@ func (m *TopModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.quitting = true return m, tea.Quit } + case tea.WindowSizeMsg: m.table.SetWidth(msg.Width) m.table.SetHeight(msg.Height) - case tea.Msg: // Corrected from tea.TickMsg + case tea.Msg: return m, m.updateRunningModels() }