From 47596158357343a66ad60963d50a3a2d606cc839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Mon, 24 Nov 2025 16:16:59 +0100 Subject: [PATCH] image/tree: Allow image names to overflow instead of truncating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Users were experiencing poor UX when image names were truncated in the table output. Instead of cutting off long image names with ellipsis, the names now wrap to the next line to ensure full visibility. Signed-off-by: Paweł Gronowski --- cli/command/image/tree.go | 73 ++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/cli/command/image/tree.go b/cli/command/image/tree.go index db93bff0f..ff0c54a4c 100644 --- a/cli/command/image/tree.go +++ b/cli/command/image/tree.go @@ -258,9 +258,10 @@ func printImageTree(outs command.Streams, view treeView) { possibleChips := getPossibleChips(view) columns := []imgColumn{ { - Title: "Image", - Align: alignLeft, - Width: 0, + Title: "Image", + Align: alignLeft, + Width: 0, + NoEllipsis: true, }, { Title: "ID", @@ -436,13 +437,27 @@ func printNames(out tui.Output, headers []imgColumn, img topImage, color, untagg } for nameIdx, name := range img.Names { - // Don't limit first names to the column width because only the last - // name will be printed alongside other columns. - if nameIdx < len(img.Names)-1 { - _, fullWidth := out.GetTtySize() - _, _ = fmt.Fprintln(out, color.Apply(tui.Ellipsis(name, int(fullWidth)))) - } else { - _, _ = fmt.Fprint(out, headers[0].Print(color, name)) + nameWidth := tui.Width(name) + lastName := nameIdx == len(img.Names)-1 + multiLine := nameWidth > headers[0].Width + + _, _ = fmt.Fprint(out, headers[0].Print(color, name)) + + // Print each name on its own line, including the last, + // unless the last name fits into the column. + // + // IMAGE ID ... + // anImage 171e65262c80 ... + // firstName + // lastNameIsALongOne + // eade5be814e8 ... + // anotherLongName + // bb747ca923a5 ... + if !lastName || multiLine { + _, _ = fmt.Fprintln(out) + } + if multiLine && lastName { + _, _ = fmt.Fprint(out, strings.Repeat(" ", headers[0].Width)) } } } @@ -462,6 +477,7 @@ type imgColumn struct { DetailsValue func(*imageDetails) string Color *aec.ANSI + NoEllipsis bool } func (h imgColumn) Print(clr aec.ANSI, s string) string { @@ -478,12 +494,16 @@ func (h imgColumn) Print(clr aec.ANSI, s string) string { func (h imgColumn) PrintC(clr aec.ANSI, s string) string { ln := tui.Width(s) - if ln > h.Width { - return clr.Apply(tui.Ellipsis(s, h.Width)) - } - fill := h.Width - ln + if fill < 0 { + if h.NoEllipsis { + fill = 0 + } else { + return clr.Apply(tui.Ellipsis(s, h.Width)) + } + } + l := fill / 2 r := fill - l @@ -492,20 +512,33 @@ func (h imgColumn) PrintC(clr aec.ANSI, s string) string { func (h imgColumn) PrintL(clr aec.ANSI, s string) string { ln := tui.Width(s) - if ln > h.Width { - return clr.Apply(tui.Ellipsis(s, h.Width)) + + fill := h.Width - ln + + if fill < 0 { + if h.NoEllipsis { + fill = 0 + } else { + return clr.Apply(tui.Ellipsis(s, h.Width)) + } } - return clr.Apply(s) + strings.Repeat(" ", h.Width-ln) + return clr.Apply(s) + strings.Repeat(" ", fill) } func (h imgColumn) PrintR(clr aec.ANSI, s string) string { ln := tui.Width(s) - if ln > h.Width { - return clr.Apply(tui.Ellipsis(s, h.Width)) + fill := h.Width - ln + + if fill < 0 { + if h.NoEllipsis { + fill = 0 + } else { + return clr.Apply(tui.Ellipsis(s, h.Width)) + } } - return strings.Repeat(" ", h.Width-ln) + clr.Apply(s) + return strings.Repeat(" ", fill) + clr.Apply(s) } // widestFirstColumnValue calculates the width needed to fully display the image names and platforms.