Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/superfile-build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ permissions:

jobs:
build:
if: github.event.pull_request.draft == false
name: Build and Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/testsuite-run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ permissions:

jobs:
test:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- name: Checkout code
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ require (
)

require (
github.com/BourgeoisBear/rasterm v1.1.1
github.com/BourgeoisBear/rasterm v1.1.2
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BourgeoisBear/rasterm v1.1.1 h1:J94gv2pRv+G0jXj9Pf3jUk2qQtWPCiTsiRGxlXoQvgo=
github.com/BourgeoisBear/rasterm v1.1.1/go.mod h1:Ifd+To5s/uyUiYx+B4fxhS8lUNwNLSxDBjskmC5pEyw=
github.com/BourgeoisBear/rasterm v1.1.2 h1:hWHZBZ45N366uNSqxWFYBV0y19q8fXRXADhPkoLF4Ss=
github.com/BourgeoisBear/rasterm v1.1.2/go.mod h1:Ifd+To5s/uyUiYx+B4fxhS8lUNwNLSxDBjskmC5pEyw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
Expand Down
4 changes: 2 additions & 2 deletions gomod2nix.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ schema = 3

[mod]
[mod."github.com/BourgeoisBear/rasterm"]
version = "v1.1.1"
hash = "sha256-uCZauICgNHFVJvq1rsZ8nDwMgyNsJOOPFwXIgWcqmFE="
version = "v1.1.2"
hash = "sha256-fYV85hVcIAT01xriEpWn0f/YyGgsc7W+0SW6iz9K/+A="
[mod."github.com/adrg/xdg"]
version = "v0.5.3"
hash = "sha256-bo6tBgHS+3sl6f4oWpmdFrZjfV6eA/3xAlysSW0bIEs="
Expand Down
2 changes: 1 addition & 1 deletion src/internal/common/style_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func FilePanelBorderStyle(height int, width int, filePanelFocussed bool, borderB
}

// Generate filePreview Box
func FilePreviewBox(height int, width int) lipgloss.Style {
func FilePreviewStyle(height int, width int) lipgloss.Style {
return lipgloss.NewStyle().
Width(width).
Height(height).
Expand Down
80 changes: 43 additions & 37 deletions src/internal/ui/preview/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,15 @@ import (
"sort"
"strings"

"github.com/yorukot/superfile/src/internal/ui"
"github.com/yorukot/superfile/src/internal/ui/rendering"

"github.com/yorukot/superfile/src/internal/common"
"github.com/yorukot/superfile/src/internal/utils"

"github.com/alecthomas/chroma/v2/lexers"
"github.com/charmbracelet/lipgloss"

"github.com/yorukot/ansichroma"

"github.com/yorukot/superfile/src/config/icon"
"github.com/yorukot/superfile/src/internal/common"
"github.com/yorukot/superfile/src/internal/ui"
"github.com/yorukot/superfile/src/internal/ui/rendering"
"github.com/yorukot/superfile/src/internal/utils"
filepreview "github.com/yorukot/superfile/src/pkg/file_preview"
)

Expand Down Expand Up @@ -79,7 +76,7 @@ func (m *Model) Close() {
func (m *Model) RenderText(text string) string {
return ui.FilePreviewPanelRenderer(m.height, m.width).
AddLines(text).
Render() + m.imagePreviewer.ClearKittyImages()
Render() + m.imagePreviewer.ClearAllImages()
}

func (m *Model) SetContentWithRenderText(text string) {
Expand Down Expand Up @@ -132,17 +129,17 @@ func renderFileInfoError(r *rendering.Renderer, err error) string {
return r.Render()
}

func renderUnsupportedFormat(box lipgloss.Style) string {
return box.Render(common.FilePreviewUnsupportedFormatText)
func renderUnsupportedFormat(filePreviewStyle lipgloss.Style) string {
return filePreviewStyle.Render(common.FilePreviewUnsupportedFormatText)
}

func renderUnsupportedFileMode(r *rendering.Renderer) string {
r.AddLines(common.FilePreviewUnsupportedFileMode)
return r.Render()
}

func renderThumbnailGenerationError(box lipgloss.Style) string {
return box.Render(common.FilePreviewThumbnailGenerationErrorText)
func renderThumbnailGenerationError(filePreviewStyle lipgloss.Style) string {
return filePreviewStyle.Render(common.FilePreviewThumbnailGenerationErrorText)
}

func renderDirectoryPreview(r *rendering.Renderer, itemPath string, previewHeight int) string {
Expand Down Expand Up @@ -182,62 +179,59 @@ func renderDirectoryPreview(r *rendering.Renderer, itemPath string, previewHeigh
return r.Render()
}

func (m *Model) renderImagePreview(box lipgloss.Style, itemPath string, previewWidth,
func (m *Model) renderImagePreview(filePreviewStyle lipgloss.Style, itemPath string, previewWidth,
previewHeight int, sideAreaWidth int,
) string {
if !m.open {
return box.Render("\n --- Preview panel is closed ---")
return filePreviewStyle.Render("\n --- Preview panel is closed ---")
}

if !common.Config.ShowImagePreview {
return box.Render("\n --- Image preview is disabled ---")
return filePreviewStyle.Render("\n --- Image preview is disabled ---")
}

// Use the new auto-detection function to choose the best renderer
imageRender, err := m.imagePreviewer.ImagePreview(itemPath, previewWidth, previewHeight,
common.Theme.FilePanelBG, sideAreaWidth)
common.Theme.FilePanelBG, sideAreaWidth, filePreviewStyle)
if errors.Is(err, image.ErrFormat) {
return box.Render("\n --- " + icon.Error + " Unsupported image formats ---")
return filePreviewStyle.Render("\n --- " + icon.Error + " Unsupported image formats ---")
}

if err != nil {
slog.Error("Error convert image to ansi", "error", err)
return box.Render("\n --- " + icon.Error + " Error convert image to ansi ---")
return filePreviewStyle.Render("\n --- " + icon.Error + " Error convert image to ansi ---")
}
return imageRender

// Check if this looks like Kitty protocol output (starts with escape sequences)
// For Kitty protocol, avoid using lipgloss alignment to prevent layout drift
if strings.HasPrefix(imageRender, "\x1b_G") {
rendered := common.FilePreviewBox(previewHeight, previewWidth).Render(imageRender)
return rendered
}

// For ANSI output, we can safely use vertical alignment
return box.AlignVertical(lipgloss.Center).AlignHorizontal(lipgloss.Center).Render(imageRender)

}

func (m *Model) renderTextPreview(r *rendering.Renderer, box lipgloss.Style, itemPath string,
func (m *Model) renderTextPreview(r *rendering.Renderer, filePreviewStyle lipgloss.Style, itemPath string,
previewWidth, previewHeight int,
) string {
format := lexers.Match(filepath.Base(itemPath))
if format == nil {
isText, err := common.IsTextFile(itemPath)
if err != nil {
slog.Error("Error while checking text file", "error", err)
return box.Render(common.FilePreviewError)
return filePreviewStyle.Render(common.FilePreviewError)
} else if !isText {
return box.Render(common.FilePreviewUnsupportedFormatText)
return filePreviewStyle.Render(common.FilePreviewUnsupportedFormatText)
}
}

fileContent, err := utils.ReadFileContent(itemPath, previewWidth, previewHeight)
if err != nil {
slog.Error("Error open file", "error", err)
return box.Render(common.FilePreviewError)
return filePreviewStyle.Render(common.FilePreviewError)
}

if fileContent == "" {
return box.Render(common.FilePreviewEmptyText)
return filePreviewStyle.Render(common.FilePreviewEmptyText)
}

if format != nil {
Expand All @@ -247,7 +241,7 @@ func (m *Model) renderTextPreview(r *rendering.Renderer, box lipgloss.Style, ite
}
if common.Config.CodePreviewer == "bat" {
if m.batCmd == "" {
return box.Render("\n --- " + icon.Error +
return filePreviewStyle.Render("\n --- " + icon.Error +
" 'bat' is not installed or not found. ---\n --- Cannot render file preview. ---")
}
fileContent, err = getBatSyntaxHighlightedContent(itemPath, previewHeight, background, m.batCmd)
Expand All @@ -257,7 +251,7 @@ func (m *Model) renderTextPreview(r *rendering.Renderer, box lipgloss.Style, ite
}
if err != nil {
slog.Error("Error render code highlight", "error", err)
return box.Render("\n" + common.FilePreviewError)
return filePreviewStyle.Render("\n" + common.FilePreviewError)
}
}

Expand All @@ -269,9 +263,9 @@ func (m *Model) RenderWithPath(itemPath string, fullModelWidth int) string {
previewHeight := m.height
previewWidth := m.width

box := common.FilePreviewBox(previewHeight, previewWidth)
filePreviewStyle := common.FilePreviewStyle(previewHeight, previewWidth)
r := ui.FilePreviewPanelRenderer(previewHeight, previewWidth)
clearCmd := m.imagePreviewer.ClearKittyImages()
clearCmd := m.imagePreviewer.ClearAllImages()

fileInfo, infoErr := os.Stat(itemPath)
if infoErr != nil {
Expand All @@ -288,7 +282,7 @@ func (m *Model) RenderWithPath(itemPath string, fullModelWidth int) string {

ext := filepath.Ext(itemPath)
if slices.Contains(common.UnsupportedPreviewFormats, ext) {
return renderUnsupportedFormat(box) + clearCmd
return renderUnsupportedFormat(filePreviewStyle) + clearCmd
}

if fileInfo.IsDir() {
Expand All @@ -299,16 +293,28 @@ func (m *Model) RenderWithPath(itemPath string, fullModelWidth int) string {
thumbnailPath, err := m.thumbnailGenerator.GetThumbnailOrGenerate(itemPath)
if err != nil {
slog.Error("Error generating thumbnail", "error", err)
return renderThumbnailGenerationError(box) + clearCmd
return renderThumbnailGenerationError(filePreviewStyle) + clearCmd
}
return m.renderImagePreview(box, thumbnailPath, previewWidth, previewHeight, fullModelWidth-previewWidth+1)
return m.renderImagePreview(
filePreviewStyle,
thumbnailPath,
previewWidth,
previewHeight,
fullModelWidth-previewWidth+1,
)
}

if isImageFile(itemPath) {
return m.renderImagePreview(box, itemPath, previewWidth, previewHeight, fullModelWidth-previewWidth+1)
return m.renderImagePreview(
filePreviewStyle,
itemPath,
previewWidth,
previewHeight,
fullModelWidth-previewWidth+1,
)
}

return m.renderTextPreview(r, box, itemPath, previewWidth, previewHeight) + clearCmd
return m.renderTextPreview(r, filePreviewStyle, itemPath, previewWidth, previewHeight) + clearCmd
}

func getBatSyntaxHighlightedContent(
Expand Down
Loading