From 56f276580f615d0663d32c79f592832366740791 Mon Sep 17 00:00:00 2001 From: gogongxt Date: Wed, 13 May 2026 17:09:42 +0800 Subject: [PATCH] Fix hints kitten breaking multi-byte UTF-8 characters when overlaying hint labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In highlight_mark(), mark_text was sliced using byte-based indexing (mark_text[:len(hint)] and mark_text[len(hint):]). Since len(hint) equals the rune count (hint is always ASCII), but len(mark_text) is a byte count, this could slice in the middle of a multi-byte UTF-8 sequence (e.g. CJK characters), producing an invalid byte sequence rendered as the Unicode replacement character (�). Fix by converting mark_text to []rune first, then slicing at rune boundaries. The hint is ASCII so len(hint) == rune count, requiring no conversion on the hint side. Co-Authored-By: Claude Opus 4.7 --- kittens/hints/main.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kittens/hints/main.go b/kittens/hints/main.go index 8d4142f8b..e8249c12c 100644 --- a/kittens/hints/main.go +++ b/kittens/hints/main.go @@ -200,10 +200,12 @@ func main(_ *cli.Command, o *Options, args []string) (rc int, err error) { if hint == "" { hint = " " } - if len(mark_text) <= len(hint) { + hint_runes := len(hint) + runes := []rune(mark_text) + if len(runes) <= hint_runes { mark_text = "" } else { - replaced_text := mark_text[:len(hint)] + replaced_text := string(runes[:hint_runes]) replaced_text = strings.ReplaceAll(replaced_text, "\r", "\n") if strings.Contains(replaced_text, "\n") { buf := strings.Builder{} @@ -224,7 +226,7 @@ func main(_ *cli.Command, o *Options, args []string) (rc int, err error) { } hint = buf.String() } - mark_text = mark_text[len(hint):] + mark_text = string(runes[hint_runes:]) } ans := hint_style(hint) + text_style(mark_text) return fmt.Sprintf("\x1b]8;;mark:%d\a%s\x1b]8;;\a", m.Index, ans)