Fix audio quality with per-user mixing buffer and prevent TUI layout break on log overflow
This commit is contained in:
140
cmd/tui/model.go
140
cmd/tui/model.go
@@ -66,7 +66,6 @@ type Model struct {
|
||||
selectedIdx int
|
||||
chatMessages []ChatMessage
|
||||
logMessages []string // Debug logs shown in chat panel
|
||||
logFullscreen bool // Toggle fullscreen log view
|
||||
inputText string
|
||||
inputActive bool
|
||||
|
||||
@@ -86,6 +85,7 @@ type Model struct {
|
||||
|
||||
// Program reference for sending messages from event handlers
|
||||
program *tea.Program
|
||||
showLog bool
|
||||
}
|
||||
|
||||
// addLog adds a message to the log panel
|
||||
@@ -213,7 +213,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
// Handle incoming audio - play through speakers
|
||||
m.client.On(ts3client.EventAudio, func(e *ts3client.AudioEvent) {
|
||||
if m.audioPlayer != nil {
|
||||
m.audioPlayer.PlayPCM(e.PCM)
|
||||
m.audioPlayer.PlayPCM(e.SenderID, e.PCM)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -449,6 +449,13 @@ func (m *Model) handleKeyPress(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
case "l", "L":
|
||||
// Toggle Log/Chat view
|
||||
if m.focus != FocusInput {
|
||||
m.showLog = !m.showLog
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Focus-specific keys
|
||||
@@ -464,10 +471,6 @@ func (m *Model) handleKeyPress(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
func (m *Model) handleChatKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
switch msg.String() {
|
||||
case "f":
|
||||
m.logFullscreen = !m.logFullscreen
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
@@ -524,18 +527,6 @@ func (m *Model) View() string {
|
||||
return "Loading..."
|
||||
}
|
||||
|
||||
// Fullscreen Log Mode
|
||||
if m.logFullscreen {
|
||||
style := lipgloss.NewStyle().
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("212")). // Active color
|
||||
Padding(0, 1).
|
||||
Width(m.width - 2).
|
||||
Height(m.height - 2)
|
||||
|
||||
return style.Render(m.renderChat())
|
||||
}
|
||||
|
||||
// Styles
|
||||
// Layout: header(1) + panels + input(3) + help(1) = header + panels + 4
|
||||
// panels should be height - 5 (1 for header, 3 for input with border, 1 for help)
|
||||
@@ -571,12 +562,60 @@ func (m *Model) View() string {
|
||||
}
|
||||
channelPanel := channelPanelStyle.Render(channelContent)
|
||||
|
||||
// Chat panel
|
||||
chatContent := m.renderChat()
|
||||
if m.focus == FocusChat {
|
||||
chatPanelStyle = chatPanelStyle.BorderForeground(lipgloss.Color("212"))
|
||||
// Right panel (Chat or Log)
|
||||
var rightPanel string
|
||||
|
||||
if m.showLog {
|
||||
// Log View
|
||||
// Use chatPanelStyle default but add focus logic
|
||||
logStyle := chatPanelStyle.Copy()
|
||||
|
||||
if m.focus == FocusChat {
|
||||
logStyle = logStyle.BorderForeground(lipgloss.Color("212"))
|
||||
}
|
||||
|
||||
// Limit to last N messages log to fit panel
|
||||
// Panel height is m.height - 7, padding reduces it more
|
||||
maxLines := m.height - 11
|
||||
if maxLines < 1 {
|
||||
maxLines = 1
|
||||
}
|
||||
|
||||
start := 0
|
||||
if len(m.logMessages) > maxLines {
|
||||
start = len(m.logMessages) - maxLines
|
||||
}
|
||||
|
||||
// Calculate available width for text
|
||||
textWidth := (m.width * 2 / 3) - 6
|
||||
if textWidth < 10 {
|
||||
textWidth = 10
|
||||
}
|
||||
|
||||
textStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("240"))
|
||||
var lines []string
|
||||
|
||||
for _, msg := range m.logMessages[start:] {
|
||||
// Sanitize: remove newlines/returns to prevent double spacing
|
||||
msg = strings.ReplaceAll(msg, "\n", " ")
|
||||
msg = strings.ReplaceAll(msg, "\r", "")
|
||||
|
||||
// Truncate simplisticly to prevent wrapping issues if too long
|
||||
if len(msg) > textWidth {
|
||||
msg = msg[:textWidth-3] + "..."
|
||||
}
|
||||
lines = append(lines, textStyle.Render(msg))
|
||||
}
|
||||
|
||||
rightPanel = logStyle.Render(strings.Join(lines, "\n"))
|
||||
} else {
|
||||
// Chat View
|
||||
chatContent := m.renderChat()
|
||||
if m.focus == FocusChat {
|
||||
chatPanelStyle = chatPanelStyle.BorderForeground(lipgloss.Color("212"))
|
||||
}
|
||||
rightPanel = chatPanelStyle.Render(chatContent)
|
||||
}
|
||||
chatPanel := chatPanelStyle.Render(chatContent)
|
||||
|
||||
// Input
|
||||
inputContent := "> " + m.inputText
|
||||
@@ -587,10 +626,14 @@ func (m *Model) View() string {
|
||||
input := inputStyle.Render(inputContent)
|
||||
|
||||
// Footer help
|
||||
help := lipgloss.NewStyle().Faint(true).Render("↑↓ navigate │ Enter join │ Tab switch │ V talk │ M mute │ +/- vol │ q quit")
|
||||
logHelp := "L log"
|
||||
if m.showLog {
|
||||
logHelp = "L chat"
|
||||
}
|
||||
help := lipgloss.NewStyle().Faint(true).Render(fmt.Sprintf("↑↓ navigate │ Enter join │ Tab switch │ %s │ V talk │ M mute │ +/- vol │ q quit", logHelp))
|
||||
|
||||
// Combine panels
|
||||
panels := lipgloss.JoinHorizontal(lipgloss.Top, channelPanel, chatPanel)
|
||||
panels := lipgloss.JoinHorizontal(lipgloss.Top, channelPanel, rightPanel)
|
||||
|
||||
return lipgloss.JoinVertical(lipgloss.Left,
|
||||
header,
|
||||
@@ -721,43 +764,36 @@ func (m *Model) renderChannels() string {
|
||||
|
||||
func (m *Model) renderChat() string {
|
||||
var lines []string
|
||||
lines = append(lines, lipgloss.NewStyle().Bold(true).Render("LOG"))
|
||||
lines = append(lines, "")
|
||||
|
||||
if len(m.logMessages) == 0 {
|
||||
lines = append(lines, lipgloss.NewStyle().Faint(true).Render("No logs yet..."))
|
||||
if len(m.chatMessages) == 0 {
|
||||
lines = append(lines, lipgloss.NewStyle().Faint(true).Render("No chat messages yet..."))
|
||||
} else {
|
||||
// Limit to last N messages that fit in the panel
|
||||
maxLines := m.height - 10
|
||||
if maxLines < 5 {
|
||||
maxLines = 5
|
||||
// Limit messages to fit panel
|
||||
// Panel height is roughly m.height - 7
|
||||
maxLines := m.height - 9
|
||||
if maxLines < 1 {
|
||||
maxLines = 1
|
||||
}
|
||||
|
||||
start := 0
|
||||
if len(m.logMessages) > maxLines {
|
||||
start = len(m.logMessages) - maxLines
|
||||
if len(m.chatMessages) > maxLines {
|
||||
start = len(m.chatMessages) - maxLines
|
||||
}
|
||||
|
||||
panelWidth := (m.width / 2) - 4
|
||||
if m.logFullscreen {
|
||||
panelWidth = m.width - 6
|
||||
}
|
||||
if panelWidth < 10 {
|
||||
panelWidth = 10
|
||||
}
|
||||
for _, msg := range m.chatMessages[start:] {
|
||||
prefix := msg.Time.Format("15:04") + " " + msg.Sender + ": "
|
||||
content := msg.Content
|
||||
|
||||
for _, msg := range m.logMessages[start:] {
|
||||
// Truncate if too long to prevent wrapping breaking the layout
|
||||
displayMsg := msg
|
||||
if len(displayMsg) > panelWidth {
|
||||
displayMsg = displayMsg[:panelWidth-3] + "..."
|
||||
}
|
||||
// Replace newlines just in case
|
||||
displayMsg = strings.ReplaceAll(displayMsg, "\n", " ")
|
||||
|
||||
lines = append(lines, lipgloss.NewStyle().Faint(true).Render(displayMsg))
|
||||
// Simple wrapping logic could go here, but for now simple truncation/display
|
||||
line := lipgloss.NewStyle().Foreground(lipgloss.Color("241")).Render(prefix) + content
|
||||
lines = append(lines, line)
|
||||
}
|
||||
}
|
||||
|
||||
// Fill remaining lines with empty space to maintain stable layout
|
||||
for len(lines) < m.height-9 {
|
||||
lines = append(lines, "")
|
||||
}
|
||||
|
||||
return lipgloss.JoinVertical(lipgloss.Left, lines...)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user