diff --git a/ui/keys.go b/ui/keys.go index 24bdbdb6..7784c2fb 100644 --- a/ui/keys.go +++ b/ui/keys.go @@ -199,11 +199,19 @@ func (m *Model) handleKey(msg tea.KeyMsg) tea.Cmd { case "r": m.playlist.CycleRepeat() + if err := config.Save("repeat", fmt.Sprintf("%q", m.playlist.Repeat().String())); err != nil { + m.saveMsg = fmt.Sprintf("Config save failed: %s", err) + m.saveMsgTTL = 60 + } m.player.ClearPreload() return m.preloadNext() case "z": m.playlist.ToggleShuffle() + if err := config.Save("shuffle", fmt.Sprintf("%v", m.playlist.Shuffled())); err != nil { + m.saveMsg = fmt.Sprintf("Config save failed: %s", err) + m.saveMsgTTL = 60 + } m.player.ClearPreload() return m.preloadNext() @@ -733,6 +741,8 @@ var keymapEntries = []keymapEntry{ {"< ,", "Previous track"}, {"← →", "Seek ±5s"}, {"+ -", "Volume up/down"}, + {"z", "Toggle shuffle"}, + {"r", "Cycle repeat"}, {"m", "Toggle mono"}, {"e", "Cycle EQ preset"}, {"t", "Choose theme"}, @@ -748,8 +758,6 @@ var keymapEntries = []keymapEntry{ {"p", "Playlist manager"}, {"i", "Track info / metadata"}, {"S", "Save track to ~/Music"}, - {"r", "Cycle repeat"}, - {"z", "Toggle shuffle"}, {"x", "Expand/collapse playlist"}, {"/", "Search playlist"}, {"Tab", "Toggle focus"}, diff --git a/ui/view.go b/ui/view.go index b6c8da89..59dd9073 100644 --- a/ui/view.go +++ b/ui/view.go @@ -841,16 +841,78 @@ func (m Model) renderHelp() string { return helpKey("↑↓", "Navigate ") + helpKey("Enter", "Load ") + helpKey("Tab", "Focus ") + helpKey("Q", "Quit") } - parts := helpKey("Spc", "⏯ ") + helpKey("<>", "Trk ") + // Build help hints with priority (lower = dropped first when too wide). + var hints []helpHint + + hints = append(hints, helpHint{helpKey("Spc", "⏯ "), 100}) + hints = append(hints, helpHint{helpKey("<>", "Trk "), 90}) track, _ := m.playlist.Current() if !track.Stream || m.player.Seekable() { - parts += helpKey("←→", "Seek ") + hints = append(hints, helpHint{helpKey("←→", "Seek "), 70}) } - parts += helpKey("+-", "Vol ") + helpKey("/", "Search ") + helpKey("a", "Queue ") + helpKey("Tab", "Focus ") + helpKey("Ctrl+K", "Keys ") + helpKey("Q", "Quit") + hints = append(hints, + helpHint{helpKey("+-", "Vol "), 80}, + helpHint{helpKey("z", "Shfl "), 20}, + helpHint{helpKey("r", "Rpt "), 20}, + helpHint{helpKey("/", "Search "), 40}, + helpHint{helpKey("a", "Queue "), 30}, + helpHint{helpKey("Tab", "Focus "), 50}, + helpHint{helpKey("Ctrl+K", "Keys "), 60}, + helpHint{helpKey("Q", "Quit"), 95}, + ) + + return fitHints(hints, m.width) +} + +// helpHint is a rendered help key with an associated display priority. +type helpHint struct { + text string + priority int +} + +// fitHints drops lowest-priority hints until they fit within maxWidth. +func fitHints(hints []helpHint, maxWidth int) string { + // Start with all hints; drop lowest priority until it fits. + active := make([]bool, len(hints)) + for i := range active { + active[i] = true + } + + for { + var total int + for i, h := range hints { + if active[i] { + total += lipgloss.Width(h.text) + } + } + if total <= maxWidth { + break + } + + // Find lowest-priority active hint and drop it. + minPri := 1<<31 - 1 + minIdx := -1 + for i, h := range hints { + if active[i] && h.priority < minPri { + minPri = h.priority + minIdx = i + } + } + if minIdx < 0 { + break // nothing left to drop + } + active[minIdx] = false + } - return parts + var result string + for i, h := range hints { + if active[i] { + result += h.text + } + } + return result } // renderStreamStatus shows a network stats line for HTTP streams: