From bba8783f1086b265a43fc2a3e980531bd83f42d6 Mon Sep 17 00:00:00 2001 From: vrubelroman Date: Mon, 27 Apr 2026 16:52:44 +0300 Subject: [PATCH] Fix directory preview layout to exactly match browser pane columns Root cause: renderPreviewPane() was stuffing directory entries into the viewport model via SetContent(), then rendering through renderPreviewContent() which wraps the viewport in an additional border+padding box. This reduced the effective content width by 4 characters compared to the browser pane's innerWidth (width-2), causing column misalignment where the date column would wrap to the next line. Fix: For directory previews, directly render entries using the same renderPaneRows() and renderColumnsHeader() functions as the browser pane, at the same innerWidth. A temporary BrowserPane is created with the directory entries, and renderPaneRows() is called with active=false, producing pixel-identical output to the main file browser. Removed renderDirectoryPreviewBody() which is no longer needed. --- internal/fs/preview.go | 14 +++++++++----- internal/ui/model.go | 13 ++++++++++++- vcom.toml | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/internal/fs/preview.go b/internal/fs/preview.go index a807c98..fb08c24 100644 --- a/internal/fs/preview.go +++ b/internal/fs/preview.go @@ -53,6 +53,7 @@ type Preview struct { Body string PlainBody string Metadata Metadata + Entries []Entry } type PreviewOptions struct { @@ -91,7 +92,7 @@ func BuildPreview(entry Entry, options PreviewOptions) Preview { preview.Kind = PreviewKindDirectory preview.Metadata.Size = entry.Size preview.Metadata.SizeKnown = entry.DirSizeKnown - preview.Body = buildDirectoryPreview(entry.Path, options) + preview.Body, preview.Entries = buildDirectoryPreview(entry.Path, options) preview.PlainBody = preview.Body return preview } @@ -305,7 +306,7 @@ func chromaStyleName(themeName string) string { } } -func buildDirectoryPreview(path string, options PreviewOptions) string { +func buildDirectoryPreview(path string, options PreviewOptions) (string, []Entry) { entries, err := ListDir(path, ListOptions{ ShowHidden: options.ShowHidden, DirsFirst: options.DirsFirst, @@ -313,12 +314,15 @@ func buildDirectoryPreview(path string, options PreviewOptions) string { SortReverse: options.SortReverse, }) if err != nil { - return fmt.Sprintf("Could not list directory:\n\n%s", err) + return fmt.Sprintf("Could not list directory:\n\n%s", err), nil } if len(entries) == 0 { - return "Directory is empty." + return "Directory is empty.", nil } + // Return all entries as-is for column-based rendering. + // The text body is still generated for terminals that don't support + // the rich rendering, and as a fallback. var lines []string for _, entry := range entries { if entry.IsParent { @@ -342,7 +346,7 @@ func buildDirectoryPreview(path string, options PreviewOptions) string { } } - return strings.Join(lines, "\n") + return strings.Join(lines, "\n"), entries } func previewIcon(entry Entry, useNerdIcons bool) string { diff --git a/internal/ui/model.go b/internal/ui/model.go index 42bcc7a..1d98217 100644 --- a/internal/ui/model.go +++ b/internal/ui/model.go @@ -2363,7 +2363,18 @@ func renderPreviewPane(preview vfs.Preview, viewportModel *viewport.Model, cfg c contentHeight := max(innerHeight-usedHeight, 3) viewportModel.Width = max(innerWidth-2, 10) viewportModel.Height = max(contentHeight-3, 1) - parts = append(parts, renderPreviewContent(viewportModel, palette, innerWidth, contentHeight)) + + // Directory previews: borrow the column layout from the browser pane + // (renderPaneRows + renderColumnsHeader at the same innerWidth), + // but non-interactive (no cursor, no selection). + if preview.Kind == vfs.PreviewKindDirectory && len(preview.Entries) > 0 { + dirPane := BrowserPane{Entries: preview.Entries} + headerRow := renderColumnsHeader(cfg, innerWidth, palette, palette.Panel, useNerdfont) + rows := renderPaneRows(dirPane, cfg, palette, innerWidth, contentHeight, false, -1, palette.Panel, useNerdfont) + parts = append(parts, lipgloss.JoinVertical(lipgloss.Left, headerRow, rows)) + } else { + parts = append(parts, renderPreviewContent(viewportModel, palette, innerWidth, contentHeight)) + } content := lipgloss.NewStyle(). Width(innerWidth). diff --git a/vcom.toml b/vcom.toml index f65c775..498e302 100644 --- a/vcom.toml +++ b/vcom.toml @@ -4,7 +4,7 @@ right_path = '' [ui] app_title = 'vcom' -theme = 'vesper' +theme = 'ayu-dark' icon_mode = 'auto' show_title_bar = true show_footer = true