1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
package tui
import (
"fmt"
"strings"
common "ior/internal/tui/common"
"charm.land/bubbles/v2/key"
"charm.land/lipgloss/v2"
)
func renderHelpOverlay(width, height int, groups [][]key.Binding) string {
if width <= 0 {
width = 80
}
if height <= 0 {
height = 24
}
lines := []string{"Help"}
for _, group := range groups {
parts := make([]string, 0, len(group))
for _, binding := range group {
h := binding.Help()
parts = append(parts, fmt.Sprintf("%s %s", h.Key, h.Desc))
}
lines = append(lines, strings.Join(parts, " • "))
}
lines = append(lines, "", "Esc/?/q close")
boxWidth := width - 6
if boxWidth < 72 {
boxWidth = 72
}
box := common.PanelStyle.Copy().
Width(boxWidth).
Render(strings.Join(lines, "\n"))
return lipgloss.Place(width, height, lipgloss.Center, lipgloss.Center, box)
}
type helpSection struct {
title string
lines []string
}
func (m Model) helpSections() []helpSection {
globalLines := []string{
"H help esc/? close help q quit",
"f filter p pid picker t tid picker o probes R parquet rec",
}
if help := m.keys.Export.Help(); help.Key != "" || help.Desc != "" {
globalLines[1] += " e stream export"
}
return []helpSection{
{
title: "Global",
lines: globalLines,
},
{
title: "Dashboard Tabs",
lines: []string{
"tab/shift+tab tabs 1..7 jump tab r reset baseline R parquet rec",
"sys/files/proc/stream tables: arrows or hjkl move pgup/pgdown page g/G top/bottom",
"sys/files/proc tables: s sort S reverse sort",
"sys/proc: v bubbles b metric events/bytes",
"files: d dirs toggle v bubbles (dirs only) b metric",
"flame: arrows/hjkl nav enter/click zoom click ancestor undo u/bs/esc undo o order",
"flame: / filter n/N match next/prev space pause b metric",
"stream: space pause enter push filter esc/F undo /? n/N search",
"stream: x/X export E open",
},
},
{
title: "PID/TID Picker",
lines: []string{
"enter select r refresh esc/q back",
},
},
}
}
func renderGlobalHelpOverlay(width, height int, sections []helpSection) string {
if width <= 0 {
width = 80
}
if height <= 0 {
height = 24
}
boxWidth := width - 4
if boxWidth > 100 {
boxWidth = 100
}
if boxWidth < 74 {
boxWidth = 74
}
contentWidth := boxWidth - 4
if contentWidth < 20 {
contentWidth = boxWidth
}
lines := make([]string, 0, 24)
lines = append(lines, "Help")
for _, section := range sections {
lines = append(lines, "")
lines = append(lines, section.title)
for _, line := range section.lines {
lines = append(lines, " "+truncateHelpLine(line, contentWidth-2))
}
}
lines = append(lines, "", "Esc/q close")
maxLines := height - 4
if maxLines < 6 {
maxLines = 6
}
if len(lines) > maxLines {
lines = lines[:maxLines-1]
lines = append(lines, truncateHelpLine("... (resize for full help)", contentWidth))
}
box := common.PanelStyle.Copy().Width(boxWidth).Render(strings.Join(lines, "\n"))
return lipgloss.Place(width, height, lipgloss.Center, lipgloss.Center, box)
}
func truncateHelpLine(s string, width int) string {
if width <= 0 {
return ""
}
if lipgloss.Width(s) <= width {
return s
}
if width == 1 {
return "…"
}
r := []rune(s)
if len(r) >= width {
return string(r[:width-1]) + "…"
}
return s
}
|