Skip to content

fix(input): support CJK editing over SSH (width-aware backspace + cursor movement)#637

Open
FrostMiKu wants to merge 1 commit into
GoMudEngine:masterfrom
FrostMiKu:fix/ssh-cjk-input-editing
Open

fix(input): support CJK editing over SSH (width-aware backspace + cursor movement)#637
FrostMiKu wants to merge 1 commit into
GoMudEngine:masterfrom
FrostMiKu:fix/ssh-cjk-input-editing

Conversation

@FrostMiKu

Copy link
Copy Markdown

Problem

When connecting over SSH, editing input containing CJK (Chinese/Japanese/Korean) characters was broken:

  • Backspace left a half-glyph on screen. A wide character occupies 2 terminal columns, but the erase only cleared 1 column (\b \b), so deleting a Chinese character left the left half of the glyph stuck on the line.
  • The cursor couldn't be moved within the line. There was no cursor model in the input buffer — it was append-only / delete-from-end. Left/Right/Home/End/Delete keys were not handled at all, so mid-line editing was impossible.

Fix

  • Cursor model. Add a Cursor byte-offset to ClientInput. CleanserInputHandler now inserts at the cursor (instead of always appending) and deletes the rune before it; AnsiHandler moves it with the left/right/Home/End/Delete keys.
  • Width-aware display. Echo and erase use runewidth so wide characters occupy the correct number of columns. Backspace now emits the right number of backspaces + clear-to-EOL instead of the single-column \b \b. Cursor moves (left/right/Home/End) move by the display width of the character they cross.
  • History fix. Up/down history recall previously erased the screen by byte count (wrong for multibyte chars); it now erases by display width and keeps the cursor consistent.
  • Consistency. Anywhere the buffer is resized (history recall, signal shortcuts, login submit, prompt redraws in main.go) now keeps Cursor valid.

Scope / unchanged behavior

  • Only server-side-echo clients (SSH and raw telnet) get the new editing — they rely on the server for echo and line rendering.
  • Local-echo clients (Mudlet, web client) are untouched; they manage their own input line.
  • Masked (password) fields stay linear; no real characters are echoed and cursor editing is disabled there.

Verification

End-to-end over a real SSH connection (with a pty), capturing exactly what the server sends back to the terminal:

Action Server output Meaning
type 你好 raw UTF-8 bytes displays correctly
Backspace ESC[2D ESC[0K back 2 cols + clear → full-width char erased (was \b \b = 1 col)
Left ESC[2D back 2 cols (crosses a wide char)
Right ESC[2C forward 2 cols
Home ESC[4D back 4 cols (width of 你世) → start

Tests added:

  • Unit tests for width calc, key matching, mid-line insert, mid-buffer backspace.
  • Integration tests that register a capturing connection and assert on the actual terminal bytes emitted for width-aware backspace, mid-line insert, and left-arrow movement.
  • Existing TestCleanserInputHandler_UTF8Backspace / _NoBackspace updated to set Cursor (required by the new model).

make validate, go build ./..., and the inputhandlers / connections / term test suites pass. (internal/templates/TestRenderPanel_MaxWidth_AnsiTagsHandled fails on master independently of this change.)

🤖 Generated with Claude Code

…ent)

Over SSH/telnet, backspacing a wide (CJK) character left a half-glyph on
screen because the erase only cleared one column, and there was no cursor
model at all, so left/right/Home/End/Delete did nothing.

- Add a Cursor offset to ClientInput. CleanserInputHandler now inserts at
  the cursor (instead of always appending) and deletes the rune before it;
  AnsiHandler handles left/right/Home/End/Delete.
- Make echo and erase width-aware via runewidth so CJK characters occupy 2
  columns. Backspace emits the correct number of backspaces + clear-to-EOL
  instead of the old single-column "\b \b".
- Fix up/down history recall to erase by display width, not byte count.
- Keep local-echo clients (Mudlet, web) and masked (password) fields on
  their existing behavior; they manage their own input line.

Verified end-to-end over a real SSH connection (with pty): typing, backspace
and left/right/Home movement all emit correct width-aware sequences. Added
unit + integration tests that assert on the actual terminal bytes emitted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@FrostMiKu FrostMiKu requested a review from Volte6 as a code owner July 9, 2026 22:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant