55 "os"
66 "os/exec"
77 "path/filepath"
8+ "strconv"
89 "strings"
910
1011 "github.com/spf13/cobra"
@@ -209,21 +210,22 @@ func Preview(cmd *cobra.Command, args []string) error {
209210
210211 text := args [0 ]
211212
213+ if strings .Contains (text , "-->" ) || strings .Contains (text , "<--" ) {
214+ return nil
215+ }
216+
212217 fzf_preview_lines := os .Getenv ("FZF_PREVIEW_LINES" )
213218 fzf_preview_columns := os .Getenv ("FZF_PREVIEW_COLUMNS" )
214- _ , exists := os .LookupEnv ("KITTY_WINDOW_ID" )
215-
216- path , _ := exec .LookPath ("chafa" )
217219
218- if strings .Contains (text , "-->" ) {
219- return nil
220- } else if strings .Contains (text , "<--" ) {
221- return nil
222- }
220+ // Check for terminal capabilities
221+ kittySupport := os .Getenv ("KITTY_WINDOW_ID" ) != ""
222+ sixelSupport := checkSixelSupport ()
223223
224224 url := re .FindStringSubmatch (text )[1 ]
225225
226- if exists {
226+ // Priority: Kitty > Sixel > Chafa
227+ switch {
228+ case kittySupport :
227229 command = exec .Command (
228230 "kitty" ,
229231 "icat" ,
@@ -235,15 +237,71 @@ func Preview(cmd *cobra.Command, args []string) error {
235237 "--scale-up" ,
236238 url ,
237239 )
238- } else if path != "" {
240+
241+ case sixelSupport :
242+ // Download image for sixel conversion
239243 err := DLSave (url , & config .TempFolder )
240244 if err != nil {
241245 return err
242246 }
243247
248+ // Get terminal dimensions
249+ width , height , err := getPreviewPaneDimensions ()
250+ if err != nil {
251+ // Fallback to FZF environment variables if available
252+ if fzfLines := os .Getenv ("FZF_PREVIEW_LINES" ); fzfLines != "" {
253+ if fzfCols := os .Getenv ("FZF_PREVIEW_COLUMNS" ); fzfCols != "" {
254+ width = fzfCols
255+ height = fzfLines
256+ }
257+ } else {
258+ // Last resort fallback
259+ width = "80"
260+ height = "24"
261+ }
262+ }
263+
244264 SplitUrl := strings .Split (url , "/" )
245265 FileName := SplitUrl [len (SplitUrl )- 1 ]
266+ file = filepath .Join (config .TempFolder , FileName )
267+
268+ // Convert terminal cells to pixels for sixel
269+ pixelWidth , pixelHeight := cellsToPixels (width , height )
270+
271+ // Try img2sixel first (better quality)
272+ if path , err := exec .LookPath ("img2sixel" ); err == nil {
273+ command = exec .Command (
274+ path ,
275+ "-w" , pixelWidth ,
276+ file ,
277+ )
278+ } else if path , err := exec .LookPath ("magick" ); err == nil {
279+ // Fallback to ImageMagick convert
280+ command = exec .Command (
281+ path ,
282+ file ,
283+ "-resize" , fmt .Sprintf ("%sx%s>" , pixelWidth , pixelHeight ),
284+ "-quality" , "100" ,
285+ "sixel:-" ,
286+ )
287+ } else {
288+ return fmt .Errorf ("sixel support detected but no converter found (install libsixel or imagemagick)" )
289+ }
290+
291+ default :
292+ // Fallback to chafa
293+ path , err := exec .LookPath ("chafa" )
294+ if err != nil {
295+ return fmt .Errorf ("no supported image preview method found" )
296+ }
246297
298+ err = DLSave (url , & config .TempFolder )
299+ if err != nil {
300+ return err
301+ }
302+
303+ SplitUrl := strings .Split (url , "/" )
304+ FileName := SplitUrl [len (SplitUrl )- 1 ]
247305 file = filepath .Join (config .TempFolder , FileName )
248306
249307 command = exec .Command (
@@ -252,20 +310,106 @@ func Preview(cmd *cobra.Command, args []string) error {
252310 fmt .Sprintf ("--size=%sx%s" , fzf_preview_columns , fzf_preview_lines ),
253311 "--clear" ,
254312 )
255- } else {
256- return fmt .Errorf ("You either need chafa or the kitty terminal." )
257313 }
258314
259315 command .Stdout = os .Stdout
260316 command .Stderr = os .Stderr
261317
262318 if err := command .Run (); err != nil {
263- return fmt .Errorf ("failed to execute command: %s" , err )
319+ return fmt .Errorf ("failed to execute preview command: %s" , err )
264320 }
265321
322+ // Clean up temporary file if created
266323 if file != "" {
267324 return os .Remove (file )
268325 }
269326
270327 return nil
271328}
329+
330+ // getPreviewPaneDimensions attempts to get the actual preview pane size
331+ func getPreviewPaneDimensions () (string , string , error ) {
332+ // Method 1: Try to get terminal size using stty
333+ cmd := exec .Command ("stty" , "size" )
334+ cmd .Stdin = os .Stdin
335+ output , err := cmd .Output ()
336+ if err == nil {
337+ parts := strings .Fields (string (output ))
338+ if len (parts ) == 2 {
339+ // fzf preview typically uses ~40% of terminal width
340+ // Adjust these percentages based on your fzf configuration
341+ totalHeight , _ := strconv .Atoi (parts [0 ])
342+ totalWidth , _ := strconv .Atoi (parts [1 ])
343+
344+ // Assuming default fzf preview layout
345+ previewHeight := totalHeight - 3 // Account for fzf interface
346+ previewWidth := int (float64 (totalWidth ) * 0.5 ) // Half screen by default
347+
348+ return strconv .Itoa (previewWidth ), strconv .Itoa (previewHeight ), nil
349+ }
350+ }
351+
352+ // Method 2: Use tput
353+ widthCmd := exec .Command ("tput" , "cols" )
354+ heightCmd := exec .Command ("tput" , "lines" )
355+
356+ widthOut , errW := widthCmd .Output ()
357+ heightOut , errH := heightCmd .Output ()
358+
359+ if errW == nil && errH == nil {
360+ width , _ := strconv .Atoi (strings .TrimSpace (string (widthOut )))
361+ height , _ := strconv .Atoi (strings .TrimSpace (string (heightOut )))
362+
363+ // Adjust for fzf preview pane (customize based on your layout)
364+ previewWidth := int (float64 (width ) * 0.5 )
365+ previewHeight := height - 3
366+
367+ return strconv .Itoa (previewWidth ), strconv .Itoa (previewHeight ), nil
368+ }
369+
370+ return "" , "" , fmt .Errorf ("could not determine terminal dimensions" )
371+ }
372+
373+ // cellsToPixels converts terminal cell dimensions to pixel dimensions
374+ func cellsToPixels (cellWidth , cellHeight string ) (string , string ) {
375+ // Default cell dimensions (can be made configurable)
376+ cellPixelWidth := 9 // Typical terminal cell width in pixels
377+ cellPixelHeight := 18 // Typical terminal cell height in pixels
378+
379+ // Check for common terminal environment variables
380+ if os .Getenv ("TERM_PROGRAM" ) == "iTerm.app" {
381+ cellPixelWidth = 8
382+ cellPixelHeight = 16
383+ }
384+
385+ width , _ := strconv .Atoi (cellWidth )
386+ height , _ := strconv .Atoi (cellHeight )
387+
388+ pixelWidth := width * cellPixelWidth
389+ pixelHeight := height * cellPixelHeight
390+
391+ // Apply some padding to ensure image fits well
392+ pixelWidth = int (float64 (pixelWidth ) * 0.95 )
393+ pixelHeight = int (float64 (pixelHeight ) * 0.95 )
394+
395+ return strconv .Itoa (pixelWidth ), strconv .Itoa (pixelHeight )
396+ }
397+
398+ // checkSixelSupport detects if the terminal supports sixel graphics
399+ func checkSixelSupport () bool {
400+ // Check TERM environment variable for known sixel-capable terminals
401+ term := os .Getenv ("TERM" )
402+ sixelTerms := []string {"xterm-256color" , "mlterm" , "yaft-256color" , "foot" , "contour" , "tmux-256color" }
403+
404+ for _ , st := range sixelTerms {
405+ if strings .Contains (term , st ) {
406+ return true
407+ }
408+ }
409+
410+ // Check for explicit sixel support environment variable
411+ if os .Getenv ("SIXEL_SUPPORT" ) == "1" {
412+ return true
413+ }
414+ return false
415+ }
0 commit comments