summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2023-11-07 11:42:32 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2023-11-07 11:51:21 +0900
commit230fc49ae216169f9812adcf8942bba3993e61e0 (patch)
tree381b3739db998a35e3a8b276c59c9b7c11f477b8
parent250d507bdfe370045a32c8cd85fc2b4f34c8df5c (diff)
(Experimental) Add support for iTerm2 inline image protocol
Close #1102 fzf --preview 'imgcat -W $FZF_PREVIEW_COLUMNS -H $FZF_PREVIEW_LINES {}' Notes: * There is no good way to determine the height of the rendered image, so we assume that the image takes the full height of the preview window. So the image cannot be displayed with the other text. * fzf-preview.sh script was updated to use `imgcat` if it's available but `chafa` is not. * iTerm2 also supports Sixel, so adding support for this protocol is not quite necessary but it renders animated GIFs much better (e.g. looping).
-rw-r--r--CHANGELOG.md7
-rwxr-xr-xbin/fzf-preview.sh15
-rw-r--r--src/terminal.go30
3 files changed, 39 insertions, 13 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a02e12cd..0a041f8d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,7 +10,12 @@ CHANGELOG
```sh
fzf --preview='fzf-preview.sh {}'
```
-- (Experimental) Sixel and Kitty image support now also available on Windows
+- (Experimental) iTerm2 inline image protocol support in preview window
+ ```sh
+ # Using https://iterm2.com/utilities/imgcat
+ fzf --preview 'imgcat -W $FZF_PREVIEW_COLUMNS -H $FZF_PREVIEW_LINES {}'
+ ```
+- (Experimental) Sixel, Kitty, and iTerm2 image support now also available on Windows
- HTTP server can be configured to accept remote connections
```sh
# FZF_API_KEY is required for a non-localhost listen address
diff --git a/bin/fzf-preview.sh b/bin/fzf-preview.sh
index d72cd2d4..8991e3de 100755
--- a/bin/fzf-preview.sh
+++ b/bin/fzf-preview.sh
@@ -4,8 +4,9 @@
# image in the preview window of fzf.
#
# Dependencies:
-# - https://github.com/hpjansson/chafa
# - https://github.com/sharkdp/bat
+# - https://github.com/hpjansson/chafa
+# - https://iterm2.com/utilities/imgcat
if [[ $# -ne 1 ]]; then
>&2 echo "usage: $0 FILENAME"
@@ -44,6 +45,7 @@ elif ! [[ $KITTY_WINDOW_ID ]] && (( FZF_PREVIEW_TOP + FZF_PREVIEW_LINES == $(stt
dim=${FZF_PREVIEW_COLUMNS}x$((FZF_PREVIEW_LINES - 1))
fi
+# 1. Use kitty icat on kitty terminal
if [[ $KITTY_WINDOW_ID ]]; then
# 1. 'memory' is the fastest option but if you want the image to be scrollable,
# you have to use 'stream'.
@@ -52,10 +54,21 @@ if [[ $KITTY_WINDOW_ID ]]; then
# This confuses fzf and makes it render scroll offset indicator.
# So we remove the last line and append the reset code to its previous line.
kitty icat --clear --transfer-mode=memory --stdin=no --place="$dim@0x0" "$file" | sed '$d' | sed $'$s/$/\e[m/'
+
+# 2. Use chafa with Sixel output
elif command -v chafa > /dev/null; then
chafa -f sixel -s "$dim" "$file"
# Add a new line character so that fzf can display multiple images in the preview window
echo
+
+# 3. If chafa is not found but imgcat is available, use it on iTerm2
+elif command -v imgcat > /dev/null; then
+ # NOTE: We should use https://iterm2.com/utilities/it2check to check if the
+ # user is running iTerm2. But for the sake of simplicty, we just assume
+ # that's the case here.
+ imgcat -W "${dim%%x*}" -H "${dim##*x}" "$file"
+
+# 4. Cannot find any suitable method to preview the image
else
file "$file"
fi
diff --git a/src/terminal.go b/src/terminal.go
index 747001ef..f48e5f25 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -66,7 +66,8 @@ func init() {
// * https://github.com/tmux/tmux/wiki/FAQ#what-is-the-passthrough-escape-sequence-and-how-do-i-use-it
// * https://sw.kovidgoyal.net/kitty/graphics-protocol
// * https://en.wikipedia.org/wiki/Sixel
- passThroughRegex = regexp.MustCompile(`\x1bPtmux;\x1b\x1b.*?[^\x1b]\x1b\\|\x1b(_G|P[0-9;]*q).*?\x1b\\\r?`)
+ // * https://iterm2.com/documentation-images.html
+ passThroughRegex = regexp.MustCompile(`\x1bPtmux;\x1b\x1b.*?[^\x1b]\x1b\\|\x1b(_G|P[0-9;]*q).*?\x1b\\\r?|\x1b]1337;.*?\a`)
}
type jumpMode int
@@ -125,7 +126,7 @@ type previewed struct {
numLines int
offset int
filled bool
- sixel bool
+ image bool
wipe bool
wireframe bool
}
@@ -2027,7 +2028,7 @@ func (t *Terminal) renderPreviewText(height int, lines []string, lineNo int, unc
var ansi *ansiState
spinnerRedraw := t.pwindow.Y() == 0
wiped := false
- sixel := false
+ image := false
wireframe := false
Loop:
for _, line := range lines {
@@ -2055,18 +2056,25 @@ Loop:
t.pwindow.Move(y, x)
}
for idx, passThrough := range passThroughs {
- // Handling Sixel output
+ // Handling Sixel/iTerm image
requiredLines := 0
isSixel := strings.HasPrefix(passThrough, "\x1bP")
- if isSixel {
+ isItermImage := strings.HasPrefix(passThrough, "\x1b]1337;")
+ isImage := isSixel || isItermImage
+ if isImage {
t.previewed.wipe = true
- if t.termSize.PxHeight > 0 {
+ // NOTE: We don't have a good way to get the height of an iTerm image,
+ // so we assume that it requires the full height of the preview
+ // window.
+ requiredLines = height
+
+ if isSixel && t.termSize.PxHeight > 0 {
rows := strings.Count(passThrough, "-")
requiredLines = int(math.Ceil(float64(rows*6*t.termSize.Lines) / float64(t.termSize.PxHeight)))
}
}
- // Render wireframe when Sixel image cannot be displayed entirely
+ // Render wireframe when the image cannot be displayed entirely
if requiredLines > 0 && y+requiredLines > height {
top := true
for ; y < height; y++ {
@@ -2081,17 +2089,17 @@ Loop:
}
// Clear previous wireframe or any other text
- if (t.previewed.wireframe || isSixel && !t.previewed.sixel) && !wiped {
+ if (t.previewed.wireframe || isImage && !t.previewed.image) && !wiped {
wiped = true
for i := y + 1; i < height; i++ {
t.pwindow.MoveAndClear(i, 0)
}
// Required for tcell to clear the previous text
- if !t.previewed.sixel {
+ if !t.previewed.image {
t.tui.Sync(false)
}
}
- sixel = sixel || isSixel
+ image = image || isImage
if idx == 0 {
t.pwindow.MoveAndClear(y, x)
} else {
@@ -2154,7 +2162,7 @@ Loop:
}
lineNo++
}
- t.previewed.sixel = sixel
+ t.previewed.image = image
t.previewed.wireframe = wireframe
}