diff --git a/config/scripts/.local/scripts/fzfclip b/config/scripts/.local/scripts/fzfclip
index 1d93a4e..99c9ec3 100755
--- a/config/scripts/.local/scripts/fzfclip
+++ b/config/scripts/.local/scripts/fzfclip
@@ -1,15 +1,14 @@
#!/usr/bin/env bash
-# shellcheck disable=SC2016
# Description:
# View and manage clipboard history using fzf, with support for
# image preview in compatible terminals.
# Requirements:
+# - fzf
# - cliphist
# - wl-clipboard
-# - fzf
-# - sixel-query, kgp-query, iterm2-query from this repository
-# - chafa (for image preview)
+# - python with urllib (for URL quoting/unquoting)
+# - chafa (optional, for image preview)
# - ffmpegthumbnailer (optional, for video thumbnails)
# Credits:
# - Original idea and some code adapted from https://github.com/SHORiN-KiWATA/shorinclip
@@ -38,9 +37,21 @@
set -euo pipefail
+_cleanup() {
+ if [ -n "${CACHE_DIR:-}" ] && [ -d "$CACHE_DIR" ]; then
+ rm -rf "$CACHE_DIR"
+ fi
+ if [ -n "${WATCH_PID:-}" ]; then
+ kill "$WATCH_PID" 2>/dev/null || true
+ fi
+ if [ -n "${FZF_SOCKET:-}" ] && [ -S "$FZF_SOCKET" ]; then
+ rm -f "$FZF_SOCKET"
+ fi
+}
+trap _cleanup EXIT
+
CACHE_DIR=$(mktemp -d)
export CACHE_DIR
-# trap rm later
export C_TERTIARY='\x1b[1;35m'
export C_PRIMARY='\x1b[1;34m'
@@ -49,12 +60,111 @@ export C_RESET='\x1b[0m'
# Check for terminal graphics support and set environment variables accordingly
+graphics-query() {
+ # Port of [graphics-query](https://github.com/Uyanide/dotfiles/blob/main/config/scripts/.local/scripts/graphics-query)
+
+ # Ensure in a interactive terminal
+ [ ! -t 0 ] && return
+
+ (
+ # 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
+
+ support_kgp=0
+ support_iterm2=0
+ support_sixel=0
+
+ response=""
+ 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
+
+ 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 "kitty"
+ fi
+
+ if [ "$support_iterm2" -eq 1 ]; then
+ echo "iterm"
+ fi
+
+ if [ "$support_sixel" -eq 1 ]; then
+ echo "sixels"
+ fi
+ )
+}
+
+SUPPORT_ICAT=0
+SUPPORT_SIXEL=0
+SUPPORT_ITERM2=0
+
+ENABLE_ICAT=0
+ENABLE_SIXEL=0
+ENABLE_ITERM2=0
+
+_check_graphics_support() {
+ # type graphics-query &>/dev/null || return
+ local result
+ result=$(graphics-query)
+ if [[ "$result" == *"kitty"* ]]; then
+ SUPPORT_ICAT=1
+ elif [[ "$result" == *"sixels"* ]]; then
+ SUPPORT_SIXEL=1
+ elif [[ "$result" == *"iterm"* ]]; then
+ SUPPORT_ITERM2=1
+ fi
+}
+
_check_kitty_icat() {
- # workaround for WezTerm
+ # # workaround for WezTerm
if [ -n "${WEZTERM_EXECUTABLE:-}" ]; then
return 1
fi
- kgp-query
+ [[ "$SUPPORT_ICAT" -eq 1 ]]
}
_check_sixel() {
@@ -65,36 +175,70 @@ _check_sixel() {
elif [ -n "${TMUX:-}" ]; then
return 1
fi
- sixel-query
+ [[ "$SUPPORT_SIXEL" -eq 1 ]]
}
_check_iterm2() {
- iterm2-query
+ [[ "$SUPPORT_ITERM2" -eq 1 ]]
}
-ENABLE_ICAT=0
-ENABLE_SIXEL=0
-ENABLE_ITERM2=0
-
# Priority: KGP > sixel > iterm2
+_check_graphics_support
if _check_kitty_icat; then
- export ENABLE_ICAT=1
+ ENABLE_ICAT=1
elif _check_sixel; then
- export ENABLE_SIXEL=1
+ ENABLE_SIXEL=1
elif _check_iterm2; then
- export ENABLE_ITERM2=1
+ ENABLE_ITERM2=1
fi
export ENABLE_ICAT
export ENABLE_SIXEL
export ENABLE_ITERM2
+# URL handling (for file:// URLs)
+
+url_unquote() {
+ if type python3 &>/dev/null; then
+ python3 -c "import sys, urllib.parse; print(urllib.parse.unquote(sys.stdin.read().strip()), end='')"
+ else
+ cat
+ fi
+}
+
+url_quote() {
+ if type python3 &>/dev/null; then
+ python3 -c "import sys, urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()), end='')"
+ else
+ cat
+ fi
+}
+export -f url_unquote
+export -f url_quote
+
# Preview functions
+_clear_preview() {
+ # chafa --clear will simply send '\x1b[H\x1b[2J' which may not work
+ # for images displayed with KGP, so we send the specific clear sequence
+ # manually in this case.
+ if [ "$ENABLE_ICAT" -eq 1 ]; then
+ printf "\x1b_Ga=d\x1b\\"
+ fi
+}
+export -f _clear_preview
+
_preview_image() {
local file="$1"
+ if ! type chafa >/dev/null 2>&1; then
+ text="Preview not available (chafa not found)."$'\n'
+ text+="Image: $file"
+ _preview_text "$text"
+ return
+ fi
+ # Though chafa is able to detect which output format to use based on the terminal capabilities,
+ # it is yet not always reliable, so we force it based on our checks.
if [ "$ENABLE_ICAT" -eq 1 ]; then
- printf "\x1b_Ga=d\x1b\\"
chafa -f kitty --size="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}" "$file"
elif [ "$ENABLE_SIXEL" -eq 1 ]; then
chafa -f sixels --size="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}" "$file"
@@ -108,40 +252,61 @@ 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
+ printf "%s" "$content" | head -n 100
}
export -f _preview_text
+_preview_video() {
+ local video_hash thumb_file path
+ path="$1"
+ video_hash=$(echo "$path" | md5sum | cut -d" " -f1)
+ thumb_file="$CACHE_DIR/$video_hash.png"
+ if [ ! -f "$thumb_file" ]; then
+ if type ffmpegthumbnailer &>/dev/null; then
+ ffmpegthumbnailer -i "$path" -o "$thumb_file" -s 480 -t 0 >/dev/null 2>&1
+ else
+ _preview_text "Thumbnail not available (ffmpegthumbnailer not found)."$'\n'
+ fi
+ fi
+ if [ -s "$thumb_file" ]; then
+ _preview_image "$thumb_file"
+ else
+ _preview_text "Video: $path"
+ fi
+}
+export -f _preview_video
+
+# Kinda buggy right now
+# _preview_audio() {
+# local path="$1"
+# if type mpv &>/dev/null; then
+# echo "Playing audio: $path" >&2
+# exec mpv --no-video --keep-open=no --loop-file=no --loop-playlist=no "$path" &>/dev/null
+# else
+# _preview_text "Audio: $path"
+# fi
+# }
+# export -f _preview_audio
+
_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
+ _preview_video "$path"
+ # elif [[ "$path_mime" =~ audio ]]; then
+ # _preview_audio "$path"
else
_preview_text "$path"
fi
}
export -f _preview_file
+
preview() {
+ _clear_preview
+
entry="$1"
content=$(echo "$entry" | cut -f2-)
@@ -154,31 +319,31 @@ preview() {
[ -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 "^
/dev/null 2>&1 &
+wl-paste --watch bash -c "curl -s --unix-socket '$FZF_SOCKET' -X POST -d 'reload($RELOAD_CMD)' http://localhost" &>/dev/null &
WATCH_PID=$!
-trap 'rm -rf "$CACHE_DIR"; kill $WATCH_PID 2>/dev/null' EXIT
# Ensure terminal is large enough
@@ -277,9 +422,9 @@ while [[ $(tput cols) -lt 35 || $(tput lines) -lt 25 ]]; do
[ "$wait_timeout" -eq 0 ] && exit 1
done
-cliphist list | format_clip_list | add_num | fzf \
+$RELOAD_CMD | fzf \
--ansi \
- --listen "$FZF_PORT" \
+ --listen "$FZF_SOCKET" \
--bind "ctrl-r:reload($RELOAD_CMD)" \
--bind "ctrl-x:execute-silent(bash -c 'cliphist delete <<< \"\$1\"' -- {})+reload($RELOAD_CMD)" \
--prompt=" > " \
@@ -292,4 +437,5 @@ cliphist list | format_clip_list | add_num | fzf \
--delimiter '\t' \
--preview-window=down:60%,wrap \
--preview "preview {}" \
- --bind "enter:execute-silent(bash -c 'copy_selection \"\$1\"' -- {})+accept"
+ --bind "enter:execute-silent(bash -c 'copy_selection \"\$1\"' -- {})+accept" \
+|| [ $? -eq 141 ]
diff --git a/config/scripts/.local/scripts/fzfclip-wrap b/config/scripts/.local/scripts/fzfclip-wrap
index 0cb9c29..f272421 100755
--- a/config/scripts/.local/scripts/fzfclip-wrap
+++ b/config/scripts/.local/scripts/fzfclip-wrap
@@ -4,8 +4,6 @@
# 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" || {
@@ -13,4 +11,4 @@ flock -n "$LOCK_FD" || {
exit 1
}
-ghostty -e fzfclip "$@"
+ghostty -e fzfclip "$@" {LOCK_FD}>&-
diff --git a/config/scripts/.local/scripts/playlive b/config/scripts/.local/scripts/playlive
index 4f39cdc..258755c 100755
--- a/config/scripts/.local/scripts/playlive
+++ b/config/scripts/.local/scripts/playlive
@@ -45,13 +45,13 @@ mp4_offset=$((offset - 3))
tail -c "+$mp4_offset" "$file" > "$tmp_video"
play_cmd=()
-if command -v mpv >/dev/null 2>&1; then
+if type mpv >/dev/null 2>&1; then
play_cmd=(mpv --title="Live Photo View: $file" --keep-open=yes --loop-file=yes --loop-playlist=no --idle=yes)
-elif command -v vlc >/dev/null 2>&1; then
+elif type vlc >/dev/null 2>&1; then
play_cmd=(vlc --meta-title="Live Photo View: $file")
else
echo "Error: No suitable media player found." >&2
exit 1
fi
-"${play_cmd[@]}" "$tmp_video"
\ No newline at end of file
+"${play_cmd[@]}" "$tmp_video"
diff --git a/memo/oh-my-altgr.md b/memo/oh-my-altgr.md
index 5e5422a..08320d0 100644
--- a/memo/oh-my-altgr.md
+++ b/memo/oh-my-altgr.md
@@ -1,4 +1,4 @@
-de 布局太全能了
+de 布局太全能了:
@@ -23,3 +23,5 @@ shift > Y X C V B N M ; : _
altgr | » « ¢ „ “ ” µ · … –
sh+al ˍ › ‹ © ‚ ‘ ’ º × ÷ —
```
+
+btw, `^` 死键对数字键(上方一排和小键盘均可)也有效, 作用为打出n次幂, 例如 `^` + `9` -> `⁹`.
diff --git a/scripts b/scripts
new file mode 120000
index 0000000..736fee1
--- /dev/null
+++ b/scripts
@@ -0,0 +1 @@
+config/scripts/.local/scripts
\ No newline at end of file