#!/usr/bin/env bash # Universal file preview — used by fzf widgets and as a standalone command. # Usage: preview [--zen] [line] zen=false if [[ "$1" == "--zen" ]]; then zen=true shift fi file="${1:?usage: preview [--zen] [line]}" line="${2:-}" if [[ ! -e "$file" ]]; then echo "not found: $file" exit 1 fi # Directory preview if [[ -d "$file" ]]; then if command -v eza &>/dev/null; then eza --tree --level=2 --icons --color=always "$file" else ls -1A "$file" fi exit 0 fi # Standalone mode: full terminal, can page output # fzf mode: constrained to preview pane dimensions # Check both the env var AND that stdout is not a TTY — execute() inherits # FZF_PREVIEW_COLUMNS but redirects stdout to /dev/tty. is_fzf() { [[ -n "${FZF_PREVIEW_COLUMNS:-}" && ! -t 1 ]]; } preview_image() { local w h if is_fzf; then w="$FZF_PREVIEW_COLUMNS" h="$FZF_PREVIEW_LINES" else w="$(tput cols)" h="$(tput lines)" fi # iTerm2/WezTerm: use native inline image protocol (works through tmux passthrough) if [[ "${LC_TERMINAL:-}${TERM_PROGRAM:-}" == *iTerm* || "${LC_TERMINAL:-}${TERM_PROGRAM:-}" == *WezTerm* ]]; then local data data="$(base64 < "$file")" if [[ -n "${TMUX:-}" ]]; then printf '\033Ptmux;\033\033]1337;File=inline=1;width=%s;height=%s;preserveAspectRatio=1:%s\a\033\\' \ "$w" "$h" "$data" else printf '\033]1337;File=inline=1;width=%s;height=%s;preserveAspectRatio=1:%s\a' \ "$w" "$h" "$data" fi if ! is_fzf; then printf '\n[press any key]' read -rsn1 /dev/null; then local style_args=(-s dark) [[ -f "$glow_style" ]] && style_args=(-s "$glow_style") if is_fzf; then script -q /dev/null bash -c \ 'COLORTERM=truecolor glow "$@"' _ "${style_args[@]}" -w "${FZF_PREVIEW_COLUMNS:-80}" "$file" \ 2>/dev/null | tr -d '\r' else local tmp term_w render_w tmp=$(mktemp) # Hide tmux chrome in zen mode if [[ "$zen" == true && -n "${TMUX:-}" ]]; then tmux set status off tmux set pane-border-status off 2>/dev/null trap 'tmux set status on; tmux set pane-border-status off 2>/dev/null; rm -f "$tmp"' EXIT else trap 'rm -f "$tmp"' EXIT fi term_w="$(tput cols)" render_w="$term_w" if [[ "$zen" == true ]] && (( term_w > 104 )); then render_w=100 fi local pad=$(( (term_w - render_w) / 2 )) local glow_input="$file" if [[ "$zen" == true ]]; then # Pre-process: replace link URLs with dummy anchors (keeps link styling) # and strip bare URLs glow_input=$(mktemp).md perl -pe ' s/\[([^\]]+)\]\([^\)]+\)/[$1](#)/g; s/\s*?//g; ' "$file" > "$glow_input" fi script -q /dev/null bash -c \ 'COLORTERM=truecolor glow "$@"' _ "${style_args[@]}" -w "$render_w" "$glow_input" 2>/dev/null \ | tr -d '\r' \ | if (( pad > 0 )); then sed "s/^/$(printf '%*s' "$pad" '')/"; else cat; fi \ > "$tmp" [[ "$glow_input" != "$file" ]] && rm -f "$glow_input" LESS='-R --mouse' less < "$tmp" fi elif is_fzf; then bat --color=always --style=numbers ${line:+--highlight-line="$line"} "$file" else bat --paging=always --color=always --style=numbers "$file" fi } preview_archive() { local lower_file lower_file="$(printf '%s' "$file" | tr '[:upper:]' '[:lower:]')" local output case "$lower_file" in *.tar|*.tar.gz|*.tgz|*.tar.bz2|*.tbz2|*.tar.xz|*.txz) output="$(tar -tf "$file" | head -80)" ;; *.zip|*.jar|*.war) output="$(unzip -l "$file" | head -80)" ;; *.gz) output="$(gzip -l "$file")" ;; *) output="$(file "$file")" ;; esac if is_fzf; then printf '%s\n' "$output" else printf '%s\n' "$output" | bat --paging=always --style=plain --language=txt fi } preview_text() { local bat_args=(--color=always --style=numbers) if [[ -n "$line" ]]; then bat_args+=(--highlight-line="$line") if is_fzf; then local start=$((line > 30 ? line - 30 : 1)) bat_args+=(--line-range="$start:$((line + 30))") fi elif is_fzf; then bat_args+=(--line-range=:100) fi if ! is_fzf; then bat_args+=(--paging=always) fi bat "${bat_args[@]}" "$file" 2>/dev/null || head -100 "$file" } preview_binary() { local output output="$(file "$file"; echo ""; hexdump -C "$file" | head -40)" if is_fzf; then printf '%s\n' "$output" else printf '%s\n' "$output" | bat --paging=always --style=plain --language=txt fi } # Route by extension ext="${file##*.}" ext="$(printf '%s' "$ext" | tr '[:upper:]' '[:lower:]')" case "$ext" in png|jpg|jpeg|gif|webp|bmp|svg|ico|tiff|tif|avif) if command -v chafa &>/dev/null; then preview_image else file "$file" fi ;; pdf) if command -v pdftotext &>/dev/null; then preview_pdf else file "$file" fi ;; md|markdown|mdx) preview_markdown ;; tar|gz|tgz|bz2|tbz2|xz|txz|zip|jar|war) preview_archive ;; *) # MIME fallback for extensionless or ambiguous files mime="$(file --mime-type -b "$file" 2>/dev/null || echo "unknown")" case "$mime" in image/*) if command -v chafa &>/dev/null; then preview_image else file "$file" fi ;; application/pdf) if command -v pdftotext &>/dev/null; then preview_pdf else file "$file" fi ;; text/*|application/json|application/xml|application/javascript|*/x-shellscript) preview_text ;; *) preview_binary ;; esac ;; esac