diff --git a/cmd/tui/model.go b/cmd/tui/model.go index f96f995..cda1f87 100644 --- a/cmd/tui/model.go +++ b/cmd/tui/model.go @@ -31,6 +31,14 @@ type ListItem struct { User *UserNode } +func (l ListItem) IsSpacer() bool { + if l.IsUser || l.Channel == nil { + return false + } + // TeamSpeak spacers usually look like [spacer0], [*spacer1], etc. + return strings.Contains(strings.ToLower(l.Channel.Name), "[spacer") +} + // ChatMessage represents a message in the chat type ChatMessage struct { Time time.Time @@ -503,6 +511,35 @@ func (m *Model) updateChannelList(channels []*ts3client.Channel) { } } } + + // Ensure selectedIdx is valid (not on a spacer) + if len(m.items) > 0 { + if m.selectedIdx >= len(m.items) { + m.selectedIdx = len(m.items) - 1 + } + + // If current is a spacer, find next valid one + if m.items[m.selectedIdx].IsSpacer() { + found := false + // Try going down + for i := m.selectedIdx; i < len(m.items); i++ { + if !m.items[i].IsSpacer() { + m.selectedIdx = i + found = true + break + } + } + // If not found, try going up + if !found { + for i := m.selectedIdx; i >= 0; i-- { + if !m.items[i].IsSpacer() { + m.selectedIdx = i + break + } + } + } + } + } } func (m *Model) handleKeyPress(msg tea.KeyMsg) (tea.Model, tea.Cmd) { @@ -673,17 +710,37 @@ func (m *Model) handleUserViewKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) { func (m *Model) handleChannelKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) { switch msg.String() { case "up", "k": - if m.selectedIdx > 0 { + for m.selectedIdx > 0 { m.selectedIdx-- + if !m.items[m.selectedIdx].IsSpacer() { + break + } + // If we hit the top and it's a spacer, we might need to go down to find the first valid one + if m.selectedIdx == 0 && m.items[m.selectedIdx].IsSpacer() { + // Search forward for the first valid one + for i := 0; i < len(m.items); i++ { + if !m.items[i].IsSpacer() { + m.selectedIdx = i + break + } + } + break + } } case "down", "j": - if m.selectedIdx < len(m.items)-1 { + for m.selectedIdx < len(m.items)-1 { m.selectedIdx++ + if !m.items[m.selectedIdx].IsSpacer() { + break + } } case "enter": // Join selected channel OR open user view if m.selectedIdx < len(m.items) && m.client != nil { item := m.items[m.selectedIdx] + if item.IsSpacer() { + return m, nil // Do nothing for spacers + } if !item.IsUser { // Channel ch := item.Channel