fix(cli): repaint wide-character input on Windows

This commit is contained in:
GTC2080 committed 2026-07-01 20:15:05 +10:00
1 parent ebd73457da
commit c146456d8a
2 files changed
+45

No files matched your search

+17
View File
@@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"time"
@@ -1428,6 +1429,7 @@ func (m chatTUI) update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}
beforeInput := m.input.Value()
var ic tea.Cmd
m.input, ic = m.input.Update(msg)
cmds = append(cmds, ic)
@@ -1436,10 +1438,25 @@ func (m chatTUI) update(msg tea.Msg) (tea.Model, tea.Cmd) {
if _, ok := msg.(tea.KeyPressMsg); ok {
m.updateCompletion()
}
if shouldClearWideInputChange(beforeInput, m.input.Value()) {
cmds = append(cmds, tea.ClearScreen)
}
return m, finalize(m, cmds)
}
var clearWideInputChanges = runtime.GOOS == "windows"
func shouldClearWideInputChange(before, after string) bool {
return clearWideInputChanges &&
before != after &&
(hasWideInputCells(before) || hasWideInputCells(after))
}
func hasWideInputCells(s string) bool {
return s != "" && visibleWidth(s) != utf8.RuneCountInString(s)
}
// finalize drains the committed-line queue and batches the turn's commands. In
// the default alt-screen path the queue is already mirrored in m.transcript. In
// Termux finalized lines are also emitted into the terminal's native scrollback.
+28
View File
@@ -1914,6 +1914,34 @@ func TestSessionSwitchSuppressesOneClearScreen(t *testing.T) {
}
}
func TestWideInputChangeRequestsClearScreen(t *testing.T) {
prev := clearWideInputChanges
clearWideInputChanges = true
defer func() { clearWideInputChanges = prev }()
m := newTestChatTUI()
m.input.SetValue("天安a")
m.input.SetCursorColumn(len([]rune("天安a")))
next, cmd := m.update(tea.KeyPressMsg{Code: '门', Text: "门"})
got := next.(chatTUI)
if got.input.Value() != "天安a门" {
t.Fatalf("wide-char insert should preserve the textarea value, got %q", got.input.Value())
}
if cmd == nil {
t.Fatal("wide-char input changes should request a full redraw")
}
if shouldClearWideInputChange("ascii", "ascii!") {
t.Fatal("single-width ASCII input should not request the wide-input redraw")
}
if !shouldClearWideInputChange("门", "") {
t.Fatal("removing the last wide character should request a full redraw")
}
if !shouldClearWideInputChange("a门", "a") {
t.Fatal("removing a wide character from mixed input should request a full redraw")
}
}
func TestReplayActiveBranchClearsPlanModeAndMarksSessionSwitch(t *testing.T) {
m := newTestChatTUI()
m.ctrl = control.New(control.Options{})