detached 883aaa9

refactor(daemon): clarify floating geometry boundaries

commit
883aaa92363de677cd1414bd616d952fe358821e
author
brice · · 2026-07-11 06:00 UTC
parent
9dd5126
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +10 -6
M internal/usecase/daemon/floating_render.go +4 -2
@@ -8,8 +8,10 @@ import (
88 )
99
1010 // floatingGeometry describes a popup's outer frame and terminal content area.
11-// Committed geometry is tab-content-relative and origin-zero; Bounds and Inner
12-// are both relative to tab content.
11+// Bounds and Inner always share the same coordinate space. Committed pane
12+// geometry is origin-zero and tab-content-relative; composeFloatingFrame
13+// translates both fields and returns/caches frame-absolute geometry. Callers
14+// must not translate a composed or cached value again.
1315 type floatingGeometry struct {
1416 Bounds domain.Rect
1517 Inner domain.Rect
@@ -8,8 +8,10 @@ import (
8 )8 )
9 9
10 // floatingGeometry describes a popup's outer frame and terminal content area.10 // floatingGeometry describes a popup's outer frame and terminal content area.
11-// Committed geometry is tab-content-relative and origin-zero; Bounds and Inner11+// Bounds and Inner always share the same coordinate space. Committed pane
12-// are both relative to tab content.12+// geometry is origin-zero and tab-content-relative; composeFloatingFrame
13+// translates both fields and returns/caches frame-absolute geometry. Callers
14+// must not translate a composed or cached value again.
13 type floatingGeometry struct {15 type floatingGeometry struct {
14 Bounds domain.Rect16 Bounds domain.Rect
15 Inner domain.Rect17 Inner domain.Rect
M internal/usecase/daemon/floating_render_test.go +6 -4
@@ -309,14 +309,15 @@ func BenchmarkComposeFloatingFrameCached(b *testing.B) {
309309 base := renderer.NewFrame(80, 24)
310310 content := domain.Rect{Y: 1, Width: 80, Height: 22}
311311 cfg := domain.FloatingConfig{Width: 80, Height: 80}
312+ desired := calculateContentFloatingGeometry(domain.Size{Cols: content.Width, Rows: content.Height}, cfg)
312313 cache := &composedFrameCache{}
313- composeFloatingFrame(base, nil, p, 1, content, calculateContentFloatingGeometry(domain.Size{Cols: content.Width, Rows: content.Height}, cfg), tabLayoutSnapshot{}, themeui.Theme{}, cache, false)
314+ composeFloatingFrame(base, nil, p, 1, content, desired, tabLayoutSnapshot{}, themeui.Theme{}, cache, false)
314315 p.screen.ClearDamage()
315316
316317 b.ReportAllocs()
317318 b.ResetTimer()
318319 for b.Loop() {
319- composeFloatingFrame(base, nil, p, 1, content, calculateContentFloatingGeometry(domain.Size{Cols: content.Width, Rows: content.Height}, cfg), tabLayoutSnapshot{}, themeui.Theme{}, cache, false)
320+ composeFloatingFrame(base, nil, p, 1, content, desired, tabLayoutSnapshot{}, themeui.Theme{}, cache, false)
320321 }
321322 }
322323
@@ -326,12 +327,13 @@ func TestComposeFloatingFrameCachedDoesNotAllocate(t *testing.T) {
326327 base := renderer.NewFrame(80, 24)
327328 content := domain.Rect{Y: 1, Width: 80, Height: 22}
328329 cfg := domain.FloatingConfig{Width: 80, Height: 80}
330+ desired := calculateContentFloatingGeometry(domain.Size{Cols: content.Width, Rows: content.Height}, cfg)
329331 cache := &composedFrameCache{}
330- composeFloatingFrame(base, nil, p, 1, content, calculateContentFloatingGeometry(domain.Size{Cols: content.Width, Rows: content.Height}, cfg), tabLayoutSnapshot{}, themeui.Theme{}, cache, false)
332+ composeFloatingFrame(base, nil, p, 1, content, desired, tabLayoutSnapshot{}, themeui.Theme{}, cache, false)
331333 p.screen.ClearDamage()
332334
333335 allocs := testing.AllocsPerRun(100, func() {
334- composeFloatingFrame(base, nil, p, 1, content, calculateContentFloatingGeometry(domain.Size{Cols: content.Width, Rows: content.Height}, cfg), tabLayoutSnapshot{}, themeui.Theme{}, cache, false)
336+ composeFloatingFrame(base, nil, p, 1, content, desired, tabLayoutSnapshot{}, themeui.Theme{}, cache, false)
335337 })
336338 require.Zero(t, allocs, "cached floating composition must not allocate without damage or title changes")
337339 }
@@ -309,14 +309,15 @@ func BenchmarkComposeFloatingFrameCached(b *testing.B) {
309 base := renderer.NewFrame(80, 24)309 base := renderer.NewFrame(80, 24)
310 content := domain.Rect{Y: 1, Width: 80, Height: 22}310 content := domain.Rect{Y: 1, Width: 80, Height: 22}
311 cfg := domain.FloatingConfig{Width: 80, Height: 80}311 cfg := domain.FloatingConfig{Width: 80, Height: 80}
312+ desired := calculateContentFloatingGeometry(domain.Size{Cols: content.Width, Rows: content.Height}, cfg)
312 cache := &composedFrameCache{}313 cache := &composedFrameCache{}
313- composeFloatingFrame(base, nil, p, 1, content, calculateContentFloatingGeometry(domain.Size{Cols: content.Width, Rows: content.Height}, cfg), tabLayoutSnapshot{}, themeui.Theme{}, cache, false)314+ composeFloatingFrame(base, nil, p, 1, content, desired, tabLayoutSnapshot{}, themeui.Theme{}, cache, false)
314 p.screen.ClearDamage()315 p.screen.ClearDamage()
315 316
316 b.ReportAllocs()317 b.ReportAllocs()
317 b.ResetTimer()318 b.ResetTimer()
318 for b.Loop() {319 for b.Loop() {
319- composeFloatingFrame(base, nil, p, 1, content, calculateContentFloatingGeometry(domain.Size{Cols: content.Width, Rows: content.Height}, cfg), tabLayoutSnapshot{}, themeui.Theme{}, cache, false)320+ composeFloatingFrame(base, nil, p, 1, content, desired, tabLayoutSnapshot{}, themeui.Theme{}, cache, false)
320 }321 }
321 }322 }
322 323
@@ -326,12 +327,13 @@ func TestComposeFloatingFrameCachedDoesNotAllocate(t *testing.T) {
326 base := renderer.NewFrame(80, 24)327 base := renderer.NewFrame(80, 24)
327 content := domain.Rect{Y: 1, Width: 80, Height: 22}328 content := domain.Rect{Y: 1, Width: 80, Height: 22}
328 cfg := domain.FloatingConfig{Width: 80, Height: 80}329 cfg := domain.FloatingConfig{Width: 80, Height: 80}
330+ desired := calculateContentFloatingGeometry(domain.Size{Cols: content.Width, Rows: content.Height}, cfg)
329 cache := &composedFrameCache{}331 cache := &composedFrameCache{}
330- composeFloatingFrame(base, nil, p, 1, content, calculateContentFloatingGeometry(domain.Size{Cols: content.Width, Rows: content.Height}, cfg), tabLayoutSnapshot{}, themeui.Theme{}, cache, false)332+ composeFloatingFrame(base, nil, p, 1, content, desired, tabLayoutSnapshot{}, themeui.Theme{}, cache, false)
331 p.screen.ClearDamage()333 p.screen.ClearDamage()
332 334
333 allocs := testing.AllocsPerRun(100, func() {335 allocs := testing.AllocsPerRun(100, func() {
334- composeFloatingFrame(base, nil, p, 1, content, calculateContentFloatingGeometry(domain.Size{Cols: content.Width, Rows: content.Height}, cfg), tabLayoutSnapshot{}, themeui.Theme{}, cache, false)336+ composeFloatingFrame(base, nil, p, 1, content, desired, tabLayoutSnapshot{}, themeui.Theme{}, cache, false)
335 })337 })
336 require.Zero(t, allocs, "cached floating composition must not allocate without damage or title changes")338 require.Zero(t, allocs, "cached floating composition must not allocate without damage or title changes")
337 }339 }

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 12

Tags 11

↑↓ moveenter openesc close