diff --git a/CHANGELOG.md b/CHANGELOG.md index 7284dd2..c8f4028 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,4 +26,5 @@ - Start the shared `tui` in full detail mode like `gitcrawl`, while keeping `d` as the compact/full detail toggle. - Trim redundant chat member columns so message panes prioritize time, age, author, and title instead of repeating the selected channel/kind on every row. - Default single-channel chat archives to a people/group view so the left pane stays useful for Discord and Slack data. +- Match `gitcrawl` TUI resize breakpoints: 140+ columns use three panes, 100-139 uses top split plus full-width detail, and narrow terminals stack. - Rename the public package nouns to `config`, `store`, `snapshot`, `mirror`, `state`, `output`, `tui`, and `cache`. diff --git a/tui/tui.go b/tui/tui.go index c0bd1c8..39fc7e4 100644 --- a/tui/tui.go +++ b/tui/tui.go @@ -2440,12 +2440,12 @@ func (m model) layout() archiveLayout { if height <= 0 { height = 24 } - bodyH := maxInt(1, height-3) - if width >= 120 { + bodyH := maxInt(8, height-3) + if width >= 140 { if m.layoutMode == layoutModeRightStack { rowsW := maxInt(56, width*44/100) rightW := width - rowsW - contextH := clampInt(maxInt(3, bodyH*42/100), 1, maxInt(1, bodyH-1)) + contextH := clampInt(maxInt(8, bodyH*42/100), 1, maxInt(1, bodyH-1)) return archiveLayout{ rows: rect{x: 0, y: 1, w: rowsW, h: bodyH}, context: rect{x: rowsW, y: 1, w: rightW, h: contextH}, @@ -2453,26 +2453,18 @@ func (m model) layout() archiveLayout { mode: string(layoutModeRightStack), } } - rowsW := maxInt(42, width*34/100) - contextW := maxInt(38, width*30/100) - detailW := width - rowsW - contextW - if detailW < 40 { - deficit := 40 - detailW - shrinkRows := minInt(deficit, maxInt(0, rowsW-42)) - rowsW -= shrinkRows - deficit -= shrinkRows - shrinkContext := minInt(deficit, maxInt(0, contextW-38)) - contextW -= shrinkContext - } + rowsW := maxInt(48, width*34/100) + contextW := maxInt(40, width*30/100) + detailW := maxInt(42, width-rowsW-contextW) return archiveLayout{ rows: rect{x: 0, y: 1, w: rowsW, h: bodyH}, context: rect{x: rowsW, y: 1, w: contextW, h: bodyH}, - detail: rect{x: rowsW + contextW, y: 1, w: width - rowsW - contextW, h: bodyH}, + detail: rect{x: rowsW + contextW, y: 1, w: detailW, h: bodyH}, mode: string(layoutModeColumns), } } if width >= 100 { - topH := clampInt(maxInt(4, bodyH/2), 1, maxInt(1, bodyH-1)) + topH := clampInt(maxInt(8, bodyH/2), 1, maxInt(1, bodyH-1)) rowsW := width / 2 return archiveLayout{ rows: rect{x: 0, y: 1, w: rowsW, h: topH}, @@ -2482,9 +2474,9 @@ func (m model) layout() archiveLayout { mode: "split", } } - rowsH := clampInt(maxInt(3, bodyH*36/100), 1, maxInt(1, bodyH-2)) - contextH := clampInt(maxInt(2, bodyH*28/100), 1, maxInt(1, bodyH-rowsH-1)) - detailH := maxInt(1, bodyH-rowsH-contextH) + rowsH := clampInt(maxInt(7, bodyH*36/100), 1, maxInt(1, bodyH-2)) + contextH := clampInt(maxInt(6, bodyH*28/100), 1, maxInt(1, bodyH-rowsH-1)) + detailH := maxInt(6, bodyH-rowsH-contextH) return archiveLayout{ rows: rect{x: 0, y: 1, w: width, h: rowsH}, context: rect{x: 0, y: 1 + rowsH, w: width, h: contextH}, diff --git a/tui/tui_test.go b/tui/tui_test.go index 6c963a9..1e38d4b 100644 --- a/tui/tui_test.go +++ b/tui/tui_test.go @@ -679,7 +679,7 @@ func TestFocusedDetailPaneScrollsIndependently(t *testing.T) { }}, }) m.width = 80 - m.height = 24 + m.height = 30 m.focus = focusDetail m.scrollFocused(1) if m.selected != 0 { @@ -1428,7 +1428,7 @@ func TestLayoutToggleUsesRightStackMode(t *testing.T) { } } -func TestWideTmuxPanesPreferThreeColumns(t *testing.T) { +func TestMediumTmuxPanesUseGitcrawlSplitLayout(t *testing.T) { m := newModel(Options{ Title: "archive", Items: []Item{{Title: "alpha", Tags: []string{"page"}}}, @@ -1436,17 +1436,17 @@ func TestWideTmuxPanesPreferThreeColumns(t *testing.T) { m.width = 122 m.height = 34 layout := m.layout() - if layout.mode != string(layoutModeColumns) || layout.stacked { - t.Fatalf("layout = %#v, want three columns", layout) + if layout.mode != "split" || !layout.stacked { + t.Fatalf("layout = %#v, want gitcrawl-style split", layout) } - if layout.rows.h != layout.context.h || layout.context.h != layout.detail.h { - t.Fatalf("panes should share height in column mode: %#v", layout) + if layout.rows.y != layout.context.y || layout.detail.y <= layout.rows.y { + t.Fatalf("split panes should put rows/context above detail: %#v", layout) } - if paneContentWidth(layout.context.w) < 34 { - t.Fatalf("context pane too narrow for useful columns: %#v", layout) + if layout.rows.w+layout.context.w != 122 { + t.Fatalf("split top panes should fill terminal width: %#v", layout) } - if paneContentWidth(layout.rows.w) < 36 { - t.Fatalf("rows pane too narrow for date/age columns: %#v", layout) + if paneContentWidth(layout.context.w) < 52 || paneContentWidth(layout.rows.w) < 52 { + t.Fatalf("split panes too narrow for useful columns: %#v", layout) } }