Compare commits

...

3 Commits

Author SHA1 Message Date
lerko be14436701 style(tui): accent keys, muted descriptions, dot separators in hotbar
CI / test (pull_request) Successful in 1m49s
CI / lint (pull_request) Successful in 1m17s
CI / vulncheck (pull_request) Successful in 56s
Keys rendered in accent color, descriptions in muted. Dot separators
between items. No brackets. Faster to scan at a glance.
2026-06-30 19:56:16 -04:00
lerko efa8894b18 refactor(tui): unify detail toggle on Enter, remove i keybind
Enter now toggles detail sidebar open/closed with preference
persistence. Removes redundant i keybind that did the same thing.
Hotbar updated: [Enter]Detail when closed, [Enter]Close when open.
2026-06-30 19:51:44 -04:00
lerko ef54b36e0d refactor(tui): make Enter always open detail, Space for group collapse
Enter now consistently opens the detail sidebar for both monitors and
groups. Group collapse/expand is Space-only. Hotbar shows [Space]Expand
or [Space]Collapse contextually when cursor is on a group row.
2026-06-30 19:09:51 -04:00
2 changed files with 41 additions and 50 deletions
+19 -38
View File
@@ -650,23 +650,27 @@ func (m *Model) handleDashboardKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
return m, nil
}
if len(m.sites) > 0 {
site := m.sites[m.cursor]
if site.Type == "group" {
m.collapsed[site.ID] = !m.collapsed[site.ID]
payload := collapsedJSON(m.collapsed)
st := m.store
ctx := m.ctx
m.refreshLive()
return m, writeCmd("Save collapsed groups", func() error {
return st.SetPreference(ctx, "collapsed_groups", payload)
})
m.detailOpen = !m.detailOpen
m.detailMode = detailDefault
m.recalcLayout()
st := m.store
ctx := m.ctx
open := m.detailOpen
var cmd tea.Cmd
if m.detailOpen {
cmd = m.loadDetailCmd(m.sites[m.cursor].ID)
}
if !m.detailOpen {
m.detailOpen = true
m.detailMode = detailDefault
m.recalcLayout()
saveCmd := writeCmd("Save detail preference", func() error {
v := "false"
if open {
v = "true"
}
return st.SetPreference(ctx, "detail_open", v)
})
if cmd != nil {
return m, tea.Batch(cmd, saveCmd)
}
return m, m.loadDetailCmd(site.ID)
return m, saveCmd
}
case "e":
return m.handleEditItem()
@@ -693,29 +697,6 @@ func (m *Model) handleDashboardKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
return st.UpdateSitePaused(ctx, id, paused)
})
}
case "i":
if len(m.sites) > 0 {
m.detailOpen = !m.detailOpen
m.recalcLayout()
st := m.store
ctx := m.ctx
open := m.detailOpen
var cmd tea.Cmd
if m.detailOpen {
cmd = m.loadDetailCmd(m.sites[m.cursor].ID)
}
saveCmd := writeCmd("Save detail preference", func() error {
v := "false"
if open {
v = "true"
}
return st.SetPreference(ctx, "detail_open", v)
})
if cmd != nil {
return m, tea.Batch(cmd, saveCmd)
}
return m, saveCmd
}
case "esc":
if m.focusedPanel != panelMonitors {
m.focusedPanel = panelMonitors
+22 -12
View File
@@ -277,36 +277,46 @@ func (m Model) renderStatusLine(stats dashboardStats) string {
return left + strings.Repeat(" ", padW) + ver
}
func (m Model) hotkey(key, desc string) string {
k := lipgloss.NewStyle().Foreground(m.theme.Accent).Render(key)
d := m.st.subtleStyle.Render(desc)
return k + " " + d
}
func (m Model) renderFooter(_ dashboardStats) string {
dot := m.st.subtleStyle.Render(" · ")
if m.filterMode {
cursor := lipgloss.NewStyle().Foreground(m.theme.Accent).Render("│")
return "\n" + m.st.titleStyle.Render("/") + " " + m.filterText + cursor + " " + m.st.subtleStyle.Render("[Enter]Apply [Esc]Clear")
keys := m.hotkey("Enter", "Apply") + dot + m.hotkey("Esc", "Clear")
return "\n" + m.st.titleStyle.Render("/") + " " + m.filterText + cursor + " " + keys
}
var keys string
var parts []string
if m.focusedPanel == panelMaint {
keys = "[n]New [x]End [d]Del [Esc]Back [S]Settings [T]Theme [q]Quit"
parts = []string{m.hotkey("n", "New"), m.hotkey("x", "End"), m.hotkey("d", "Del"), m.hotkey("Esc", "Back")}
} else if m.focusedPanel == panelLogs {
keys = "[↑/↓]Scroll [Enter]Expand [l/Esc]Back [S]Settings [T]Theme [q]Quit"
parts = []string{m.hotkey("↑/↓", "Scroll"), m.hotkey("Enter", "Expand"), m.hotkey("l/Esc", "Back")}
} else if m.detailOpen && m.detailMode == detailSLA {
keys = "[1-4]Period [Esc]Back [l]Logs [S]Settings [T]Theme [q]Quit"
parts = []string{m.hotkey("1-4", "Period"), m.hotkey("Esc", "Back")}
} else if m.detailOpen && m.detailMode == detailHistory {
keys = "[Esc]Back [l]Logs [S]Settings [T]Theme [q]Quit"
parts = []string{m.hotkey("Esc", "Back")}
} else if m.detailOpen {
keys = "[i]Close [h]History [s]SLA [e]Edit [m]Maint [l]Logs [S]Settings [T]Theme [q]Quit"
parts = []string{m.hotkey("Enter", "Close"), m.hotkey("h", "History"), m.hotkey("s", "SLA"), m.hotkey("e", "Edit"), m.hotkey("m", "Maint")}
} else {
enterHint := "[Enter]Detail"
parts = []string{m.hotkey("/", "Filter"), m.hotkey("Enter", "Detail")}
if m.cursor < len(m.sites) && m.sites[m.cursor].Type == "group" {
if m.collapsed[m.sites[m.cursor].ID] {
enterHint = "[Enter]Expand"
parts = append(parts, m.hotkey("Space", "Expand"))
} else {
enterHint = "[Enter]Collapse"
parts = append(parts, m.hotkey("Space", "Collapse"))
}
}
keys = "[/]Filter [i]Info " + enterHint + " [n]New [e]Edit [d]Del [m]Maint [l]Logs [S]Settings [T]Theme [q]Quit"
parts = append(parts, m.hotkey("n", "New"), m.hotkey("e", "Edit"), m.hotkey("d", "Del"), m.hotkey("m", "Maint"))
}
parts = append(parts, m.hotkey("l", "Logs"), m.hotkey("S", "Settings"), m.hotkey("T", "Theme"), m.hotkey("q", "Quit"))
line := m.st.subtleStyle.Render(keys)
line := strings.Join(parts, dot)
if m.filterText != "" {
line = m.st.subtleStyle.Render(fmt.Sprintf("filter: %s ", m.filterText)) + line
}