package filepicker import ( "git.mlow.ca/mlow/lmcli/pkg/util/dirtree" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) type Model struct { tree *dirtree.Tree cursor int displayNodes []*dirtree.Node // Flattened view of visible nodes } func New(path string) Model { m := Model{ tree: dirtree.NewTree(path), } m.tree.Root.LoadChildren(false, 0, nil) m.updateRenderTree() return m } func (m *Model) updateRenderTree() { m.displayNodes = dirtree.FlattenTree(m.tree.Root, false) } func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "j": if m.cursor < len(m.displayNodes)-1 { m.cursor++ } case "k": if m.cursor > 0 { m.cursor-- } case "l", "right": current := m.displayNodes[m.cursor] if current.IsDir() { current.Expanded = true current.LoadChildren(false, 0, nil) m.updateRenderTree() } case "h", "left": current := m.displayNodes[m.cursor] if current.IsDir() && current.Expanded { current.Expanded = false m.updateRenderTree() } else { // Find and collapse parent if !current.IsRoot() { current.Parent().Expanded = false m.updateRenderTree() // Move cursor to parent for i, node := range m.displayNodes { if node == current.Parent() { m.cursor = i break } } } } case " ": current := m.displayNodes[m.cursor] if current.IsSelected() { current.Deselect(true) } else { current.Select(true, 0) } m.updateRenderTree() case "H": m.tree.ShowHidden = !m.tree.ShowHidden m.tree.Root.LoadChildren(false, 0, nil) } } return m, nil } func (m Model) Init() tea.Cmd { return nil } func (m Model) View() string { // Define styles dirStyle := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("12")) // Blue for directories fileStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("7")) // White for files selectedStyle := lipgloss.NewStyle().Bold(true) renderNode := func(node *dirtree.Node) string { var nodeName string if node.IsDir() { caret := "▶ " if node.Expanded { caret = "▼ " } nodeName = caret + node.Name + "/" nodeName = dirStyle.Render(nodeName) } else { nodeName = fileStyle.Render(node.Name) } if node.IsSelected() { nodeName = selectedStyle.Render(nodeName) } return nodeName } return dirtree.RenderNodes(m.displayNodes, dirtree.Compact, m.cursor, renderNode) }