diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 9a34086..8a8b137 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -633,17 +633,17 @@ func TestToolbarSearch(t *testing.T) { t.Skipf("butuh >= 2 kontainer (err=%v, n=%d)", err, len(items)) } - // Target: prefix nama yang tidak termuat di nama kontainer sebelumnya — - // pencarian melompat ke kecocokan pertama dari atas. + // Target: prefix nama yang tidak termuat di nama kontainer mana pun — + // supaya lompatan kursor tidak ambigu (kecocokan pertama = target). target := "" - for i, it := range items { + for _, it := range items { q := it.Name if len(q) > 6 { q = q[:6] } unique := true - for k := 0; k < i; k++ { - if strings.Contains(strings.ToLower(items[k].Name), strings.ToLower(q)) { + for _, other := range items { + if other.Name != it.Name && strings.Contains(strings.ToLower(other.Name), strings.ToLower(q)) { unique = false break } @@ -661,6 +661,7 @@ func TestToolbarSearch(t *testing.T) { u.waitContains("beranda", 5*time.Second) u.send("2") u.waitContains("[1-5]", 3*time.Second) + u.send("j") // kursor pindah dari item pertama → lompatan cari terlihat if !u.clickText("Cari") { t.Fatal("pill Cari tidak ditemukan") } @@ -697,6 +698,7 @@ func TestTabActionColumn(t *testing.T) { u.waitContains("beranda", 5*time.Second) u.send("2") u.waitContains("[1-5]", 3*time.Second) + u.waitContains("Statistik", 5*time.Second) // detail kontainer terpilih tampil u.waitContains("Aksi Tab", 3*time.Second) u.waitContains("ikut ekor", 3*time.Second) if !u.clickText("[S]") { diff --git a/internal/app/app.go b/internal/app/app.go index 1cac767..6975886 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -901,12 +901,21 @@ func panel(title, meta string, lines []string, w, h int, focused bool) string { bc = th.Accent } border := lipgloss.NewStyle().Foreground(lipgloss.Color(string(bc))) - titleS := text(title) - left := border.Render("╭─ ") + titleS + // Judul/meta dipangkas agar baris border TIDAK pernah melebihi lebar panel + // (nama kontainer panjang dulu mendorong kolom kanan keluar layar). + avail := w - 6 + if meta != "" { + avail = w - 8 + } + metaW := 0 + if meta != "" && avail > 4 { + metaW = min(len([]rune(meta)), max(avail/3, 2)) + } + left := border.Render("╭─ ") + text(truncate(title, max(avail-metaW, 1))) right := border.Render("─╮") if meta != "" { - right = " " + dim(meta) + border.Render(" ─╮") + right = " " + dim(truncate(meta, metaW)) + border.Render(" ─╮") } gap := w - lipgloss.Width(left) - lipgloss.Width(right) if gap < 1 { @@ -914,9 +923,7 @@ func panel(title, meta string, lines []string, w, h int, focused bool) string { } var b strings.Builder - b.WriteString(left) - b.WriteString(border.Render(strings.Repeat("─", gap))) - b.WriteString(right) + b.WriteString(ansiTruncate(left+border.Render(strings.Repeat("─", gap))+right, w)) content := lipgloss.NewStyle().Width(w - 4) for i := 0; i < h-2; i++ { @@ -984,12 +991,7 @@ func (m Model) footerPlain() string { if m.animating { anim = fmt.Sprintf("%d", m.fps) } - full := i18n.Tf("footer.status", hints, anim, m.sound.Status()) - if lipgloss.Width(full) > m.width { - // Hint aksi lebih penting daripada status diagnostik: buang status dulu. - full = truncate(hints, m.width) - } - return full + return truncate(i18n.Tf("footer.status", hints, anim, m.sound.Status()), m.width) } func (m Model) footerLine() string { return dim(m.footerPlain()) } diff --git a/internal/app/layout_test.go b/internal/app/layout_test.go index bfb6a7d..aadf629 100644 --- a/internal/app/layout_test.go +++ b/internal/app/layout_test.go @@ -47,6 +47,22 @@ func TestToolbarPlain(t *testing.T) { } } +// TestPanelTitleFits — judul/meta panjang tidak boleh melebihi lebar panel: +// nama kontainer/stack panjang pernah mendorong kolom kanan keluar layar. +func TestPanelTitleFits(t *testing.T) { + long := strings.Repeat("GITEA-ACTIONS-TASK-", 8) + for _, w := range []int{6, 10, 40, 100} { + for _, meta := range []string{"", "running", long} { + out := panel(long, meta, []string{"isi"}, w, 4, false) + for _, line := range strings.Split(out, "\n") { + if got := lipgloss.Width(line); got > w { + t.Fatalf("w=%d meta=%q: baris lebar %d > %d: %q", w, meta, got, w, line) + } + } + } + } +} + // TestSearchJump — pencarian melompat ke item pertama yang cocok (daftar tetap // utuh), dan nama item mengikuti seksi aktif. func TestSearchJump(t *testing.T) {