fix(tui): F-006 flatten multi-line paste in input render
A multi-line paste put raw newlines into the single-line input slot, overflowing the fixed-height box and leaving a smeared/stale region. flattenForDisplay collapses newlines (↵) and tabs for rendering only; the inputBuffer keeps real characters for submission.
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// F-006: a multi-line paste must collapse to a single display line so the fixed-height
|
||||||
|
// input box can't overflow and leave a smeared/stale region.
|
||||||
|
func TestFlattenForDisplay(t *testing.T) {
|
||||||
|
cases := map[string]string{
|
||||||
|
"plain text": "plain text",
|
||||||
|
"line one\nline two": "line one↵ line two",
|
||||||
|
"crlf\r\nhere": "crlf↵ here",
|
||||||
|
"tab\tseparated": "tab separated",
|
||||||
|
"a\nb\nc": "a↵ b↵ c",
|
||||||
|
}
|
||||||
|
for in, want := range cases {
|
||||||
|
if got := flattenForDisplay(in); got != want {
|
||||||
|
t.Errorf("flattenForDisplay(%q) = %q, want %q", in, got, want)
|
||||||
|
}
|
||||||
|
if strings.ContainsAny(flattenForDisplay(in), "\n\r\t") {
|
||||||
|
t.Errorf("flattenForDisplay(%q) still contains a control char", in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -206,6 +206,14 @@ func (m Model) renderMain(h int) string {
|
|||||||
|
|
||||||
// --- input bar ---
|
// --- input bar ---
|
||||||
|
|
||||||
|
// flattenForDisplay collapses control characters (newlines from a multi-line paste, tabs)
|
||||||
|
// to a single visible line so the fixed-height input box can't overflow and smear stale cells
|
||||||
|
// (F-006). The underlying inputBuffer keeps its real characters for submission.
|
||||||
|
func flattenForDisplay(s string) string {
|
||||||
|
repl := strings.NewReplacer("\r\n", "↵ ", "\r", "↵ ", "\n", "↵ ", "\t", " ")
|
||||||
|
return repl.Replace(s)
|
||||||
|
}
|
||||||
|
|
||||||
func (m Model) renderInput() string {
|
func (m Model) renderInput() string {
|
||||||
t := m.theme
|
t := m.theme
|
||||||
|
|
||||||
@@ -231,7 +239,7 @@ func (m Model) renderInput() string {
|
|||||||
case m.inputBuffer == "":
|
case m.inputBuffer == "":
|
||||||
line = caret + t.span(placeholder, t.P.Faint)
|
line = caret + t.span(placeholder, t.P.Faint)
|
||||||
default:
|
default:
|
||||||
line = t.span(m.inputBuffer, t.P.FgStrong) + caret
|
line = t.span(flattenForDisplay(m.inputBuffer), t.P.FgStrong) + caret
|
||||||
}
|
}
|
||||||
|
|
||||||
// status sub-line: <session/none> · <uuid> · <mode>
|
// status sub-line: <session/none> · <uuid> · <mode>
|
||||||
|
|||||||
Reference in New Issue
Block a user