v0.11.1 ab30a57

dialog_text_test.go RawHistoryFolder

1package modes
2
3import (
4	"strings"
5	"testing"
6
7	"github.com/mattn/go-runewidth"
8
9	"github.com/bnema/zut/packages/agent/subagents"
10	"github.com/bnema/zut/packages/tui"
11)
12
13func assertRowsFitWidth(t *testing.T, rows []string, width int) {
14	t.Helper()
15	for i, row := range rows {
16		if got := runewidth.StringWidth(stripANSIBytes(row)); got > width {
17			t.Errorf("row %d width = %d, want <= %d: %q", i, got, width, stripANSIBytes(row))
18		}
19	}
20}
21
22func TestBtwDialogWrapsAssistantAndKeepsCursorAligned(t *testing.T) {
23	const width = 80
24	response := "This response is deliberately long enough to require several wrapped display rows without losing any words."
25	d := &btwDialog{
26		active:    true,
27		lineInput: true,
28		turns: []btwTurn{{
29			User:      "check wrapping",
30			Assistant: response,
31		}},
32		editor: tui.NewEditor(""),
33	}
34
35	rows := d.Render(tui.Theme{}, width)
36	assertRowsFitWidth(t, rows, width)
37	plain := strings.Join(rows, "\n")
38	for _, word := range strings.Fields(response) {
39		if !strings.Contains(plain, word) {
40			t.Fatalf("wrapped response lost word %q:\n%s", word, plain)
41		}
42	}
43
44	cursorRow, _ := d.CursorPos(width)
45	if want := len(rows) - 3; cursorRow != want {
46		t.Fatalf("cursor row = %d, want editor row %d", cursorRow, want)
47	}
48}
49
50func TestSupervisorTranscriptWrapsAssistantAndDiagnosticRows(t *testing.T) {
51	const width = 80
52	long := strings.Repeat("long response text ", 8)
53	rows := renderSupervisorTranscriptBlocks([]string{
54		long,
55		"stderr: " + long,
56		"error: " + long,
57	}, tui.Theme{}, width, false)
58
59	assertRowsFitWidth(t, rows, width)
60	plain := strings.Join(rows, "\n")
61	for _, want := range []string{"long", "stderr", "✖"} {
62		if !strings.Contains(plain, want) {
63			t.Fatalf("wrapped transcript missing %q:\n%s", want, plain)
64		}
65	}
66}
67
68func TestSubagentsDialogViewingWrapsSpawnedAgentResponse(t *testing.T) {
69	const width = 80
70	response := strings.Repeat("spawned agent response ", 6)
71	rows := []subagents.AgentSnapshot{{
72		ID:     "agent-1",
73		Status: subagents.StatusDone,
74		Lines:  []string{response},
75	}}
76	d := newSubagentsDialog()
77	d.Open(staticSnapshots(rows...), nil, nil, nil, nil, nil, "")
78	_ = d.Render(tui.Theme{}, width)
79	d.HandleKey(tui.Key{Kind: tui.KeyEnter})
80
81	rendered := d.Render(tui.Theme{}, width)
82	assertRowsFitWidth(t, rendered, width)
83	if !strings.Contains(strings.Join(rendered, "\n"), "response") {
84		t.Fatal("spawned agent response missing from transcript")
85	}
86}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close