Refactor clipboard management and graphics query scripts
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -36,7 +36,7 @@ binds {
|
|||||||
Alt+Space { spawn-sh "pkill -x rofi || rofi -show drun"; }
|
Alt+Space { spawn-sh "pkill -x rofi || rofi -show drun"; }
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
Mod+V { spawn "shorinclip-wrap"; }
|
Mod+V { spawn-sh "fzfclip-wrap"; }
|
||||||
Mod+Period { spawn-sh "pkill -x rofi || rofi-emoji"; }
|
Mod+Period { spawn-sh "pkill -x rofi || rofi-emoji"; }
|
||||||
Ctrl+Alt+Delete { spawn-sh "pkill -x wlogout || wlogout -p layer-shell"; }
|
Ctrl+Alt+Delete { spawn-sh "pkill -x wlogout || wlogout -p layer-shell"; }
|
||||||
Print { spawn "niri" "msg" "action" "screenshot-screen"; }
|
Print { spawn "niri" "msg" "action" "screenshot-screen"; }
|
||||||
|
|||||||
@@ -13,8 +13,6 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
trap 'echo $LINENO: $BASH_COMMAND' ERR
|
|
||||||
|
|
||||||
# Lock
|
# Lock
|
||||||
|
|
||||||
exec {LOCK_FD}>/tmp/"$(basename "$0")".lock
|
exec {LOCK_FD}>/tmp/"$(basename "$0")".lock
|
||||||
|
|||||||
Executable
+296
@@ -0,0 +1,296 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
|
||||||
|
# Description:
|
||||||
|
# View and manage clipboard history using fzf, with support for
|
||||||
|
# image previews in compatible terminals.
|
||||||
|
# Requirements:
|
||||||
|
# - cliphist
|
||||||
|
# - wl-clipboard
|
||||||
|
# - fzf
|
||||||
|
# - sixel-query & kgp-query from this repository
|
||||||
|
# - chafa (for image previews)
|
||||||
|
# - ffmpegthumbnailer (optional, for video thumbnails)
|
||||||
|
# Credits:
|
||||||
|
# - Original idea and some code adapted from https://github.com/SHORiN-KiWATA/shorinclip
|
||||||
|
# LICENSE:
|
||||||
|
# # MIT License
|
||||||
|
# #
|
||||||
|
# # Copyright (c) 2026 shorinkiwata
|
||||||
|
# #
|
||||||
|
# # Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# # of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# # in the Software without restriction, including without limitation the rights
|
||||||
|
# # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# # copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# # furnished to do so, subject to the following conditions:
|
||||||
|
# #
|
||||||
|
# # The above copyright notice and this permission notice shall be included in all
|
||||||
|
# # copies or substantial portions of the Software.
|
||||||
|
# #
|
||||||
|
# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# # SOFTWARE.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CACHE_DIR=$(mktemp -d)
|
||||||
|
export CACHE_DIR
|
||||||
|
# trap rm later
|
||||||
|
|
||||||
|
export C_TERTIARY='\x1b[1;35m'
|
||||||
|
export C_PRIMARY='\x1b[1;34m'
|
||||||
|
export C_CYAN='\x1b[1;36m'
|
||||||
|
export C_RESET='\x1b[0m'
|
||||||
|
|
||||||
|
# Check for terminal graphics support and set environment variables accordingly
|
||||||
|
|
||||||
|
_check_kitty_icat() {
|
||||||
|
# workaround for WezTerm
|
||||||
|
if [ -n "${WEZTERM_EXECUTABLE:-}" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
kgp-query
|
||||||
|
}
|
||||||
|
|
||||||
|
_check_sixel() {
|
||||||
|
# workaround for Zellij
|
||||||
|
if [ -n "${ZELLIJ_SESSION_NAME:-}" ]; then
|
||||||
|
return 1
|
||||||
|
# same for tmux, unless otherwise configured
|
||||||
|
elif [ -n "${TMUX:-}" ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
sixel-query
|
||||||
|
}
|
||||||
|
|
||||||
|
_check_iterm2() {
|
||||||
|
iterm2-query
|
||||||
|
}
|
||||||
|
|
||||||
|
ENABLE_ICAT=0
|
||||||
|
ENABLE_SIXEL=0
|
||||||
|
ENABLE_ITERM2=0
|
||||||
|
|
||||||
|
# Priority: KGP > sixel > iterm2
|
||||||
|
if _check_kitty_icat; then
|
||||||
|
export ENABLE_ICAT=1
|
||||||
|
elif _check_sixel; then
|
||||||
|
export ENABLE_SIXEL=1
|
||||||
|
elif _check_iterm2; then
|
||||||
|
export ENABLE_ITERM2=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export ENABLE_ICAT
|
||||||
|
export ENABLE_SIXEL
|
||||||
|
export ENABLE_ITERM2
|
||||||
|
|
||||||
|
# Preview functions
|
||||||
|
|
||||||
|
_preview_image() {
|
||||||
|
local file="$1"
|
||||||
|
if [ "$ENABLE_ICAT" -eq 1 ]; then
|
||||||
|
printf "\x1b_Ga=d\x1b\\"
|
||||||
|
chafa -f kitty --animate=off --size="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}" "$file"
|
||||||
|
elif [ "$ENABLE_SIXEL" -eq 1 ]; then
|
||||||
|
chafa -f sixels --animate=off --size="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}" "$file"
|
||||||
|
elif [ "$ENABLE_ITERM2" -eq 1 ]; then
|
||||||
|
chafa -f iterm2 --animate=off --size="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}" "$file"
|
||||||
|
else
|
||||||
|
chafa -f symbols --animate=off --size="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}" "$file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f _preview_image
|
||||||
|
|
||||||
|
_preview_text() {
|
||||||
|
local content="$1"
|
||||||
|
if [ "$ENABLE_ICAT" -eq 1 ]; then
|
||||||
|
printf "\x1b_Ga=d\x1b\\"
|
||||||
|
fi
|
||||||
|
echo "$content" | head -n 100
|
||||||
|
}
|
||||||
|
export -f _preview_text
|
||||||
|
|
||||||
|
_preview_file() {
|
||||||
|
path="$1"
|
||||||
|
path_mime=$(file -b --mime-type "$path")
|
||||||
|
if [[ $path_mime =~ image ]]; then
|
||||||
|
_preview_image "$path"
|
||||||
|
elif [[ "$path_mime" =~ video ]]; then
|
||||||
|
video_hash=$(echo "$path" | md5sum | cut -d" " -f1)
|
||||||
|
thumb_file="$CACHE_DIR/$video_hash.png"
|
||||||
|
if [ ! -f "$thumb_file" ]; then
|
||||||
|
if command -v ffmpegthumbnailer &>/dev/null;then
|
||||||
|
ffmpegthumbnailer -i "$path" -o "$thumb_file" -s 480 -t 0 >/dev/null 2>&1
|
||||||
|
else
|
||||||
|
_preview_text "ffmpegthumbnailer not installed, cannot generate thumbnail for video."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ -s "$thumb_file" ]; then
|
||||||
|
_preview_image "$thumb_file"
|
||||||
|
else
|
||||||
|
_preview_text "Video: $path (No thumbnail)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
_preview_text "$path"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f _preview_file
|
||||||
|
|
||||||
|
preview() {
|
||||||
|
entry="$1"
|
||||||
|
|
||||||
|
content=$(echo "$entry" | cut -f2-)
|
||||||
|
mimeType=$(echo "$entry" | cliphist decode | file -b --mime-type -)
|
||||||
|
ext=$(echo "$mimeType" | awk -F"/" "{print \$2}")
|
||||||
|
|
||||||
|
if [[ $mimeType =~ image ]]; then
|
||||||
|
img_hash=$(echo "$entry" | cliphist decode | md5sum | cut -d" " -f1)
|
||||||
|
cache_file="$CACHE_DIR/$img_hash.$ext"
|
||||||
|
[ -f "$cache_file" ] || echo "$entry" | cliphist decode > "$cache_file"
|
||||||
|
_preview_image "$cache_file"
|
||||||
|
|
||||||
|
elif [ "$mimeType" = "text/html" ] && echo "$content" | grep -q QQ; then
|
||||||
|
qq_img_file=$(echo "$entry" | cliphist decode | grep -oP "^<img src=\"file://\K[^\"]+")
|
||||||
|
#qq_ext="${qq_img_file##*.}"
|
||||||
|
#qq_img_cache_file=$CACHE_DIR/$id.$qq_ext
|
||||||
|
#cp $qq_img_file $qq_img_cache_file
|
||||||
|
if [ -f "$qq_img_file" ]; then
|
||||||
|
_preview_image "$qq_img_file"
|
||||||
|
else
|
||||||
|
_preview_text "$qq_img_file does not exist."
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif path=$(echo "$entry" | cliphist decode) && [[ "$path" == /* ]]; then
|
||||||
|
if [ -e "$path" ]; then
|
||||||
|
_preview_file "$path"
|
||||||
|
else
|
||||||
|
_preview_text "$path does not exist."
|
||||||
|
fi
|
||||||
|
elif decoded=$(echo "$entry" | cliphist decode) && [[ "$decoded" == file://* ]]; then
|
||||||
|
raw_path="${decoded#file://}"
|
||||||
|
raw_path=$(echo "$raw_path" | python -c "import sys, urllib.parse; print(urllib.parse.unquote(sys.stdin.read().strip()))")
|
||||||
|
if [ -e "$raw_path" ]; then
|
||||||
|
_preview_file "$raw_path"
|
||||||
|
else
|
||||||
|
_preview_text "$raw_path does not exist."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ "$ENABLE_ICAT" = 1 ]; then
|
||||||
|
printf "\x1b_Ga=d\x1b\\"
|
||||||
|
fi
|
||||||
|
_preview_text "$(echo "$entry" | cliphist decode)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f preview
|
||||||
|
|
||||||
|
# Optimize entry formatting
|
||||||
|
|
||||||
|
format_clip_list() {
|
||||||
|
sed -E \
|
||||||
|
-e "s/(\t).*\.(mp4|mkv|webm|avi|mov|flv|wmv)$/\1${C_TERTIARY}[VIDEO]File.\2${C_RESET}/" \
|
||||||
|
-e "s/(\t)file:\/\/.*\.(mp4|mkv|webm|avi|mov|flv|wmv)$/\1${C_TERTIARY}[VIDEO]Url.\2${C_RESET}/" \
|
||||||
|
-e "s/(\t).*src=\"file:\/\/.*[qQ][qQ].*/\1${C_PRIMARY}[IMG_HTML]QQ${C_RESET}/" \
|
||||||
|
-e "s/(\t)file:\/\/.*xwechat.*temp.*/\1${C_PRIMARY}[IMG]WeChat${C_RESET}/" \
|
||||||
|
-e "s/(\t)file:\/\/.*\.gif$/\1${C_PRIMARY}[IMG]Url.gif${C_RESET}/" \
|
||||||
|
-e "s/(\t)file:\/\/.*\.(png|jpg|jpeg|webp|bmp)$/\1${C_TERTIARY}[IMG]Url.\2${C_RESET}/" \
|
||||||
|
-e "s/(\t)file:\/\/.*/\1${C_CYAN}[URL]File${C_RESET}/" \
|
||||||
|
-e "s/(\t)\/.*\.gif$/\1${C_PRIMARY}[IMG]Path.gif${C_RESET}/" \
|
||||||
|
-e "s/(\t)\/.*\.(png|jpg|jpeg|webp|bmp)$/\1${C_TERTIARY}[IMG]Path.\2${C_RESET}/" \
|
||||||
|
-e "s/\[\[ binary data .* (png|jpg|jpeg|gif|webp) .*\]\]/${C_TERTIARY}[IMG]Bin.\1${C_RESET}/" \
|
||||||
|
-e "s/\[\[ binary data .* \]\]/${C_CYAN}[BINARY]${C_RESET}/"
|
||||||
|
}
|
||||||
|
export -f format_clip_list
|
||||||
|
|
||||||
|
add_num() {
|
||||||
|
awk -F '\t' '{printf "%s\t\x1b[90m%-2d \x1b[0m%s\n", $1, NR, $2}'
|
||||||
|
}
|
||||||
|
export -f add_num
|
||||||
|
|
||||||
|
# Action when confirmed
|
||||||
|
|
||||||
|
copy_selection() {
|
||||||
|
local input="$1"
|
||||||
|
local decoded
|
||||||
|
decoded=$(echo "$input" | cliphist decode)
|
||||||
|
local mime
|
||||||
|
mime=$(echo "$decoded" | file -b --mime-type -)
|
||||||
|
|
||||||
|
url_encode() {
|
||||||
|
python -c "import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1]))" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Image
|
||||||
|
if [[ "$mime" =~ image ]]; then
|
||||||
|
echo "$decoded" | wl-copy
|
||||||
|
|
||||||
|
# HTML with QQ image
|
||||||
|
elif [ "$mime" = "text/html" ]; then
|
||||||
|
local qq_src
|
||||||
|
qq_src=$(echo "$decoded" | grep -oP "^<img src=\"file://\K[^\"]+")
|
||||||
|
|
||||||
|
if [ -f "$qq_src" ]; then
|
||||||
|
local encoded_path
|
||||||
|
encoded_path=$(url_encode "$qq_src")
|
||||||
|
|
||||||
|
echo "file://$encoded_path" | wl-copy --type text/uri-list
|
||||||
|
else
|
||||||
|
echo "$decoded" | wl-copy
|
||||||
|
fi
|
||||||
|
|
||||||
|
# URL starting with file://
|
||||||
|
elif [[ "$decoded" == file://* ]]; then
|
||||||
|
echo "$decoded" | wl-copy --type text/uri-list
|
||||||
|
|
||||||
|
# file path
|
||||||
|
elif [[ "$decoded" == /* ]] && [ -e "$decoded" ]; then
|
||||||
|
local encoded_path
|
||||||
|
encoded_path=$(url_encode "$decoded")
|
||||||
|
echo "file://$encoded_path" | wl-copy --type text/uri-list
|
||||||
|
|
||||||
|
# Other data, just copy
|
||||||
|
else
|
||||||
|
echo "$decoded" | wl-copy
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f copy_selection
|
||||||
|
|
||||||
|
# Reload mechanism
|
||||||
|
|
||||||
|
FZF_PORT=$(shuf -i 10000-60000 -n 1)
|
||||||
|
RELOAD_CMD="cliphist list | format_clip_list | add_num"
|
||||||
|
wl-paste --watch bash -c "curl -s -X POST -d 'reload($RELOAD_CMD)' http://localhost:$FZF_PORT" >/dev/null 2>&1 &
|
||||||
|
WATCH_PID=$!
|
||||||
|
trap 'rm -rf "$CACHE_DIR"; kill $WATCH_PID 2>/dev/null' EXIT
|
||||||
|
|
||||||
|
# Ensure terminal is large enough
|
||||||
|
|
||||||
|
wait_timeout=50
|
||||||
|
while [[ $(tput cols) -lt 35 || $(tput lines) -lt 25 ]]; do
|
||||||
|
printf "\rWaiting for terminal size at least 35x25... %d" "$wait_timeout"
|
||||||
|
sleep 1
|
||||||
|
((wait_timeout--))
|
||||||
|
[ "$wait_timeout" -eq 0 ] && exit 1
|
||||||
|
done
|
||||||
|
|
||||||
|
cliphist list | format_clip_list | add_num | fzf \
|
||||||
|
--ansi \
|
||||||
|
--listen "$FZF_PORT" \
|
||||||
|
--bind "ctrl-r:reload($RELOAD_CMD)" \
|
||||||
|
--bind "ctrl-x:execute-silent(bash -c 'cliphist delete <<< \"\$1\"' -- {})+reload($RELOAD_CMD)" \
|
||||||
|
--prompt=" > " \
|
||||||
|
--header='CTRL-X: Delete | CTRL-R: Reload | ENTER: Paste' \
|
||||||
|
--color='header:italic:yellow,prompt:blue,pointer:blue' \
|
||||||
|
--info=hidden \
|
||||||
|
--no-sort \
|
||||||
|
--layout=reverse \
|
||||||
|
--with-nth 2.. \
|
||||||
|
--delimiter '\t' \
|
||||||
|
--preview-window=down:60%,wrap \
|
||||||
|
--preview "preview {}" \
|
||||||
|
--bind "enter:execute-silent(bash -c 'copy_selection \"\$1\"' -- {})+accept"
|
||||||
|
|
||||||
Executable
+16
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Description:
|
||||||
|
# Wrapper for fzfclip to ensure only one instance is
|
||||||
|
# running and to launch it in ghostty.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
exec {LOCK_FD}>/tmp/"$(basename "$0")".lock
|
||||||
|
|
||||||
|
flock -n "$LOCK_FD" || {
|
||||||
|
echo "Another instance is running. Exiting." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ghostty -e fzfclip "$@"
|
||||||
Executable
+103
@@ -0,0 +1,103 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Description:
|
||||||
|
# Query terminal capabilities for graphics support, including:
|
||||||
|
# - Kitty Graphics Protocol (KGP)
|
||||||
|
# - iTerm2 inline images
|
||||||
|
# - Sixel graphics
|
||||||
|
# This script will print three lines of output:
|
||||||
|
# SUPPORT_KGP=1 or 0
|
||||||
|
# SUPPORT_ITERM2=1 or 0
|
||||||
|
# SUPPORT_SIXEL=1 or 0
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# Do NOT source this script directly, as it will modify the terminal state
|
||||||
|
# and print its result directly to stdout.
|
||||||
|
# Instead, use command substitution to capture the output, for example:
|
||||||
|
# eval "$(graphics-query)"
|
||||||
|
# or parse the output manually.
|
||||||
|
#
|
||||||
|
# See also:
|
||||||
|
# - kgp-query: specifically checks for Kitty Graphics Protocol support
|
||||||
|
# - iterm2-query: specifically checks for iTerm2 inline image support
|
||||||
|
# - sixel-query: specifically checks for Sixel graphics support
|
||||||
|
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Ensure in a interactive terminal
|
||||||
|
[ ! -t 0 ] && exit 1
|
||||||
|
|
||||||
|
# Construct query
|
||||||
|
KGP_QUERY_ID=$RANDOM
|
||||||
|
KGP_QUERY_CODE=$(printf "\033_Gi=%d,s=1,v=1,a=q,t=d,f=24;AAAA\033\\" "$KGP_QUERY_ID")
|
||||||
|
ITERM2_QUERY_CODE=$(printf "\033]1337;ReportCellSize\a")
|
||||||
|
KGP_EXPECTED_RESPONSE=$(printf "\033_Gi=%d;OK\033\\" "$KGP_QUERY_ID")
|
||||||
|
ITERM2_EXPECTED_RESPONSE=$(printf "\033]1337;") # followed by "ReportCellSize=...", but only the prefix is enough
|
||||||
|
FENCE_CODE=$(printf "\033[c")
|
||||||
|
|
||||||
|
# Set terminal to raw mode with timeout
|
||||||
|
stty_orig=$(stty -g)
|
||||||
|
trap 'stty "$stty_orig"' EXIT
|
||||||
|
stty -echo -icanon min 1 time 0
|
||||||
|
|
||||||
|
printf "%s%s%s" "$ITERM2_QUERY_CODE" "$KGP_QUERY_CODE" "$FENCE_CODE" > /dev/tty
|
||||||
|
|
||||||
|
response=""
|
||||||
|
support_kgp=0
|
||||||
|
support_iterm2=0
|
||||||
|
while true; do
|
||||||
|
IFS= read -r -N 1 -t 0.3 char || {
|
||||||
|
[ -z "$char" ] && break
|
||||||
|
}
|
||||||
|
|
||||||
|
response+="$char"
|
||||||
|
|
||||||
|
if [[ "$response" == *"$KGP_EXPECTED_RESPONSE"* ]]; then
|
||||||
|
support_kgp=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$response" == *"$ITERM2_EXPECTED_RESPONSE"* ]]; then
|
||||||
|
support_iterm2=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$response" == *$'\033['*'c' ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${#response} -gt 1024 ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
support_sixel=0
|
||||||
|
if [[ "$response" =~ $'\x1b'\[\?([0-9;]*)c ]]; then
|
||||||
|
params="${BASH_REMATCH[1]}"
|
||||||
|
|
||||||
|
IFS=';' read -ra codes <<< "$params"
|
||||||
|
|
||||||
|
for code in "${codes[@]}"; do
|
||||||
|
if [[ "$code" == "4" ]]; then
|
||||||
|
support_sixel=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$support_kgp" -eq 1 ]; then
|
||||||
|
echo "SUPPORT_KGP=1"
|
||||||
|
else
|
||||||
|
echo "SUPPORT_KGP=0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$support_iterm2" -eq 1 ]; then
|
||||||
|
echo "SUPPORT_ITERM2=1"
|
||||||
|
else
|
||||||
|
echo "SUPPORT_ITERM2=0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$support_sixel" -eq 1 ]; then
|
||||||
|
echo "SUPPORT_SIXEL=1"
|
||||||
|
else
|
||||||
|
echo "SUPPORT_SIXEL=0"
|
||||||
|
fi
|
||||||
Executable
+47
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Ensure in a interactive terminal
|
||||||
|
[ ! -t 0 ] && exit 1
|
||||||
|
|
||||||
|
# Construct query
|
||||||
|
QUERY_CODE=$(printf "\033]1337;ReportCellSize\a")
|
||||||
|
EXPECTED_RESPONSE=$(printf "\033]1337;") # followed by "ReportCellSize=...", but only the prefix is enough
|
||||||
|
# Construct fence code that most terminals will respond to
|
||||||
|
# to ensure that at least something will be sent back and
|
||||||
|
# to detect the end of the response
|
||||||
|
FENCE_CODE=$(printf "\033[c")
|
||||||
|
|
||||||
|
# Set terminal to raw mode
|
||||||
|
stty_orig=$(stty -g)
|
||||||
|
trap 'stty "$stty_orig"' EXIT
|
||||||
|
stty -echo -icanon min 1 time 0
|
||||||
|
|
||||||
|
printf "%s%s" "$QUERY_CODE" "$FENCE_CODE" > /dev/tty
|
||||||
|
|
||||||
|
response=""
|
||||||
|
ret=1
|
||||||
|
while true; do
|
||||||
|
IFS= read -r -N 1 -t 0.3 char || {
|
||||||
|
[ -z "$char" ] && break
|
||||||
|
}
|
||||||
|
|
||||||
|
response+="$char"
|
||||||
|
|
||||||
|
if [[ "$response" == *"$EXPECTED_RESPONSE"* ]]; then
|
||||||
|
ret=0
|
||||||
|
# Keep reading until response is complete to
|
||||||
|
# avoid leaving unread data in the terminal
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$response" == *$'\033['*'c' ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${#response} -gt 1024 ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
exit $ret
|
||||||
@@ -16,7 +16,7 @@ EXPECTED_RESPONSE=$(printf "\033_Gi=%d;OK\033\\" "$QUERY_ID")
|
|||||||
# to detect the end of the response
|
# to detect the end of the response
|
||||||
FENCE_CODE=$(printf "\033[c")
|
FENCE_CODE=$(printf "\033[c")
|
||||||
|
|
||||||
# Set terminal to raw mode with timeout as 0.5s
|
# Set terminal to raw mode with timeout
|
||||||
stty_orig=$(stty -g)
|
stty_orig=$(stty -g)
|
||||||
trap 'stty "$stty_orig"' EXIT
|
trap 'stty "$stty_orig"' EXIT
|
||||||
stty -echo -icanon min 1 time 0
|
stty -echo -icanon min 1 time 0
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
pkill -f "ghostty -e shorinclip" || ghostty -e shorinclip
|
|
||||||
Reference in New Issue
Block a user