difi/internal/ui/model.go
2026-01-28 12:13:29 -05:00

135 lines
3.0 KiB
Go

package ui
import (
"fmt"
"io"
"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/oug-t/difi/internal/git"
"github.com/oug-t/difi/internal/tree"
)
const TargetBranch = "main"
type Model struct {
fileTree list.Model
diffViewport viewport.Model
selectedPath string
width, height int
}
func NewModel() Model {
files, _ := git.ListChangedFiles(TargetBranch)
items := tree.Build(files)
l := list.New(items, listDelegate{}, 0, 0)
l.SetShowTitle(false)
l.SetShowHelp(false)
l.SetShowStatusBar(false)
l.SetFilteringEnabled(false)
m := Model{
fileTree: l,
diffViewport: viewport.New(0, 0),
}
if len(items) > 0 {
if first, ok := items[0].(tree.TreeItem); ok {
m.selectedPath = first.FullPath
}
}
return m
}
func (m Model) Init() tea.Cmd {
if m.selectedPath != "" {
return git.DiffCmd(TargetBranch, m.selectedPath)
}
return nil
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
var cmds []tea.Cmd
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
treeWidth := int(float64(m.width) * 0.25)
m.fileTree.SetSize(treeWidth, m.height)
m.diffViewport.Width = m.width - treeWidth - 2
m.diffViewport.Height = m.height
case tea.KeyMsg:
switch msg.String() {
case "q", "ctrl+c":
return m, tea.Quit
case "up", "k", "down", "j":
m.fileTree, cmd = m.fileTree.Update(msg)
cmds = append(cmds, cmd)
if item, ok := m.fileTree.SelectedItem().(tree.TreeItem); ok && !item.IsDir {
if item.FullPath != m.selectedPath {
m.selectedPath = item.FullPath
cmds = append(cmds, git.DiffCmd(TargetBranch, m.selectedPath))
}
}
case "e":
if m.selectedPath != "" {
return m, git.OpenEditorCmd(m.selectedPath)
}
}
case git.DiffMsg:
m.diffViewport.SetContent(msg.Content)
case git.EditorFinishedMsg:
if msg.Err != nil {
}
return m, git.DiffCmd(TargetBranch, m.selectedPath)
}
return m, tea.Batch(cmds...)
}
func (m Model) View() string {
if m.width == 0 {
return "Loading..."
}
treeView := PaneStyle.Copy().
Width(m.fileTree.Width()).
Height(m.fileTree.Height()).
Render(m.fileTree.View())
diffView := DiffStyle.Copy().
Width(m.diffViewport.Width).
Height(m.diffViewport.Height).
Render(m.diffViewport.View())
return lipgloss.JoinHorizontal(lipgloss.Top, treeView, diffView)
}
type listDelegate struct{}
func (d listDelegate) Height() int { return 1 }
func (d listDelegate) Spacing() int { return 0 }
func (d listDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil }
func (d listDelegate) Render(w io.Writer, m list.Model, index int, item list.Item) {
i, ok := item.(tree.TreeItem)
if !ok {
return
}
str := i.Title()
if index == m.Index() {
fmt.Fprint(w, SelectedItemStyle.Render("│ "+str))
} else {
fmt.Fprint(w, ItemStyle.Render(str))
}
}