Files
SkrueckenandCursor aa8f89b43a Jump to status options with their first-letter shortcuts.
o/w/d select OPEN/WIP/DONE in the status menu, with the hotkeys shown inline.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-19 10:38:28 +02:00

97 lines
2.0 KiB
Go

package views
import (
"fmt"
"strings"
"unicode"
"github.com/charmbracelet/lipgloss"
"task/internal/ticket"
)
// StatusMenu renders a status picker overlay.
type StatusMenu struct {
Cursor int
Width int
Styles StatusMenuStyles
}
// StatusMenuStyles holds styles for the status picker.
type StatusMenuStyles struct {
Box lipgloss.Style
Title lipgloss.Style
Selected lipgloss.Style
Normal lipgloss.Style
}
// NewStatusMenu creates a menu with the given status pre-selected.
func NewStatusMenu(current ticket.Status, width int, styles StatusMenuStyles) StatusMenu {
return StatusMenu{
Cursor: current.Index(),
Width: width,
Styles: styles,
}
}
// MoveDown moves the selection down.
func (m *StatusMenu) MoveDown() {
if m.Cursor < len(ticket.AllStatuses)-1 {
m.Cursor++
}
}
// MoveUp moves the selection up.
func (m *StatusMenu) MoveUp() {
if m.Cursor > 0 {
m.Cursor--
}
}
// JumpToShortcut moves the cursor to the status whose label starts with r.
func (m *StatusMenu) JumpToShortcut(r rune) bool {
r = unicode.ToLower(r)
for i, st := range ticket.AllStatuses {
if st.Shortcut() == r {
m.Cursor = i
return true
}
}
return false
}
// Selected returns the currently highlighted status.
func (m StatusMenu) Selected() ticket.Status {
if m.Cursor < 0 || m.Cursor >= len(ticket.AllStatuses) {
return ticket.StatusTodo
}
return ticket.AllStatuses[m.Cursor]
}
// View renders the status menu.
func (m StatusMenu) View() string {
var lines []string
lines = append(lines, m.Styles.Title.Render("Set status"))
for i, st := range ticket.AllStatuses {
entry := fmt.Sprintf("%c %s", st.Shortcut(), st.Label())
if i == m.Cursor {
lines = append(lines, m.Styles.Selected.Render("▸ "+entry))
} else {
lines = append(lines, m.Styles.Normal.Render(" "+entry))
}
}
body := strings.Join(lines, "\n")
w := m.Width
if w < 20 {
w = 20
}
return m.Styles.Box.Width(min(40, w-2)).Render(body)
}
func min(a, b int) int {
if a < b {
return a
}
return b
}