summaryrefslogtreecommitdiff
path: root/internal/tui/pidpicker
diff options
context:
space:
mode:
Diffstat (limited to 'internal/tui/pidpicker')
-rw-r--r--internal/tui/pidpicker/model.go55
-rw-r--r--internal/tui/pidpicker/model_test.go16
2 files changed, 51 insertions, 20 deletions
diff --git a/internal/tui/pidpicker/model.go b/internal/tui/pidpicker/model.go
index 4e63429..34da674 100644
--- a/internal/tui/pidpicker/model.go
+++ b/internal/tui/pidpicker/model.go
@@ -2,7 +2,7 @@ package pidpicker
import (
"fmt"
- "ior/internal/tui"
+ "ior/internal/tui/messages"
"strings"
"github.com/charmbracelet/bubbles/key"
@@ -13,6 +13,37 @@ import (
const allPIDsLabel = "All PIDs"
+// KeyMap defines picker-specific key bindings.
+type KeyMap struct {
+ Enter key.Binding
+ Esc key.Binding
+ Refresh key.Binding
+}
+
+// DefaultKeyMap returns picker defaults.
+func DefaultKeyMap() KeyMap {
+ return KeyMap{
+ Enter: key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "select")),
+ Esc: key.NewBinding(key.WithKeys("esc"), key.WithHelp("esc", "back")),
+ Refresh: key.NewBinding(key.WithKeys("r"), key.WithHelp("r", "refresh")),
+ }
+}
+
+func (k KeyMap) PickerShortHelp() []key.Binding {
+ return []key.Binding{k.Enter, k.Refresh, k.Esc}
+}
+
+var (
+ screenStyle = lipgloss.NewStyle()
+ headerStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("75"))
+ helpBarStyle = lipgloss.NewStyle().
+ Foreground(lipgloss.Color("246")).
+ BorderTop(true).
+ BorderForeground(lipgloss.Color("238"))
+ highlightStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("222"))
+ errorStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("203"))
+)
+
type processesLoadedMsg struct {
processes []ProcessInfo
err error
@@ -26,17 +57,17 @@ type Model struct {
selectedIndex int
width int
height int
- keys tui.KeyMap
+ keys KeyMap
lastErr error
}
// New creates a PID picker model with default shared key bindings.
func New() Model {
- return NewWithKeys(tui.Keys)
+ return NewWithKeys(DefaultKeyMap())
}
// NewWithKeys creates a PID picker model with the provided key bindings.
-func NewWithKeys(keys tui.KeyMap) Model {
+func NewWithKeys(keys KeyMap) Model {
input := textinput.New()
input.Prompt = "Filter: "
input.Placeholder = "pid, comm, or cmdline"
@@ -117,16 +148,16 @@ func (m Model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
func (m Model) emitSelection() tea.Cmd {
if m.selectedIndex <= 0 {
- return func() tea.Msg { return tui.PidSelectedMsg{Pid: 0} }
+ return func() tea.Msg { return messages.PidSelectedMsg{Pid: 0} }
}
idx := m.selectedIndex - 1
if idx < 0 || idx >= len(m.filtered) {
- return func() tea.Msg { return tui.PidSelectedMsg{Pid: 0} }
+ return func() tea.Msg { return messages.PidSelectedMsg{Pid: 0} }
}
pid := m.filtered[idx].Pid
- return func() tea.Msg { return tui.PidSelectedMsg{Pid: pid} }
+ return func() tea.Msg { return messages.PidSelectedMsg{Pid: pid} }
}
func (m *Model) applyFilter() {
@@ -175,7 +206,7 @@ func cloneProcesses(in []ProcessInfo) []ProcessInfo {
// View renders the PID picker with filter input, list, and help bar.
func (m Model) View() string {
var b strings.Builder
- b.WriteString(tui.HeaderStyle.Render("Select PID"))
+ b.WriteString(headerStyle.Render("Select PID"))
b.WriteString("\n")
b.WriteString(m.input.View())
b.WriteString("\n\n")
@@ -185,12 +216,12 @@ func (m Model) View() string {
if m.lastErr != nil {
b.WriteString("\n")
- b.WriteString(tui.ErrorStyle.Render("scan error: " + m.lastErr.Error()))
+ b.WriteString(errorStyle.Render("scan error: " + m.lastErr.Error()))
}
b.WriteString("\n")
- b.WriteString(tui.HelpBarStyle.Render(renderHelp(m.keys.PickerShortHelp())))
- return tui.ScreenStyle.Render(b.String())
+ b.WriteString(helpBarStyle.Render(renderHelp(m.keys.PickerShortHelp())))
+ return screenStyle.Render(b.String())
}
func (m Model) renderRows() string {
@@ -221,7 +252,7 @@ func (m Model) renderRow(index int, label string) string {
style := lipgloss.NewStyle()
if index == m.selectedIndex {
prefix = "> "
- style = tui.HighlightStyle
+ style = highlightStyle
}
return style.Render(prefix + label)
}
diff --git a/internal/tui/pidpicker/model_test.go b/internal/tui/pidpicker/model_test.go
index c8e59af..7347eca 100644
--- a/internal/tui/pidpicker/model_test.go
+++ b/internal/tui/pidpicker/model_test.go
@@ -1,7 +1,7 @@
package pidpicker
import (
- "ior/internal/tui"
+ "ior/internal/tui/messages"
"strings"
"testing"
@@ -9,7 +9,7 @@ import (
)
func TestApplyFilterByPIDCommAndCmdline(t *testing.T) {
- m := NewWithKeys(tui.DefaultKeyMap())
+ m := NewWithKeys(DefaultKeyMap())
m.processes = []ProcessInfo{
{Pid: 100, Comm: "bash", Cmdline: "bash -l"},
{Pid: 200, Comm: "sshd", Cmdline: "/usr/sbin/sshd -D"},
@@ -35,14 +35,14 @@ func TestApplyFilterByPIDCommAndCmdline(t *testing.T) {
}
func TestEnterEmitsAllPIDsAndSelectedPID(t *testing.T) {
- m := NewWithKeys(tui.DefaultKeyMap())
+ m := NewWithKeys(DefaultKeyMap())
m.processes = []ProcessInfo{{Pid: 7, Comm: "vim"}, {Pid: 9, Comm: "top"}}
m.applyFilter()
modelAny, cmdAny := m.Update(tea.KeyMsg{Type: tea.KeyEnter})
_ = modelAny
msgAny := cmdAny()
- pidAny, ok := msgAny.(tui.PidSelectedMsg)
+ pidAny, ok := msgAny.(messages.PidSelectedMsg)
if !ok {
t.Fatalf("expected PidSelectedMsg for all-pids selection, got %T", msgAny)
}
@@ -54,7 +54,7 @@ func TestEnterEmitsAllPIDsAndSelectedPID(t *testing.T) {
modelOne, cmdOne := m.Update(tea.KeyMsg{Type: tea.KeyEnter})
_ = modelOne
msgOne := cmdOne()
- pidOne, ok := msgOne.(tui.PidSelectedMsg)
+ pidOne, ok := msgOne.(messages.PidSelectedMsg)
if !ok {
t.Fatalf("expected PidSelectedMsg for concrete selection, got %T", msgOne)
}
@@ -64,7 +64,7 @@ func TestEnterEmitsAllPIDsAndSelectedPID(t *testing.T) {
}
func TestEscQuitsAndRefreshTriggersScan(t *testing.T) {
- m := NewWithKeys(tui.DefaultKeyMap())
+ m := NewWithKeys(DefaultKeyMap())
_, escCmd := m.Update(tea.KeyMsg{Type: tea.KeyEsc})
if escCmd == nil {
@@ -84,7 +84,7 @@ func TestEscQuitsAndRefreshTriggersScan(t *testing.T) {
}
func TestRuneRDoesNotTriggerRefreshWhileFilterFocused(t *testing.T) {
- m := NewWithKeys(tui.DefaultKeyMap())
+ m := NewWithKeys(DefaultKeyMap())
next, cmd := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'r'}})
if cmd == nil {
@@ -98,7 +98,7 @@ func TestRuneRDoesNotTriggerRefreshWhileFilterFocused(t *testing.T) {
}
func TestRenderRowsKeepsSelectionVisible(t *testing.T) {
- m := NewWithKeys(tui.DefaultKeyMap())
+ m := NewWithKeys(DefaultKeyMap())
m.height = 8 // visible rows == 2
m.processes = []ProcessInfo{
{Pid: 1, Comm: "p1"},