fix(tui): wrap the one-time setup URL so narrow terminals can't truncate it

The setup URL carries a 43-char token and overruns 80 columns; the TUI
renderer clipped it. Break it at the query '=' boundary (token on its own
line) with a shared wrapDisplayURL helper used by both the Owner wizard and
the mc-bind wizard; unit test pins the no-loss concatenation.
This commit is contained in:
Lemon-miaow committed 2026-09-22 20:28:01 +08:00
1 parent a415246adc
commit abce381faa
4 files changed
+63 -2

No files matched your search

+5 -1
View File
@@ -207,7 +207,11 @@ func (m *mcBindModel) doneView() string {
if box.Len() > 0 {
box.WriteString("\n")
}
box.WriteString(tuiLabel.Render("setup URL ") + "\n" + tuiPassword.Render(m.setupTokenURL) + "\n\n")
box.WriteString(tuiLabel.Render("setup URL ") + "\n")
for _, line := range wrapDisplayURL(m.setupTokenURL, 70) {
box.WriteString(tuiPassword.Render(line) + "\n")
}
box.WriteString("\n")
box.WriteString(tuiWarn.Render("Open this URL to complete passwordless login setup.\nIt is shown only once."))
}
if m.auditWarning != "" {
+5 -1
View File
@@ -420,7 +420,11 @@ func (m *ownerModel) doneView() string {
var box strings.Builder
box.WriteString(tuiLabel.Render("username ") + m.username + "\n")
if m.setupTokenURL != "" {
box.WriteString("\n" + tuiLabel.Render("setup URL ") + "\n" + tuiPassword.Render(m.setupTokenURL) + "\n\n")
box.WriteString("\n" + tuiLabel.Render("setup URL ") + "\n")
for _, line := range wrapDisplayURL(m.setupTokenURL, 70) {
box.WriteString(tuiPassword.Render(line) + "\n")
}
box.WriteString("\n")
box.WriteString(tuiWarn.Render("Open this URL to complete passwordless login setup. It is shown only once."))
}
if m.auditWarning != "" {
+27
View File
@@ -29,6 +29,33 @@ func tuiSeparator() string {
return tuiHint.Render(strings.Repeat("─", 70))
}
// wrapDisplayURL breaks a long URL into lines no wider than width so the TUI
// renderer never truncates it on a narrow terminal — the one-time setup URL
// carries a 43-char token and overruns 80 columns. It prefers breaking right
// after a '=' or '/' inside the window (the token then lands on its own line)
// and hard-wraps only when no boundary is available. Lines concatenate back to
// the original string.
func wrapDisplayURL(u string, width int) []string {
if width <= 0 {
width = 70
}
var lines []string
for len(u) > width {
cut := width
if i := strings.LastIndexByte(u[:width], '='); i >= 0 && i >= width/2 {
cut = i + 1
} else if i := strings.LastIndexByte(u[:width], '/'); i >= 0 && i >= width/2 {
cut = i + 1
}
lines = append(lines, u[:cut])
u = u[cut:]
}
if u != "" {
lines = append(lines, u)
}
return lines
}
// tuiStepRail renders a breadcrumb of wizard stages. Steps before `current`
// render as done, `current` is highlighted, and later steps are dimmed.
func tuiStepRail(steps []string, current int) string {
+26
View File
@@ -0,0 +1,26 @@
package main
import (
"strings"
"testing"
)
func TestWrapDisplayURL(t *testing.T) {
u := "https://op.console.example.net/setup?token=" + strings.Repeat("A", 43)
lines := wrapDisplayURL(u, 70)
if got := strings.Join(lines, ""); got != u {
t.Fatalf("concatenated lines = %q, want the original URL back", got)
}
for i, l := range lines {
if len(l) > 70 {
t.Errorf("line %d is %d cols wide: %q", i, len(l), l)
}
}
if len(lines) < 2 || !strings.HasSuffix(lines[0], "token=") {
t.Fatalf("want the first line to end at the 'token=' boundary, got %q", lines)
}
short := "https://a/b"
if got := wrapDisplayURL(short, 70); len(got) != 1 || got[0] != short {
t.Errorf("short URL should pass through unsplit, got %q", got)
}
}