update cliphist viewer
This commit is contained in:
@@ -36,7 +36,7 @@ binds {
|
||||
Alt+Space { spawn-sh "pkill -x rofi || rofi -show drun"; }
|
||||
|
||||
// Actions
|
||||
Mod+V { spawn-sh "pkill -x rofi || rofi-cliphist"; }
|
||||
Mod+V { spawn-sh "pkill -x shorin-cliphist || ghostty -e shorin-cliphist"; }
|
||||
Mod+Period { spawn-sh "pkill -x rofi || rofi-emoji"; }
|
||||
Ctrl+Alt+Delete { spawn-sh "pkill -x wlogout || wlogout -p layer-shell"; }
|
||||
Print { spawn "niri" "msg" "action" "screenshot-screen"; }
|
||||
|
||||
@@ -1,7 +1,30 @@
|
||||
#!/bin/sh
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Description:
|
||||
# Quick snippet for cliphist + rofi + wl-copy
|
||||
# ~~Quick~~ snippet for cliphist + rofi + wl-copy
|
||||
|
||||
cliphist list | rofi -dmenu -config ~/.config/rofi/dmenu.rasi -display-columns 2 -i | \
|
||||
cliphist decode | wl-copy
|
||||
tmp_dir="/tmp/cliphist"
|
||||
trap 'rm -rf "$tmp_dir"' EXIT
|
||||
mkdir -p "$tmp_dir"
|
||||
|
||||
read -r -d '' prog <<EOF
|
||||
/^[0-9]+\s<meta http-equiv=/ { next }
|
||||
match(\$0, /^([0-9]+)\s(\[\[\s)?binary.*(jpg|jpeg|png|bmp)/, grp) {
|
||||
system("echo " grp[1] "\\\\\t | cliphist decode >$tmp_dir/"grp[1]"."grp[3])
|
||||
print \$0"\0icon\x1f$tmp_dir/"grp[1]"."grp[3]
|
||||
next
|
||||
}
|
||||
1
|
||||
EOF
|
||||
|
||||
# Pipeline logic:
|
||||
# 1. cliphist list: gives "ID <tab> Content"
|
||||
# 2. gawk: adds icon paths
|
||||
# 3. rofi -dmenu: shows list, hides column 1 (ID), returns "ID <tab> Content" on select
|
||||
# 4. cliphist decode: reads ID from line, gets original content
|
||||
|
||||
result=$(cliphist list | gawk "$prog" | rofi -dmenu -display-columns 2 -config "$HOME/.config/rofi/dmenu.rasi" -show-icons -p "Clipboard")
|
||||
|
||||
if [[ -n "$result" ]]; then
|
||||
echo "$result" | cliphist decode | wl-copy
|
||||
fi
|
||||
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC2016
|
||||
|
||||
# Credit: https://github.com/SHORiN-KiWATA/shorinclip
|
||||
#
|
||||
# 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 -rf "$CACHE_DIR"' EXIT
|
||||
|
||||
# 颜色变量
|
||||
export C_TERTIARY='\x1b[1;35m'
|
||||
export C_PRIMARY='\x1b[1;34m'
|
||||
export C_CYAN='\x1b[1;36m'
|
||||
export C_RESET='\x1b[0m'
|
||||
|
||||
# 过滤无法复制粘贴的信息
|
||||
filter_clip_list() {
|
||||
grep -vP "(?s)(?i)\t.*<html.*\[表情\]"
|
||||
}
|
||||
export -f filter_clip_list
|
||||
# 美化列表
|
||||
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
|
||||
|
||||
# 导入fzf前的处理,添加序号
|
||||
add_num() {
|
||||
awk -F '\t' '{printf "%s\t\x1b[90m%-2d \x1b[0m%s\n", $1, NR, $2}'
|
||||
}
|
||||
export -f add_num
|
||||
# 复制前的处理,对不同数据采取不同的处理方式
|
||||
copy_selection() {
|
||||
#获取fzf选择的项目
|
||||
local input="$1"
|
||||
# 获取decode内容
|
||||
local decoded
|
||||
decoded=$(echo "$input" | cliphist decode)
|
||||
# 获取mime类型
|
||||
local mime
|
||||
mime=$(echo "$input" | cliphist decode | file -b --mime-type -)
|
||||
|
||||
# Python 工具:用于 URL 编码 (处理路径中的中文和空格)
|
||||
url_encode() {
|
||||
python -c "import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1]))" "$1"
|
||||
}
|
||||
|
||||
# 1. 二进制图片 -> 直接复制图片数据
|
||||
if [[ "$mime" =~ image ]]; then
|
||||
# 直接复制
|
||||
echo "$input" | cliphist decode | wl-copy
|
||||
|
||||
# 2. QQ HTML -> 提取路径 -> 存入缓存 -> 复制 file:// 缓存路径
|
||||
elif [ "$mime" = "text/html" ]; then
|
||||
local qq_src
|
||||
qq_src=$(echo "$decoded" | grep -oP "^<img src=\"file://\K[^\"]+")
|
||||
|
||||
if [ -f "$qq_src" ]; then
|
||||
# url编码处理
|
||||
local encoded_path
|
||||
encoded_path=$(url_encode "$qq_src")
|
||||
|
||||
# 拼接头并复制
|
||||
echo "file://$encoded_path" | wl-copy --type text/uri-list
|
||||
fi
|
||||
|
||||
# 4. file:// 协议路径 -> 直接复制为文件链接
|
||||
elif [[ "$decoded" == file://* ]]; then
|
||||
echo "$decoded" | wl-copy --type text/uri-list
|
||||
|
||||
# 3. 绝对路径 -> 转 file:// -> 复制为URL链接
|
||||
elif [[ "$decoded" == /* ]] && [ -e "$decoded" ]; then
|
||||
local encoded_path
|
||||
encoded_path=$(url_encode "$decoded")
|
||||
echo "file://$encoded_path" | wl-copy --type text/uri-list
|
||||
|
||||
# 5. 其他 -> 普通文本复制
|
||||
else
|
||||
echo "$input" | cliphist decode | wl-copy
|
||||
fi
|
||||
}
|
||||
export -f copy_selection
|
||||
# 自动刷新机制
|
||||
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 'kill $WATCH_PID 2>/dev/null' EXIT
|
||||
|
||||
# 读取窗口大小,避免窗口过小的时候就打开fzf导致菜单错位
|
||||
# 50次循环超时
|
||||
wait_timeout=50
|
||||
# 循环检测终端的长和宽
|
||||
while [[ $(tput cols) -lt 35 || $(tput lines) -lt 25 ]]; do
|
||||
echo "Waiting for terminal resize to at least 35x25..."
|
||||
sleep 1
|
||||
# 每循环一次减少超时时间
|
||||
((wait_timeout--))
|
||||
[ "$wait_timeout" -eq 0 ] && {
|
||||
echo "Timeout waiting for terminal resize. Exiting."
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
|
||||
#=== FZF 主程序 ===
|
||||
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: Copy' \
|
||||
--color='header:italic:yellow,prompt:blue,pointer:blue' \
|
||||
--info=hidden \
|
||||
--no-sort \
|
||||
--layout=reverse \
|
||||
--with-nth 2.. \
|
||||
--delimiter '\t' \
|
||||
--preview-window=down:50% \
|
||||
--preview '
|
||||
# 获取当期剪贴版项目的id
|
||||
id=$(echo {} | cut -f1)
|
||||
content=$(echo {} | cut -f2-)
|
||||
# 通过decode数据获取mimetype
|
||||
mimeType=$(echo {} | cliphist decode | file -b --mime-type -)
|
||||
# 获取文件后缀名
|
||||
ext=$(echo $mimeType | awk -F"/" "{print \$2}")
|
||||
|
||||
# 通过mimetype判断数据/类型
|
||||
# 如果是二进制图片
|
||||
if [[ $mimeType =~ image ]]; then
|
||||
# 生成缓存文件
|
||||
img_hash=$(echo {} | cliphist decode | md5sum | cut -d" " -f1)
|
||||
cache_file="$CACHE_DIR/$img_hash.$ext"
|
||||
echo {} | cliphist decode > "$cache_file"
|
||||
kitty +kitten icat --clear --image-id=10 --transfer-mode=file \
|
||||
--place="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}@0x0" "$cache_file" </dev/tty
|
||||
|
||||
# 如果是qq复制出来的html链接
|
||||
elif [ "$mimeType" = "text/html" ] && echo "$content" | grep -q QQ; then
|
||||
# 获取html数据中的图片路径
|
||||
qq_img_file=$(echo {} | 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
|
||||
# 把路径传给kitty +kitten icat预览
|
||||
if [ -f "$qq_img_file" ]; then
|
||||
kitty +kitten icat --transfer-mode=file --clear --image-id=10 \
|
||||
--place="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}@0x0" "$qq_img_file" </dev/tty
|
||||
else
|
||||
echo "$qq_img_file does not exsist."
|
||||
fi
|
||||
# 如果是复制文件获取的绝对路径
|
||||
elif path=$(echo {} | cliphist decode) && [[ "$path" == /* ]]; then
|
||||
# 获取路径代表的文件的mimetype
|
||||
path_mime=$(file -b --mime-type "$path")
|
||||
# 如果是图片的话
|
||||
if [[ $path_mime =~ image ]]; then
|
||||
kitty +kitten icat --transfer-mode=file --clear --image-id=10 \
|
||||
--place="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}@0x0" "$path" </dev/tty
|
||||
# 如果是视频的话
|
||||
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
|
||||
echo "ffmpegthumbnailer not installed."
|
||||
fi
|
||||
fi
|
||||
# 如果缩略图文件存在且大小不为零
|
||||
if [ -s "$thumb_file" ]; then
|
||||
kitty +kitten icat --transfer-mode=file --image-id=10 \
|
||||
--place="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}@${FZF_PREVIEW_LEFT}x${FZF_PREVIEW_TOP}" \
|
||||
"$thumb_file" </dev/tty
|
||||
else
|
||||
echo "Video: $path (No thumbnail)"
|
||||
fi
|
||||
else
|
||||
echo "$path does not exsist."
|
||||
fi
|
||||
# 如果是file:///开头的协议路径
|
||||
elif decoded=$(echo {} | 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()))")
|
||||
mime_raw_path=$(file -b --mime-type "$raw_path")
|
||||
|
||||
# 如果是图片的话
|
||||
if [[ $mime_raw_path =~ image ]]; then
|
||||
kitty +kitten icat --transfer-mode=file --clear --image-id=10 \
|
||||
--place="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}@0x0" "$raw_path" </dev/tty
|
||||
# 如果是视频的话
|
||||
elif [[ $mime_raw_path =~ video ]]; then
|
||||
video_hash=$(echo "$raw_path" | md5sum | cut -d" " -f1)
|
||||
raw_thumb_file="$CACHE_DIR/$video_hash.png"
|
||||
# 如果文件不存在的话
|
||||
if [ ! -f "$raw_thumb_file" ] ; then
|
||||
#检测是否安装了ffmpeg缩略图软件
|
||||
if command -v ffmpegthumbnailer >/dev/null 2>&1; then
|
||||
ffmpegthumbnailer -i "$raw_path" -o "$raw_thumb_file" -s 480 -t 0 >/dev/null 2>&1
|
||||
else
|
||||
echo "ffmpegthumbnailer not installed"
|
||||
fi
|
||||
fi
|
||||
# 如果缩略图文件存在且大小不为零
|
||||
if [ -s "$raw_thumb_file" ]; then
|
||||
kitty +kitten icat --transfer-mode=file --image-id=10 \
|
||||
--place="${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}@${FZF_PREVIEW_LEFT}x${FZF_PREVIEW_TOP}" \
|
||||
"$raw_thumb_file" </dev/tty
|
||||
else
|
||||
echo "Video: $raw_path (No thumbnail)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 其他数据
|
||||
else
|
||||
kitty +kitten icat --clear --silent --transfer-mode=file > /dev/tty 2>/dev/null
|
||||
# 直接预览
|
||||
echo "[$mimeType]"
|
||||
echo ---
|
||||
echo {} | cliphist decode
|
||||
fi
|
||||
' \
|
||||
--bind "enter:execute-silent(bash -c 'copy_selection \"\$1\"' -- {})+accept"
|
||||
|
||||
# 粘贴已经绑定到了fzf的enter键,脚本会自动退出,必须写在fzf的按键绑定,否则wl-copy会导致浮动终端卡住(niri下)。
|
||||
Reference in New Issue
Block a user