From 243198a6aa7dd4559ebde75ae7ccb9a0cedecfff Mon Sep 17 00:00:00 2001 From: gzagatti Date: Mon, 21 Oct 2024 18:30:34 +0200 Subject: [PATCH] better handling of terminal image printing in Wezterm in WSL --- nnn/plugins/preview-tui | 424 ++++++++++++++++++++++------------------ shells/zshrc | 37 ++++ 2 files changed, 276 insertions(+), 185 deletions(-) diff --git a/nnn/plugins/preview-tui b/nnn/plugins/preview-tui index 15ea877..99aec16 100755 --- a/nnn/plugins/preview-tui +++ b/nnn/plugins/preview-tui @@ -1,18 +1,19 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash # Description: Terminal based file previewer # # Note: This plugin needs a "NNN_FIFO" to work. See man. # # Dependencies: -# - Supports 5 independent methods to preview with: +# - Supports 6 independent methods to preview with: # - tmux (>=3.0), or # - kitty with allow_remote_control and listen_on set in kitty.conf, or +# - wezterm (https://wezfurlong.org/wezterm), or # - QuickLook on WSL (https://github.com/QL-Win/QuickLook), or # - Windows Terminal (https://github.com/Microsoft/Terminal | https://aka.ms/terminal) with WSL, or -# - $TERMINAL set to a terminal (it's xterm by default). -# - less or $PAGER -# - tree or exa or ls +# - $NNN_TERMINAL set to a terminal (it's xterm by default). +# - less or $NNN_PAGER +# - tree or exa/eza or (GNU) ls # - mediainfo or file # - mktemp # - unzip @@ -20,24 +21,30 @@ # - man # - optional: bsdtar or atool for additional archive preview # - optional: bat for code syntax highlighting -# - optional: ueberzug, kitty terminal, viu or catimg for images -# - optional: convert(ImageMagick) for playing gif preview +# - optional: ueberzug, kitty terminal, wezterm terminal, img2sixel, viu, catimg, timg or chafa for images +# - optional: convert(ImageMagick) for playing gif preview (mandatory for kitty image previews) +# - optional: mpv for gif and video +# Also requires a terminal supporting the sixel (https://www.arewesixelyet.com/) +# or kitty (https://sw.kovidgoyal.net/kitty/graphics-protocol) video_output backends. +# Requires tmux compiled with `./configure --enable-sixel` if used. # - optional: ffmpegthumbnailer for video thumbnails (https://github.com/dirkvdb/ffmpegthumbnailer) # - optional: ffmpeg for audio thumbnails # - optional: libreoffce for opendocument/officedocument preview # - optional: pdftoppm(poppler) for pdf thumbnails # - optional: gnome-epub-thumbnailer for epub thumbnails (https://gitlab.gnome.org/GNOME/gnome-epub-thumbnailer) # - optional: fontpreview for font preview (https://github.com/sdushantha/fontpreview) +# - optional: djvulibre for djvu # - optional: glow or lowdown for markdown # - optional: w3m or lynx or elinks for html -# - optional: set/export ICONLOOKUP as 1 to enable file icons in front of directory previews with .iconlookup -# Icons and colors are configureable in .iconlookup +# - optional: set/export NNN_ICONLOOKUP as 1 to enable file icons in front of directory previews with .iconlookup +# Icons and colors are configurable in .iconlookup # - optional: scope.sh file viewer from ranger. # 1. drop scope.sh executable in $PATH -# 2. set/export $USE_SCOPE as 1 +# 2. set/export $NNN_SCOPE as 1 # - optional: pistol file viewer (https://github.com/doronbehar/pistol). # 1. install pistol -# 2. set/export $USE_PISTOL as 1 +# 2. set/export $NNN_PISTOL as 1 +# - optional: librsvg for rsvg-convert # # Usage: # You need to set a NNN_FIFO path and a key for the plugin with NNN_PLUG, @@ -57,100 +64,157 @@ # # The previews will be shown in a tmux split. If that isn't possible, it # will try to use a kitty terminal split. And as a final fallback, a -# different terminal window will be used ($TERMINAL). +# different terminal window will be used ($NNN_TERMINAL). # -# Tmux and kitty users can configure $SPLIT to either "h" or "v" to set a -# 'h'orizontal split or a 'v'ertical split (as in, the line that splits the -# windows will be horizontal or vertical). +# Kitty users need something similar to the following in their kitty.conf: +# - `allow_remote_control yes` +# - `listen_on unix:$TMPDIR/kitty` +# - `enabled_layouts splits` (optional) +# With ImageMagick installed, this terminal can use the icat kitten to display images. +# Refer to kitty documentation for further details. # -# Kitty users need `allow_remote_control` set to `yes`, and `listen_on` set -# to e.g. "unix:$TMPDIR/kitty". To customize the window split, `enabled_layouts` -# has to be set to `all` or `splits` (the former is the default value). -# This terminal is also able to show images without extra dependencies. +# Users with both tmux and kitty can leverage image previews inside tmux with kitty's icat kitten +# - setup kitty as stated above +# - tmux >= v3.3a required +# - add the following to your tmux.conf: +# - `set -g allow-passthrough on` # -# Iterm2 users are recommended to use viu to view images without getting pixelated. +# Wezterm should work out of the box. If `NNN_PREVIEWIMGPROG` is not specified it will use +# built in iTerm2 image protocol. +# +# Note that GNU ls is used for its `--group-directories-first` flag. +# On MacOS this may be installed with `brew install coreutils`, or the flag can be removed. +# iTerm2 users are recommended to use viu to view images without getting pixelated. # # Windows Terminal users can set "Profile termination behavior" under "Profile > Advanced" settings -# to automaticaly close pane on quit when exit code is 0. +# to automatically close pane on quit when exit code is 0. +# +# When specifying a different terminal, additional arguments are supported. In particular, you can +# append a specific title to the terminal and set it to "nofocus" in your WM config. +# E.g for alacritty and i3, you can set $NNN_TERMINAL to 'alacritty --title preview-tui' and add +# 'no_focus [title="preview-tui"]' to your i3 config file. # -# Shell: POSIX compliant -# Authors: Todd Yamakawa, Léo Villeveygoux, @Recidiviste, Mario Ortiz Manero, Luuk van Baal, @WanderLanz +# Shell: Bash (for environment manipulation through arrays) +# Authors: Todd Yamakawa, Léo Villeveygoux, @Recidiviste, Mario Ortiz Manero, Luuk van Baal, @WanderLanz, @flipflop133 -#SPLIT="$SPLIT" # you can set a permanent split here -#TERMINAL="$TERMINAL" # same goes for the terminal -USE_SCOPE="${USE_SCOPE:-0}" -USE_PISTOL="${USE_PISTOL:-0}" -ICONLOOKUP="${ICONLOOKUP:-0}" -PAGER="${PAGER:-less -P?n -R}" -TMPDIR="${TMPDIR:-/tmp}" -BAT_STYLE="${BAT_STYLE:-plain}" -BAT_THEME="${BAT_THEME:-ansi}" -# Consider setting NNN_PREVIEWDIR to $XDG_CACHE_HOME/nnn/previews if you want to keep previews on disk between reboots -NNN_PREVIEWDIR="${NNN_PREVIEWDIR:-$TMPDIR/nnn/previews}" -NNN_PREVIEWWIDTH="${NNN_PREVIEWWIDTH:-1920}" -NNN_PREVIEWHEIGHT="${NNN_PREVIEWHEIGHT:-1080}" -NNN_PARENT="${NNN_FIFO#*.}" -[ "$NNN_PARENT" -eq "$NNN_PARENT" ] 2>/dev/null || NNN_PARENT="" -FIFOPID="$TMPDIR/nnn-preview-tui-fifopid.$NNN_PARENT" -PREVIEWPID="$TMPDIR/nnn-preview-tui-pagerpid.$NNN_PARENT" -CURSEL="$TMPDIR/nnn-preview-tui-selection.$NNN_PARENT" -FIFO_UEBERZUG="$TMPDIR/nnn-preview-tui-ueberzug-fifo.$NNN_PARENT" +# Configurable environment variables: +NNN_SPLIT=${NNN_SPLIT:-} # permanent split direction +NNN_TERMINAL=${NNN_TERMINAL:-} # external terminal to be used +# NNN_TERMINAL=${NNN_TERMINAL:-} # external terminal to be used +NNN_SPLITSIZE=${NNN_SPLITSIZE:-50} # previewer split size percentage +TMPDIR=${TMPDIR:-/tmp} # location of temporary files +ENVVARS=( + "NNN_SCOPE=${NNN_SCOPE:-0}" # use scope + "NNN_PISTOL=${NNN_PISTOL:-0}" # use pistol + "NNN_ICONLOOKUP=${NNN_ICONLOOKUP:-0}" # use .iconlookup + "NNN_PAGER=${NNN_PAGER:-less -R -C}" # pager options + "NNN_BATTHEME=${NNN_BATTHEME:-ansi}" # bat theme + "NNN_BATSTYLE=${NNN_BATSTYLE:-numbers}" # bat style + "NNN_PREVIEWWIDTH=${NNN_PREVIEWWIDTH:-1920}" # width of generated preview images + "NNN_PREVIEWHEIGHT=${NNN_PREVIEWHEIGHT:-1080}" # height of generated preview images + "NNN_PREVIEWDIR=${NNN_PREVIEWDIR:-$TMPDIR/nnn/previews}" # location of generated preview images + "NNN_PREVIEWIMGPROG=${NNN_PREVIEWIMGPROG:-}" # command used to preview images + "NNN_PREVIEWVIDEO=${NNN_PREVIEWVIDEO:-}" # mpv backend used to preview video +) +# Non-configurable environment variables +NNN_PARENT=${NNN_FIFO#*.} +[ "$NNN_PARENT" -eq "$NNN_PARENT" ] 2>/dev/null || NNN_PARENT="" # Make empty if non-numeric +ENVVARS+=( + "PWD=$PWD" + "PATH=$PATH" + "LSCOLORS=$LSCOLORS" + "LS_COLORS=$LS_COLORS" + "NNN_FIFO=$NNN_FIFO" + "FIFOPID=$TMPDIR/nnn-preview-tui-fifopid.$NNN_PARENT" + "FIFOPATH=$TMPDIR/nnn-preview-tui-fifo.$NNN_PARENT" + "PREVIEWPID=$TMPDIR/nnn-preview-tui-previewpid.$NNN_PARENT" + "CURSEL=$TMPDIR/nnn-preview-tui-selection.$NNN_PARENT" + "FIFO_UEBERZUG=$TMPDIR/nnn-preview-tui-ueberzug-fifo.$NNN_PARENT" + "POSOFFSET=$TMPDIR/nnn-preview-tui-posoffset" +) -start_preview() { - [ "$PAGER" = "most" ] && PAGER="less -R" +trap '' PIPE +exists() { type "$1" >/dev/null 2>&1 ;} +pkill() { command pkill "$@" >/dev/null 2>&1 ;} +prompt() { clear; printf "%b" "$@"; cfg=$(stty -g); stty raw -echo; head -c 1; stty "$cfg" ;} +pidkill() { + if [ -f "$1" ]; then + PID="$(cat "$1" 2>/dev/null)" || return 1 + kill "$PID" >/dev/null 2>&1 + RET=$? + wait "$PID" 2>/dev/null + return $RET + fi + return 1 +} +start_preview() { if [ -e "${TMUX%%,*}" ] && tmux -V | grep -q '[ -][3456789]\.'; then - TERMINAL=tmux + NNN_TERMINAL=tmux + exists mpv && tmux display -p '#{client_termfeatures}' | grep -q 'sixel' && ENVVARS+=("NNN_PREVIEWVIDEO=sixel") elif [ -n "$KITTY_LISTEN_ON" ]; then - TERMINAL=kitty - elif [ -z "$TERMINAL" ] && [ "$TERM_PROGRAM" = "iTerm.app" ]; then - TERMINAL=iterm + NNN_TERMINAL=kitty + exists mpv && ENVVARS+=("NNN_PREVIEWVIDEO=kitty") + elif [ -z "$NNN_TERMINAL" ] && [ "$TERM_PROGRAM" = "WezTerm" ]; then + # https://superuser.com/questions/1749781/how-can-i-check-if-the-environment-is-wsl-from-a-shell-script + if [ -f /proc/sys/fs/binfmt_misc/WSLInterop ]; then + NNN_TERMINAL=wezterm.exe + else + NNN_TERMINAL=wezterm + fi + exists mpv && ENVVARS+=("NNN_PREVIEWVIDEO=kitty") + elif [ -z "$NNN_TERMINAL" ] && [ "$TERM_PROGRAM" = "iTerm.app" ]; then + NNN_TERMINAL=iterm + exists mpv && ENVVARS+=("NNN_PREVIEWVIDEO=sixel") elif [ -n "$WT_SESSION" ]; then - TERMINAL=winterm + NNN_TERMINAL=winterm else - TERMINAL="${TERMINAL:-xterm}" + NNN_TERMINAL="${NNN_TERMINAL:-xterm}" fi - if [ -z "$SPLIT" ] && [ $(($(tput lines <"$TTY") * 2)) -gt "$(tput cols <"$TTY")" ]; then - SPLIT='h' - elif [ "$SPLIT" != 'h' ]; then - SPLIT='v' + if [ -z "$NNN_SPLIT" ] && [ $(($(tput lines) * 2)) -gt "$(tput cols)" ]; then + NNN_SPLIT='h' + elif [ "$NNN_SPLIT" != 'h' ]; then + NNN_SPLIT='v' fi - case "$TERMINAL" in + ENVVARS+=("NNN_TERMINAL=$NNN_TERMINAL" "NNN_SPLIT=$NNN_SPLIT" "QLPATH=$2" "PREVIEW_MODE=1") + case "$NNN_TERMINAL" in + iterm|winterm) # has to run in separate shell command: escape + ENVVARS=("${ENVVARS[@]/#/\\\"}") + ENVVARS=("${ENVVARS[@]/%/\\\"}") + command="$SHELL -c 'env ${ENVVARS[*]} \\\"$0\\\" \\\"$1\\\"'" ;; + # wezterm.exe) + # ENVVARS=("${ENVVARS[@]/#/\\\"}") + # ENVVARS=("${ENVVARS[@]/%/\\\"}") + # echo ${ENVVARS[*]} >&2 + # command="--cd \"$PWD\" -- env ${ENVVARS[*]} \"$0\" \"$1\"" ;; + # echo ${ENVVARS[@]} >&2 + # command="--cd \"$PWD\" -- env ${ENVVARS[@]} less output.txt" ;; + esac + + case "$NNN_TERMINAL" in tmux) # tmux splits are inverted - if [ "$SPLIT" = "v" ]; then DSPLIT="h"; else DSPLIT="v"; fi - tmux split-window -e "NNN_FIFO=$NNN_FIFO" -e "PREVIEW_MODE=1" -e TTY="$TTY" \ - -e "CURSEL=$CURSEL" -e "TMPDIR=$TMPDIR" -e "FIFOPID=$FIFOPID" \ - -e "BAT_STYLE=$BAT_STYLE" -e "BAT_THEME=$BAT_THEME" -e "PREVIEWPID=$PREVIEWPID" \ - -e "PAGER=$PAGER" -e "ICONLOOKUP=$ICONLOOKUP" -e "NNN_PREVIEWWIDTH=$NNN_PREVIEWWIDTH" \ - -e "USE_SCOPE=$USE_SCOPE" -e "SPLIT=$SPLIT" -e "USE_PISTOL=$USE_PISTOL" \ - -e "NNN_PREVIEWDIR=$NNN_PREVIEWDIR" -e "NNN_PREVIEWHEIGHT=$NNN_PREVIEWHEIGHT" \ - -e "FIFO_UEBERZUG=$FIFO_UEBERZUG" -e "QLPATH=$2" -d"$DSPLIT" "$0" "$1" ;; + ENVVARS=("${ENVVARS[@]/#/-e}") + if [ "$NNN_SPLIT" = "v" ]; then split="h"; else split="v"; fi + tmux split-window -l"$NNN_SPLITSIZE"% "${ENVVARS[@]}" -d"$split" -p"$NNN_SPLITSIZE" "$0" "$1" ;; kitty) # Setting the layout for the new window. It will be restored after the script ends. + ENVVARS=("${ENVVARS[@]/#/--env=}") kitty @ goto-layout splits - # Trying to use kitty's integrated window management as the split window. All - # environmental variables that will be used in the new window must be explicitly passed. - kitty @ launch --no-response --title "nnn preview" --keep-focus \ - --cwd current --env "PATH=$PATH" --env "NNN_FIFO=$NNN_FIFO" \ - --env "PREVIEW_MODE=1" --env "PAGER=$PAGER" --env "TMPDIR=$TMPDIR" \ - --env "USE_SCOPE=$USE_SCOPE" --env "SPLIT=$SPLIT" --env "TERMINAL=$TERMINAL"\ - --env "PREVIEWPID=$PREVIEWPID" --env "FIFO_UEBERZUG=$FIFO_UEBERZUG" \ - --env "ICONLOOKUP=$ICONLOOKUP" --env "NNN_PREVIEWHEIGHT=$NNN_PREVIEWHEIGHT" \ - --env "NNN_PREVIEWWIDTH=$NNN_PREVIEWWIDTH" --env "NNN_PREVIEWDIR=$NNN_PREVIEWDIR" \ - --env "USE_PISTOL=$USE_PISTOL" --env "BAT_STYLE=$BAT_STYLE" \ - --env "BAT_THEME=$BAT_THEME" --env "FIFOPID=$FIFOPID" --env TTY="$TTY" \ - --env "CURSEL=$CURSEL" --location "${SPLIT}split" "$0" "$1" ;; + # Trying to use kitty's integrated window management as the split window. + kitty @ launch --no-response --title "preview-tui" --keep-focus \ + --cwd "$PWD" "${ENVVARS[@]}" --location "${NNN_SPLIT}split" "$0" "$1" ;; + wezterm) + export "${ENVVARS[@]}" + if [ "$NNN_SPLIT" = "v" ]; then split="--horizontal"; else split="--bottom"; fi + wezterm cli split-pane --cwd "$PWD" $split --percent "$NNN_SPLITSIZE" "$0" "$1" >/dev/null + wezterm cli activate-pane-direction Prev ;; + wezterm.exe) + if [ "$NNN_SPLIT" = "v" ]; then split="--horizontal"; else split="--bottom"; fi + wezterm.exe cli split-pane $split --percent "$NNN_SPLITSIZE" "wsl.exe" --cd "$PWD" -e env "${ENVVARS[@]}" "$0" "$1" >/dev/null + wezterm.exe cli activate-pane-direction Prev ;; iterm) - command="$SHELL -c 'cd $PWD; \ - PATH=\\\"$PATH\\\" NNN_FIFO=\\\"$NNN_FIFO\\\" PREVIEW_MODE=1 PAGER=\\\"$PAGER\\\" \ - USE_SCOPE=\\\"$USE_SCOPE\\\" SPLIT=\\\"$SPLIT\\\" TERMINAL=\\\"$TERMINAL\\\" \ - PREVIEWPID=\\\"$PREVIEWPID\\\" CURSEL=\\\"$CURSEL\\\" TMPDIR=\\\"$TMPDIR\\\" \ - ICONLOOKUP=\\\"$ICONLOOKUP\\\" NNN_PREVIEWHEIGHT=\\\"$NNN_PREVIEWHEIGHT\\\" \ - NNN_PREVIEWWIDTH=\\\"$NNN_PREVIEWWIDTH\\\" NNN_PREVIEWDIR=\\\"$NNN_PREVIEWDIR\\\" \ - USE_PISTOL=\\\"$USE_PISTOL\\\" BAT_STYLE=\\\"$BAT_STYLE\\\" TTY=\\\"$TTY\\\" \ - BAT_THEME=\\\"$BAT_THEME\\\" FIFOPID=\\\"$FIFOPID\\\" \\\"$0\\\" \\\"$1\\\"'" - if [ "$SPLIT" = "h" ]; then split="horizontally"; else split="vertically"; fi + if [ "$NNN_SPLIT" = "h" ]; then split="horizontally"; else split="vertically"; fi osascript <<-EOF tell application "iTerm" tell current session of current window @@ -160,33 +224,27 @@ start_preview() { EOF ;; winterm) - if [ "$SPLIT" = "h" ]; then split="H"; else split="V"; fi - cmd.exe /c wt -w 0 sp -$split bash -c "cd $PWD \; PATH='$PATH' NNN_FIFO=$NNN_FIFO \ - PREVIEW_MODE=1 TTY=$TTY CURSEL=$CURSEL TMPDIR=$TMPDIR FIFOPID=$FIFOPID \ - BAT_STYLE=$BAT_STYLE BAT_THEME=$BAT_THEME PREVIEWPID=$PREVIEWPID \ - PAGER='$PAGER' ICONLOOKUP=$ICONLOOKUP NNN_PREVIEWWIDTH=$NNN_PREVIEWWIDTH \ - USE_SCOPE=$USE_SCOPE SPLIT=$SPLIT USE_PISTOL=$USE_PISTOL \ - NNN_PREVIEWDIR=$NNN_PREVIEWDIR NNN_PREVIEWHEIGHT=$NNN_PREVIEWHEIGHT \ - FIFO_UEBERZUG=$FIFO_UEBERZUG QLPATH=$2 $0 $1" \; -w 0 mf previous - ;; + if [ "$NNN_SPLIT" = "h" ]; then split="H"; else split="V"; fi + wt -w 0 sp -$split -s"0.$NNN_SPLITSIZE" "$command" \; -w 0 mf previous 2>/dev/null ;; *) if [ -n "$2" ]; then - QUICKLOOK=1 QLPATH="$2" PREVIEW_MODE=1 "$0" "$1" & + env "${ENVVARS[@]}" QUICKLOOK=1 "$0" "$1" & else - PREVIEWPID="$PREVIEWPID" CURSEL="$CURSEL" PREVIEW_MODE=1 TTY="$TTY" \ - FIFOPID="$FIFOPID" FIFO_UEBERZUG="$FIFO_UEBERZUG" $TERMINAL -e "$0" "$1" & + # shellcheck disable=SC2086 # (allow arguments) + env "${ENVVARS[@]}" $NNN_TERMINAL -e "$0" "$1" & fi ;; esac -} >/dev/null 2>&1 +} toggle_preview() { + export "${ENVVARS[@]}" if exists QuickLook.exe; then QLPATH="QuickLook.exe" elif exists Bridge.exe; then QLPATH="Bridge.exe" fi - if kill "$(cat "$FIFOPID")"; then + if pidkill "$FIFOPID"; then [ -p "$NNN_PPIPE" ] && printf "0" > "$NNN_PPIPE" - kill "$(cat "$PREVIEWPID")" + pidkill "$PREVIEWPID" pkill -f "tail --follow $FIFO_UEBERZUG" if [ -n "$QLPATH" ] && stat "$1"; then f="$(wslpath -w "$1")" && "$QLPATH" "$f" & @@ -195,39 +253,34 @@ toggle_preview() { [ -p "$NNN_PPIPE" ] && printf "1" > "$NNN_PPIPE" start_preview "$1" "$QLPATH" fi -} >/dev/null 2>&1 - -exists() { - type "$1" >/dev/null } fifo_pager() { cmd="$1" shift - # We use a FIFO to access $PAGER PID in jobs control - tmpfifopath="$TMPDIR/nnn-preview-tui-fifo.$$" - mkfifo "$tmpfifopath" || return + # We use a FIFO to access $NNN_PAGER PID in jobs control + mkfifo "$FIFOPATH" || return - $PAGER < "$tmpfifopath" & + $NNN_PAGER < "$FIFOPATH" & printf "%s" "$!" > "$PREVIEWPID" ( - exec > "$tmpfifopath" + exec > "$FIFOPATH" if [ "$cmd" = "pager" ]; then if exists bat; then - bat --terminal-width="$(tput cols <"$TTY")" --decorations=always --color=always \ - --paging=never --style="$BAT_STYLE" --theme="$BAT_THEME" "$@" & + bat --terminal-width="$cols" --decorations=always --color=always \ + --paging=never --style="$NNN_BATSTYLE" --theme="$NNN_BATTHEME" "$@" & else - $PAGER "$@" & + $NNN_PAGER "$@" & fi else "$cmd" "$@" & fi ) - rm "$tmpfifopath" -} 2>/dev/null + rm -- "$FIFOPATH" +} # Binary file: show file info inside the pager print_bin_info() { @@ -237,17 +290,18 @@ print_bin_info() { else file -b "$1" fi -} 2>/dev/null +} handle_mime() { case "$2" in image/jpeg) image_preview "$cols" "$lines" "$1" ;; image/gif) generate_preview "$cols" "$lines" "$1" "gif" ;; + image/vnd.djvu) generate_preview "$cols" "$lines" "$1" "djvu" ;; image/*) generate_preview "$cols" "$lines" "$1" "image" ;; video/*) generate_preview "$cols" "$lines" "$1" "video" ;; audio/*) generate_preview "$cols" "$lines" "$1" "audio" ;; application/font*|application/*opentype|font/*) generate_preview "$cols" "$lines" "$1" "font" ;; - */*office*|*/*document*) generate_preview "$cols" "$lines" "$1" "office" ;; + */*office*|*/*document*|*/*msword|*/*ms-excel) generate_preview "$cols" "$lines" "$1" "office" ;; application/zip) fifo_pager unzip -l "$1" ;; text/troff) if exists man; then @@ -288,13 +342,6 @@ handle_ext() { elif exists bsdtar; then fifo_pager bsdtar -tvf "$1" fi ;; - ipynb) - if exists jupyter; then - fifo_pager eval "jupyter nbconvert --stdout --to markdown --MarkdownExporter.exclude_output=True \"$1\" | pygmentize -l md" - fi ;; - textbundle|orgbundle) - fifo_pager eval "echo HERE" - ;; *) if [ "$3" = "bin" ]; then fifo_pager print_bin_info "$1" else @@ -306,13 +353,13 @@ handle_ext() { preview_file() { clear # Trying to use pistol if it's available. - if [ "$USE_PISTOL" -ne 0 ] && exists pistol; then + if [ "$NNN_PISTOL" -ne 0 ] && exists pistol; then fifo_pager pistol "$1" return fi # Trying to use scope.sh if it's available. - if [ "$USE_SCOPE" -ne 0 ] && exists scope.sh; then + if [ "$NNN_SCOPE" -ne 0 ] && exists scope.sh; then fifo_pager scope.sh "$1" "$cols" "$lines" "$(mktemp -d)" "True" return fi @@ -328,55 +375,45 @@ preview_file() { mimetype="$(file -bL --mime-type -- "$1")" ext="${1##*.}" [ -n "$ext" ] && ext="$(printf "%s" "${ext}" | tr '[:upper:]' '[:lower:]')" - lines=$(tput lines <"$TTY") - cols=$(tput cols <"$TTY") + lines=$(tput lines) + cols=$(tput cols) # Otherwise, falling back to the defaults. if [ -d "$1" ]; then cd "$1" || return - if [ "$ext" = "textbundle" ] || [ "$ext" = "orgbundle" ]; then - for ext in org md markdown adoc; do - if [ -e "text.$ext" ]; then - preview_file "text.$ext" - break - fi - done - elif [ "$ICONLOOKUP" -ne 0 ] && [ -f "$(dirname "$0")"/.iconlookup ]; then - [ "$SPLIT" = v ] && BSTR="\n" + if [ "$NNN_ICONLOOKUP" -ne 0 ] && [ -f "$(dirname "$0")"/.iconlookup ]; then + [ "$NNN_SPLIT" = v ] && BSTR="\n" # shellcheck disable=SC2012 ls -F --group-directories-first | head -n "$((lines - 3))" | "$(dirname "$0")"/.iconlookup -l "$cols" -B "$BSTR" -b " " elif exists tree; then fifo_pager tree --filelimit "$(find . -maxdepth 1 | wc -l)" -L 3 -C -F --dirsfirst --noreport elif exists exa; then - exa -G --group-directories-first --colour=always + fifo_pager exa -T --group-directories-first --colour=always -L 3 + elif exists eza; then # eza is a community fork of exa (exa is unmaintained) + fifo_pager eza -T --group-directories-first --colour=always -L 3 else fifo_pager ls -F --group-directories-first --color=always fi + cd .. elif [ "${encoding#*)}" = "binary" ]; then handle_mime "$1" "$mimetype" "$ext" "bin" else handle_mime "$1" "$mimetype" "$ext" fi -} 2>/dev/null +} generate_preview() { if [ -n "$QLPATH" ] && stat "$3"; then f="$(wslpath -w "$3")" && "$QLPATH" "$f" & - elif [ ! -f "$NNN_PREVIEWDIR/$3.(jpg|png)" ] || [ -n "$(find -L "$3" -newer "$NNN_PREVIEWDIR/$3.(jpg|png)")" ]; then + elif [ -n "$NNN_PREVIEWVIDEO" ] && [[ "$4" == +(gif|video) ]]; then + [ "$4" = "video" ] && args=(--start=10% --length=4) || args=() + video_preview "$1" "$2" "$3" "${args[@]}" && return + elif [ ! -f "$NNN_PREVIEWDIR/$3.jpg" ] || [ -n "$(find -L "$3" -newer "$NNN_PREVIEWDIR/$3.jpg")" ]; then mkdir -p "$NNN_PREVIEWDIR/${3%/*}" case $4 in audio) ffmpeg -i "$3" -filter_complex "scale=iw*min(1\,min($NNN_PREVIEWWIDTH/iw\,ih)):-1" "$NNN_PREVIEWDIR/$3.jpg" -y ;; epub) gnome-epub-thumbnailer "$3" "$NNN_PREVIEWDIR/$3.jpg" ;; - font) - # convert -size 532X365 xc:"#ffffff" \ - # -gravity center \ - # -pointsize 42 \ - # -fill "#000000" \ - # -font "$3" \ - # -annotate +0+0 "ABCDEFGHIJKLM\nNOPQRSTUVWXYZ\nabcdefghijklm\nnopqrstuvwxyz\n1234567890\n!@$\%(){}[]" \ - # -flatten "$NNN_PREVIEWDIR/$3.png" - hb-view -o "$NNN_PREVIEWDIR/$3.png" "$3" "FooBar" - ;; + font) fontpreview -i "$3" -o "$NNN_PREVIEWDIR/$3.jpg" ;; gif) if [ -p "$FIFO_UEBERZUG" ] && exists convert; then frameprefix="$NNN_PREVIEWDIR/$3/${3##*/}" if [ ! -d "$NNN_PREVIEWDIR/$3" ]; then @@ -394,51 +431,71 @@ generate_preview() { done & printf "%s" "$!" > "$PREVIEWPID" return + elif [ -n "$NNN_PREVIEWVIDEO" ]; then + video_preview "$1" "$2" "$3" && return else - exec >/dev/tty - image_preview "$1" "$2" "$3" - return + image_preview "$1" "$2" "$3" && return fi ;; - image) if exists convert; then + image) if exists rsvg-convert && [[ "${3##*.}" == "svg" ]]; then + rsvg-convert -a -w "$NNN_PREVIEWWIDTH" -h "$NNN_PREVIEWHEIGHT" -f png -o "$NNN_PREVIEWDIR/$3.png" "$3" + elif exists convert; then convert "$3" -flatten -resize "$NNN_PREVIEWWIDTH"x"$NNN_PREVIEWHEIGHT"\> "$NNN_PREVIEWDIR/$3.jpg" else image_preview "$1" "$2" "$3" && return fi ;; office) libreoffice --convert-to jpg "$3" --outdir "$NNN_PREVIEWDIR/${3%/*}" filename="$(printf "%s" "${3##*/}" | cut -d. -f1)" - mv "$NNN_PREVIEWDIR/${3%/*}/$filename.jpg" "$NNN_PREVIEWDIR/$3.jpg" ;; + mv -- "$NNN_PREVIEWDIR/${3%/*}/$filename.jpg" "$NNN_PREVIEWDIR/$3.jpg" ;; pdf) pdftoppm -jpeg -f 1 -singlefile "$3" "$NNN_PREVIEWDIR/$3" ;; - video) ffmpegthumbnailer -s0 -i "$3" -o "$NNN_PREVIEWDIR/$3.jpg" || rm "$NNN_PREVIEWDIR/$3.jpg" ;; + djvu) ddjvu -format=ppm -page=1 "$3" "$NNN_PREVIEWDIR/$3.jpg" ;; + video) video_preview "$1" "$2" "$3" && return ;; esac - fi >/dev/null + fi if [ -f "$NNN_PREVIEWDIR/$3.jpg" ]; then image_preview "$1" "$2" "$NNN_PREVIEWDIR/$3.jpg" - elif [ -f "$NNN_PREVIEWDIR/$3.png" ]; then + elif [[ "${3##*.}" == "svg" ]] && [ -f "$NNN_PREVIEWDIR/$3.png" ]; then image_preview "$1" "$2" "$NNN_PREVIEWDIR/$3.png" else fifo_pager print_bin_info "$3" fi -} 2>/dev/null +} >/dev/null 2>&1 image_preview() { clear - if [ "$TERMINAL" = "kitty" ]; then - # Kitty terminal users can use the native image preview method - kitty +kitten icat --silent --place "$1"x"$2"@0x0 --transfer-mode=stream --stdin=no "$3" & - elif exists ueberzug; then + exec >/dev/tty + if [ "$NNN_TERMINAL" = "kitty" ] && [[ "$NNN_PREVIEWIMGPROG" == +(|icat) ]]; then + kitty +kitten icat --silent --scale-up --place "$1"x"$2"@0x0 --transfer-mode=stream --stdin=no "$3" & + elif [ "$NNN_TERMINAL" = "tmux" ] && [[ -n "$KITTY_LISTEN_ON" ]] && [[ "$NNN_PREVIEWIMGPROG" == +(|icat) ]]; then + kitty +kitten icat --silent --scale-up --place "$(($1 - 1))x$(($2 - 1))"@0x0 --transfer-mode=memory --stdin=no "$3" & + elif [ "$NNN_TERMINAL" = "wezterm" ] && [[ "$NNN_PREVIEWIMGPROG" == +(|imgcat) ]]; then + wezterm imgcat "$3" & + elif exists ueberzug && [[ "$NNN_PREVIEWIMGPROG" == +(|ueberzug) ]]; then ueberzug_layer "$1" "$2" "$3" && return - elif exists catimg; then - catimg "$3" & - elif exists viu; then - viu -t "$3" & + elif [ "$NNN_TERMINAL" = "wezterm.exe" ] && exists "${NNN_PREVIEWIMGPROG%% *}"; then + $NNN_PREVIEWIMGPROG "$3" && return + elif exists "${NNN_PREVIEWIMGPROG%% *}"; then # can include command flags; only check first word + "$NNN_PREVIEWIMGPROG" "$3" & else fifo_pager print_bin_info "$3" && return fi printf "%s" "$!" > "$PREVIEWPID" -} 2>/dev/null +} + +video_preview() { + clear + exec >/dev/tty + if [ -n "$NNN_PREVIEWVIDEO" ]; then + mpv --no-config --really-quiet --vo="$NNN_PREVIEWVIDEO" --profile=sw-fast --loop-file --no-audio "$4" "$3" & + else + ffmpegthumbnailer -m -s0 -i "$3" -o "$NNN_PREVIEWDIR/$3.jpg" || rm -- "$NNN_PREVIEWDIR/$3.jpg" & + fi + printf "%s" "$!" > "$PREVIEWPID" +} ueberzug_layer() { - printf '{"action": "add", "identifier": "nnn_ueberzug", "x": 0, "y": 0, "width": "%d", "height": "%d", "scaler": "fit_contain", "path": "%s"}\n' "$1" "$2" "$3" > "$FIFO_UEBERZUG" + [ -f "$POSOFFSET" ] && read -r x y < "$POSOFFSET" + printf '{"action": "add", "identifier": "nnn_ueberzug", "x": %d, "y": %d, "width": "%d", "height": "%d", "scaler": "fit_contain", "path": "%s"}\n'\ + "${x:-0}" "${y:-0}" "$1" "$2" "$3" > "$FIFO_UEBERZUG" } ueberzug_remove() { @@ -447,18 +504,18 @@ ueberzug_remove() { winch_handler() { clear - kill "$(cat "$PREVIEWPID")" + pidkill "$PREVIEWPID" if [ -p "$FIFO_UEBERZUG" ]; then pkill -f "tail --follow $FIFO_UEBERZUG" tail --follow "$FIFO_UEBERZUG" | ueberzug layer --silent --parser json & fi preview_file "$(cat "$CURSEL")" -} 2>/dev/null +} preview_fifo() { while read -r selection; do if [ -n "$selection" ]; then - kill "$(cat "$PREVIEWPID")" + pidkill "$PREVIEWPID" [ -p "$FIFO_UEBERZUG" ] && ueberzug_remove [ "$selection" = "close" ] && break preview_file "$selection" @@ -467,33 +524,30 @@ preview_fifo() { done < "$NNN_FIFO" sleep 0.1 # make sure potential preview by winch_handler is killed pkill -P "$$" -} 2>/dev/null +} -if [ "$PREVIEW_MODE" ]; then - if [ "$TERMINAL" != "kitty" ] && exists ueberzug; then +if [ "$PREVIEW_MODE" -eq 1 ] 2>/dev/null; then + if exists ueberzug && [ "$NNN_TERMINAL" != "kitty" ] && [[ "$NNN_PREVIEWIMGPROG" == +(|ueberzug) ]]; then mkfifo "$FIFO_UEBERZUG" tail --follow "$FIFO_UEBERZUG" | ueberzug layer --silent --parser json & fi preview_file "$PWD/$1" - preview_fifo & + preview_fifo & WAITPID=$! printf "%s" "$!" > "$FIFOPID" printf "%s" "$PWD/$1" > "$CURSEL" - trap 'winch_handler; wait' WINCH - trap 'rm "$PREVIEWPID" "$CURSEL" "$FIFO_UEBERZUG" "$FIFOPID" 2>/dev/null' INT HUP EXIT - wait "$!" 2>/dev/null + trap 'winch_handler' WINCH + trap 'rm -- "$PREVIEWPID" "$CURSEL" "$FIFO_UEBERZUG" "$FIFOPID" "$POSOFFSET" 2>/dev/null' INT HUP EXIT + while kill -s 0 $WAITPID; do + wait $WAITPID 2>/dev/null + done exit 0 else if [ ! -r "$NNN_FIFO" ]; then - clear - printf "No FIFO available! (\$NNN_FIFO='%s')\nPlease read Usage in preview-tui." "$NNN_FIFO" - cfg=$(stty -g); stty raw -echo; head -c 1; stty "$cfg" + prompt "No FIFO available! (\$NNN_FIFO='$NNN_FIFO')\nPlease read Usage in '$0'." elif [ "$KITTY_WINDOW_ID" ] && [ -z "$TMUX" ] && [ -z "$KITTY_LISTEN_ON" ]; then - clear - printf "\$KITTY_LISTEN_ON not set!\nPlease read Usage in preview-tui." - cfg=$(stty -g); stty raw -echo; head -c 1; stty "$cfg" + prompt "\$KITTY_LISTEN_ON not set!\nPlease read Usage in '$0'." else - TTY="$(tty)" - TTY="$TTY" toggle_preview "$1" & + toggle_preview "$1" & fi fi diff --git a/shells/zshrc b/shells/zshrc index b98b146..6326742 100644 --- a/shells/zshrc +++ b/shells/zshrc @@ -445,6 +445,43 @@ if [[ $- == *i* ]]; then export WINHOME=$(wslpath -u $(cmd.exe /c "echo %USERPROFILE%" 2>&1 | tail -n 1 | tr -d '\r')) ## }}} + ## More robust terminal image + if [ "$TERM_PROGRAM" = "WezTerm" ]; then + if hash timg 2>/dev/null; then + function timgcat() { + if [ $# -eq 0 ]; then + echo "No argument provided. Exiting ..." + exit 1 + fi + + timg -p i "$@" + + printf "\rPress 'q' to exit." + + # Save current terminal settings + old_stty_cfg=$(stty -g) + + # Ensure terminal settings are restored on exit + trap 'stty "$old_stty_cfg"' EXIT + + # Set terminal to raw mode + stty raw -echo + + while : ; do + key=$(dd bs=1 count=1 2>/dev/null) + if [ "$key" = 'q' ]; then + break + fi + done + stty "$old_stty_cfg" + printf "\n\033[2K\r" + } + if hash nnn 2>/dev/null; then + export NNN_PREVIEWIMGPROG="timg -p i" + fi + fi + fi + fi # }}}