From cbc6369071f9ff0745fe17043e3ec73e171a354c Mon Sep 17 00:00:00 2001 From: "Brandon McGinty (chatgpt)" Date: Sun, 9 Aug 2026 15:12:53 -0400 Subject: [PATCH] Preserve UTF-8 when truncating input status --- client_notification_test.go | 10 ++++++++++ ui.go | 13 ++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/client_notification_test.go b/client_notification_test.go index 762191d..59b3bf4 100644 --- a/client_notification_test.go +++ b/client_notification_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" "time" + "unicode/utf8" "git.stormux.org/storm/barnard/gumble/gumble" ) @@ -57,6 +58,15 @@ func TestNotificationExpansionIsSinglePassAndNotifyDoesNotBlock(t *testing.T) { // and renderer resources. Cleanup must be safe for repeated disconnects. // Regression: user and channel names in the navigation tree bypassed message // escaping and could still carry terminal control characters. +// Regression: status truncation used byte indexes and could create invalid +// UTF-8 when a non-ASCII user or channel name exceeded the display limit. +func TestTruncateInputStatusPreservesUTF8(t *testing.T) { + got := truncateInputStatus(strings.Repeat("é", 21)) + if !utf8.ValidString(got) || utf8.RuneCountInString(got) != 21 { + t.Fatalf("invalid truncation %q", got) + } +} + func TestTreeItemSanitizesServerNames(t *testing.T) { item := TreeItem{Channel: &gumble.Channel{Name: "\x1b[2Jroom"}} if got := item.String(); got != "#[2Jroom" { diff --git a/ui.go b/ui.go index 1aee71b..71cfccc 100644 --- a/ui.go +++ b/ui.go @@ -62,14 +62,21 @@ func (b *Barnard) GetInputStatus() string { } func (b *Barnard) UpdateInputStatus(status string) { - if len(status) > 20 { - status = status[:17] + "..." + "]" - } + status = truncateInputStatus(status) b.UiInputStatus.Text = status b.RebuildUserChannelTreePreservingSelection() b.Ui.Refresh() } +// truncateInputStatus limits terminal cells without splitting UTF-8 runes. +func truncateInputStatus(status string) string { + chars := []rune(status) + if len(chars) > 20 { + return string(chars[:17]) + "..." + "]" + } + return status +} + func (b *Barnard) AddOutputLine(line string) { now := time.Now() b.UiOutput.AddLine(fmt.Sprintf("%s [%02d:%02d:%02d]", line, now.Hour(), now.Minute(), now.Second()))