v0.3.0 761f038

layout.go RawHistoryFolder

1package tui
2
3import "strings"
4
5const (
6	InputStylePlain = "plain"
7	InputStyleLines = "lines"
8	InputStyleBlock = "block"
9
10	StatusPositionAboveInput = "above_input"
11	StatusPositionBelowInput = "below_input"
12
13	WorkingPositionAboveInput = "above_input"
14	WorkingPositionBelowInput = "below_input"
15)
16
17func NormalizeInputStyle(v string) string {
18	switch strings.ToLower(strings.TrimSpace(v)) {
19	case InputStyleLines, "line":
20		return InputStyleLines
21	case InputStyleBlock, "bubble", "boxed", "box":
22		return InputStyleBlock
23	default:
24		return InputStylePlain
25	}
26}
27
28func NormalizeStatusPosition(v string) string {
29	switch strings.ToLower(strings.TrimSpace(v)) {
30	case StatusPositionBelowInput, "below", "bottom":
31		return StatusPositionBelowInput
32	default:
33		return StatusPositionAboveInput
34	}
35}
36
37func NormalizeWorkingPosition(v string) string {
38	switch strings.ToLower(strings.TrimSpace(v)) {
39	case WorkingPositionBelowInput, "below", "bottom":
40		return WorkingPositionBelowInput
41	default:
42		return WorkingPositionAboveInput
43	}
44}
45
46func InputLines(th Theme, lines []string, width int) []string {
47	if width < 1 {
48		width = 1
49	}
50	rule := th.FGColor(th.Muted, strings.Repeat("─", width))
51	out := make([]string, 0, len(lines)+2)
52	out = append(out, rule)
53	out = append(out, lines...)
54	out = append(out, rule)
55	return out
56}
57
58func InputBlock(th Theme, lines []string, width int) []string {
59	if width < 1 {
60		width = 1
61	}
62	bubbleW := width - 2
63	if bubbleW < 1 {
64		bubbleW = 1
65	}
66	bg := th.sgrBGColor(th.UserBubbleBG)
67	bar := bg + th.FGColor(th.Accent, "▌ ")
68	row := func(content string) string {
69		visible := visibleWidth(content)
70		if visible < bubbleW {
71			content += strings.Repeat(" ", bubbleW-visible)
72		}
73		body := bg + strings.ReplaceAll(content, reset, reset+bg) + reset
74		return bar + body
75	}
76	out := make([]string, 0, len(lines)+2)
77	out = append(out, row(""))
78	for _, line := range lines {
79		out = append(out, row(line))
80	}
81	out = append(out, row(""))
82	return out
83}
84
85// CursorColor returns OSC 12 to set the terminal cursor color. Unlike SGR
86// styling, the cursor is terminal-owned, so modal backdrops must set this
87// separately from their dimmed text rows.
88func CursorColor(color TerminalColor) string {
89	rgb, ok := rgbForTerminalColor(color)
90	if !ok {
91		return ""
92	}
93	return "\x1b]12;rgb:" + hexByte(rgb[0]) + "/" + hexByte(rgb[1]) + "/" + hexByte(rgb[2]) + "\x07"
94}
95
96func CursorColor256(index int) string { return CursorColor(Color256(index)) }
97
98func ResetCursorColor() string { return "\x1b]112\x07" }
99
100func CursorShapeBlock() string { return "\x1b[1 q" }
101
102func ResetCursorShape() string { return "\x1b[0 q" }
103
104func xterm256RGB(index int) (int, int, int) {
105	if index < 0 {
106		index = 0
107	}
108	if index > 255 {
109		index = 255
110	}
111	ansi := [16][3]int{
112		{0, 0, 0}, {128, 0, 0}, {0, 128, 0}, {128, 128, 0},
113		{0, 0, 128}, {128, 0, 128}, {0, 128, 128}, {192, 192, 192},
114		{128, 128, 128}, {255, 0, 0}, {0, 255, 0}, {255, 255, 0},
115		{0, 0, 255}, {255, 0, 255}, {0, 255, 255}, {255, 255, 255},
116	}
117	if index < 16 {
118		c := ansi[index]
119		return c[0], c[1], c[2]
120	}
121	if index < 232 {
122		idx := index - 16
123		levels := [6]int{0, 95, 135, 175, 215, 255}
124		return levels[idx/36], levels[(idx/6)%6], levels[idx%6]
125	}
126	gray := 8 + (index-232)*10
127	return gray, gray, gray
128}
129
130func hexByte(v int) string {
131	const digits = "0123456789abcdef"
132	if v < 0 {
133		v = 0
134	}
135	if v > 255 {
136		v = 255
137	}
138	return string([]byte{digits[v>>4], digits[v&0xf]})
139}

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