Diff
1diff --git a/internal/ui/app.go b/internal/ui/app.go
2index a84287fef0561eb69eca2d0097b7c1b7b549a8b4..5d31f28a64555b0b49695216663c1283f91f49a5 100644
3--- a/internal/ui/app.go
4+++ b/internal/ui/app.go
5@@ -281,25 +281,39 @@ func keyedBlock(width int, title string, pairs ...[2]string) []string {
6 return lines
7 }
8
9+// maxLineLength caps wrapped log lines so narration does not stretch across a
10+// wide terminal.
11+const maxLineLength = 80
12+
13+// wrapWidth is the width world output wraps at: the terminal width, capped at
14+// maxLineLength. A zero or unknown width falls back to the cap.
15+func wrapWidth(width int) int {
16+ if width <= 0 || width > maxLineLength {
17+ return maxLineLength
18+ }
19+ return width
20+}
21+
22 // entryLines renders one display-log turn into log lines. Speech fields are
23 // revealed independently (romaji, then english); the judge line follows the turn
24 // that produced it.
25 func entryLines(e game.DisplayEntry, width int) []string {
26+ w := wrapWidth(width)
27 var lines []string
28 if e.Action != "" {
29 lines = append(lines, actionStyle.Render("> "+sanitizeRomajiOnly(e.Action)))
30 }
31 if e.Desc != "" {
32- lines = append(lines, narrationStyle.Render(sanitizeRomajiOnly(e.Desc)))
33+ lines = append(lines, narrationStyle.Width(w).Render(sanitizeRomajiOnly(e.Desc)))
34 }
35 if e.HasSpeech {
36 if e.ShowRomaji {
37- lines = append(lines, npcRomajiStyle.Render(" » "+sanitizeRomajiOnly(valueOrNone(e.Romaji))))
38+ lines = append(lines, npcRomajiStyle.Width(w).Render(" » "+sanitizeRomajiOnly(valueOrNone(e.Romaji))))
39 } else {
40 lines = append(lines, hintStyle.Render(" » [F3] romaji hidden"))
41 }
42 if e.ShowEnglish {
43- lines = append(lines, npcEngStyle.Render(" "+sanitizeRomajiOnly(valueOrNone(e.English))))
44+ lines = append(lines, npcEngStyle.Width(w).Render(" "+sanitizeRomajiOnly(valueOrNone(e.English))))
45 } else {
46 lines = append(lines, hintStyle.Render(" [F4] english hidden"))
47 }