v0.1.0 47aaa0f

performance_test.go RawHistoryFolder

1package ui
2
3import (
4	"os"
5	"testing"
6
7	"github.com/bnema/nefergui/internal/css"
8
9	"github.com/bnema/nefergui/internal/text"
10)
11
12// demoFrame mirrors the product example's home view without opening a display.
13func demoFrame(f *Frame) {
14	root := f.Root(Class("app"), Class("light"), Class("comfortable"))
15	header := root.Header(Class("header"))
16	header.Heading("NeferGUI", Level(1), Class("logo"))
17	header.Button("New", Key("new-document"))
18	header.Text("Untitled.txt", Class("muted"))
19	header.Spacer(Class("stretch"))
20	header.Button("Dark mode", Key("dark-mode"), Class("theme-toggle"))
21	body := root.Row(Class("body"))
22	sidebar := body.Aside(Class("sidebar"))
23	sidebar.Text("DOCUMENTS", Class("eyebrow"))
24	menu := sidebar.Nav(Class("menu"))
25	menu.Button("Untitled.txt", Key("home"), Class("menu-item"), Class("active"))
26	menu.Button("Preferences", Key("settings"), Class("menu-item"))
27	sidebar.Spacer(Class("stretch"))
28	sidebar.Text("Local workspace", Class("sidebar-note"))
29	sidebar.Text("1 document · in memory", Class("hint"))
30	content := body.Main(Class("content"))
31	workspace := content.Row(Class("workspace"))
32	editor := workspace.Column(Class("editor-pane"))
33	tab := editor.Row(Class("pane-bar"))
34	tab.Text("Untitled.txt", Class("document-title"))
35	tab.Spacer(Class("stretch"))
36	tab.Text("Plain text", Class("hint"))
37	notes := "Project notes\n\nA native text workspace built with NeferGUI.\n\nSelect text, edit a paragraph, or paste from your clipboard.\nUse the properties pane to try radio buttons and the slider.\n\nEverything stays in memory for this session."
38	editor.Textarea("Document", &notes, Key("document"), Class("document-editor"), Placeholder("Start writing…"))
39	inspector := workspace.Aside(Class("inspector"))
40	inspector.Text("PROPERTIES", Class("eyebrow"))
41	inspector.Text("Author", Class("field-label"))
42	name := ""
43	inspector.Input("Author", &name, Key("name"), Placeholder("Your name"))
44	inspector.Button("Apply", Key("continue"), Disabled(true))
45	inspector.Separator()
46	inspector.Text("Interface density", Class("field-label"))
47	density := "comfortable"
48	inspector.Radio("Comfortable", "comfortable", &density, Key("comfortable"))
49	inspector.Radio("Compact", "compact", &density, Key("compact"))
50	inspector.Separator()
51	volume := 40.0
52	inspector.Slider("Volume 40 %", &volume, 0, 100, 5, Key("volume"))
53	inspector.Spacer(Class("stretch"))
54	inspector.Text("Changes stay in this session.", Class("hint"))
55	footer := root.Footer(Class("status-bar"))
56	footer.Text("Ready")
57	footer.Spacer(Class("stretch"))
58	footer.Text("UTF-8   •   Plain text", Class("hint"))
59}
60
61func demoRuntime(tb testing.TB) *runtime {
62	tb.Helper()
63	catalog, err := text.Load(text.DirectorySource("../../testdata/fonts"))
64	if err != nil {
65		tb.Fatal(err)
66	}
67	r := newRuntime()
68	stylesheet, err := os.ReadFile("../../examples/demo/app.css")
69	if err != nil {
70		tb.Fatal(err)
71	}
72	r.styles = css.Compile(css.UA(), css.Parse(string(stylesheet)))
73	r.setTextEngine(text.NewEngine(catalog))
74	return r
75}
76
77func BenchmarkDemoFrame(b *testing.B) {
78	r := demoRuntime(b)
79	if !r.Build(demoFrame) {
80		b.Fatal("initial build failed")
81	}
82	b.ReportAllocs()
83	b.ResetTimer()
84	for i := 0; i < b.N; i++ {
85		r.Redraw()
86		if !r.Build(demoFrame) {
87			b.Fatal("build failed")
88		}
89		if len(r.output.Display) == 0 {
90			b.Fatal("empty display list")
91		}
92	}
93}
94
95func TestDemoFrameAllocationBudget(t *testing.T) {
96	r := demoRuntime(t)
97	if !r.Build(demoFrame) {
98		t.Fatal("initial build failed")
99	}
100	allocs := testing.AllocsPerRun(10, func() {
101		r.Redraw()
102		if !r.Build(demoFrame) || len(r.output.Display) == 0 {
103			panic("demo frame failed")
104		}
105	})
106	const budget = 1100
107	if allocs > budget {
108		t.Fatalf("demo frame allocations: %.0f > %d", allocs, budget)
109	}
110}

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 12

↑↓ moveenter openesc close