mirror of
				https://github.com/shokinn/hosts-go.git
				synced 2025-11-04 04:28:34 +00:00 
			
		
		
		
	feat: begin tui implementation
This commit is contained in:
		
							parent
							
								
									b81f11f711
								
							
						
					
					
						commit
						1b66db10e2
					
				
					 9 changed files with 200 additions and 154 deletions
				
			
		
							
								
								
									
										57
									
								
								internal/tui/model.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								internal/tui/model.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
package tui
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"hosts-go/internal/core"
 | 
			
		||||
 | 
			
		||||
	list "github.com/charmbracelet/bubbles/list"
 | 
			
		||||
	tea "github.com/charmbracelet/bubbletea"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// entryItem wraps a HostEntry for display in a list component.
 | 
			
		||||
type entryItem struct{ entry *core.HostEntry }
 | 
			
		||||
 | 
			
		||||
func (e entryItem) Title() string       { return e.entry.Hostname }
 | 
			
		||||
func (e entryItem) Description() string { return e.entry.IP }
 | 
			
		||||
func (e entryItem) FilterValue() string { return e.entry.Hostname }
 | 
			
		||||
 | 
			
		||||
// Model is the main Bubble Tea model for the application.
 | 
			
		||||
type Model struct {
 | 
			
		||||
	list   list.Model
 | 
			
		||||
	hosts  *core.HostsFile
 | 
			
		||||
	width  int
 | 
			
		||||
	height int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewModel constructs the TUI model from a parsed HostsFile.
 | 
			
		||||
func NewModel(hf *core.HostsFile) Model {
 | 
			
		||||
	items := make([]list.Item, len(hf.Entries))
 | 
			
		||||
	for i, e := range hf.Entries {
 | 
			
		||||
		items[i] = entryItem{entry: e}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l := list.New(items, list.NewDefaultDelegate(), 0, 0)
 | 
			
		||||
	l.SetShowStatusBar(false)
 | 
			
		||||
	l.SetFilteringEnabled(false)
 | 
			
		||||
	l.SetShowHelp(false)
 | 
			
		||||
	l.SetShowPagination(false)
 | 
			
		||||
 | 
			
		||||
	return Model{
 | 
			
		||||
		list:  l,
 | 
			
		||||
		hosts: hf,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init satisfies tea.Model.
 | 
			
		||||
func (m Model) Init() tea.Cmd { return nil }
 | 
			
		||||
 | 
			
		||||
// SelectedEntry returns the currently selected host entry.
 | 
			
		||||
func (m Model) SelectedEntry() *core.HostEntry {
 | 
			
		||||
	if len(m.hosts.Entries) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	idx := m.list.Index()
 | 
			
		||||
	if idx < 0 || idx >= len(m.hosts.Entries) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return m.hosts.Entries[idx]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								internal/tui/update.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								internal/tui/update.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
package tui
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	tea "github.com/charmbracelet/bubbletea"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Update handles all messages for the TUI.
 | 
			
		||||
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 | 
			
		||||
	var cmd tea.Cmd
 | 
			
		||||
	switch msg := msg.(type) {
 | 
			
		||||
	case tea.KeyMsg:
 | 
			
		||||
		switch msg.String() {
 | 
			
		||||
		case "q", "ctrl+c":
 | 
			
		||||
			return m, tea.Quit
 | 
			
		||||
		}
 | 
			
		||||
	case tea.WindowSizeMsg:
 | 
			
		||||
		m.width = msg.Width
 | 
			
		||||
		m.height = msg.Height
 | 
			
		||||
		m.list.SetSize(msg.Width/2, msg.Height)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m.list, cmd = m.list.Update(msg)
 | 
			
		||||
	return m, cmd
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								internal/tui/view.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								internal/tui/view.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
package tui
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/charmbracelet/lipgloss"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	listStyle   = lipgloss.NewStyle().Padding(0, 1)
 | 
			
		||||
	detailStyle = lipgloss.NewStyle().Padding(0, 1)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// View renders the two-pane layout.
 | 
			
		||||
func (m Model) View() string {
 | 
			
		||||
	listView := m.list.View()
 | 
			
		||||
 | 
			
		||||
	var detail strings.Builder
 | 
			
		||||
	if len(m.hosts.Entries) > 0 {
 | 
			
		||||
		entry := m.hosts.Entries[m.list.Index()]
 | 
			
		||||
		status := "active"
 | 
			
		||||
		if !entry.Active {
 | 
			
		||||
			status = "inactive"
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Fprintf(&detail, "IP: %s\n", entry.IP)
 | 
			
		||||
		fmt.Fprintf(&detail, "Host: %s\n", entry.Hostname)
 | 
			
		||||
		if len(entry.Aliases) > 0 {
 | 
			
		||||
			fmt.Fprintf(&detail, "Aliases: %s\n", strings.Join(entry.Aliases, ", "))
 | 
			
		||||
		}
 | 
			
		||||
		if entry.Comment != "" {
 | 
			
		||||
			fmt.Fprintf(&detail, "Comment: %s\n", entry.Comment)
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Fprintf(&detail, "Status: %s", status)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	left := listStyle.Width(m.width / 2).Height(m.height).Render(listView)
 | 
			
		||||
	right := detailStyle.Width(m.width - m.width/2).Height(m.height).Render(detail.String())
 | 
			
		||||
	return lipgloss.JoinHorizontal(lipgloss.Top, left, right)
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue