feat: 上传我的配置

This commit is contained in:
2026-02-06 17:17:48 +08:00
parent 5ae578a012
commit ac517ad717
126 changed files with 15159 additions and 0 deletions

View File

@@ -0,0 +1,653 @@
#!/usr/bin/env bash
set -euo pipefail
########################
# 配置区域(直接改这里)
########################
NIRI_CONFIG="$HOME/.config/niri/config.kdl" # niri 配置文件
SHOTEDITOR_DEFAULT="satty" # 默认截图编辑器swappy 或 satty
COPY_CMD="wl-copy" # 复制到剪贴板的命令
# 菜单程序,按你实际使用的启动器改
# wofi 示例: MENU_CMD='wofi -d'
# rofi 示例: MENU_CMD='rofi -dmenu'
MENU_CMD='fuzzel --dmenu'
# 图片目录
PICTURES_DIR="$(xdg-user-dir PICTURES 2>/dev/null || echo "$HOME/Pictures")"
SCREEN_DIR="$PICTURES_DIR/Screenshots"
########################
# 本地化(中/英)
########################
LOCALE="${LC_MESSAGES:-${LANG:-en}}"
if [[ "$LOCALE" == zh* ]]; then
# 通用
LABEL_CANCEL="取消"
LABEL_SETTINGS="设置"
LABEL_EDIT_YES="编辑"
LABEL_EDIT_NO="不编辑"
# Niri 模式
LABEL_NIRI_FULL="全屏"
LABEL_NIRI_WINDOW="窗口"
LABEL_NIRI_REGION="选取区域"
# Grim 模式
LABEL_GRIM_FULL="全屏"
LABEL_GRIM_REGION="选取区域"
# 设置菜单
LABEL_SETTINGS_EDITOR="截图工具"
LABEL_SETTINGS_BACKEND="后端模式"
LABEL_BACKEND_AUTO="自动(检测 Niri"
LABEL_BACKEND_GRIM="仅 Grim+slurp"
LABEL_BACK="返回"
# 编辑开关显示
LABEL_EDIT_STATE_ON="编辑:开启"
LABEL_EDIT_STATE_OFF="编辑:关闭"
# 提示文字
PROMPT_MAIN="请选择截图模式"
PROMPT_SETTINGS="设置 / 更改选项"
PROMPT_EDITOR="请选择截图编辑工具"
PROMPT_BACKEND="请选择后端模式"
else
LABEL_CANCEL="Cancel"
LABEL_SETTINGS="Settings"
LABEL_EDIT_YES="Edit"
LABEL_EDIT_NO="No edit"
LABEL_NIRI_FULL="Fullscreen"
LABEL_NIRI_WINDOW="Window"
LABEL_NIRI_REGION="Region"
LABEL_GRIM_FULL="Fullscreen"
LABEL_GRIM_REGION="Select area"
LABEL_SETTINGS_EDITOR="Screenshot tool"
LABEL_SETTINGS_BACKEND="Backend mode"
LABEL_BACKEND_AUTO="Auto (detect Niri)"
LABEL_BACKEND_GRIM="Grim+slurp only"
LABEL_BACK="Back"
LABEL_EDIT_STATE_ON="Edit: ON"
LABEL_EDIT_STATE_OFF="Edit: OFF"
PROMPT_MAIN="Choose screenshot mode"
PROMPT_SETTINGS="Settings / Options"
PROMPT_EDITOR="Choose screenshot editor"
PROMPT_BACKEND="Choose backend mode"
fi
########################
# 持久化配置路径 (已修改为 .cache 目录)
########################
XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
# 旧路径: CONFIG_DIR="$HOME/.config/waybar/waybar-shot"
CONFIG_DIR="$XDG_CACHE_HOME/waybar-power-screenshot-sh"
BACKEND_FILE="$CONFIG_DIR/backend"
EDITOR_FILE="$CONFIG_DIR/editor"
EDIT_MODE_FILE="$CONFIG_DIR/edit_mode" # yes / no
# 确保新的缓存目录存在
mkdir -p "$CONFIG_DIR"
########################
# 通用工具函数
########################
menu() {
printf '%s\n' "$@" | eval "$MENU_CMD" 2>/dev/null || true
}
menu_prompt() {
local prompt="$1"
shift
local esc_prompt="${prompt//\"/\\\"}"
printf '%s\n' "$@" | eval "$MENU_CMD --prompt \"$esc_prompt\"" 2>/dev/null || true
}
load_backend_mode() {
local mode
if [[ -n "${SHOT_BACKEND:-}" ]]; then
mode="$SHOT_BACKEND"
elif [[ -f "$BACKEND_FILE" ]]; then
mode="$(<"$BACKEND_FILE")"
else
mode="auto"
fi
case "$mode" in
auto|grim|niri) ;;
*) mode="auto" ;;
esac
printf '%s\n' "$mode"
}
save_backend_mode() {
local mode="$1"
# 由于脚本开始时已创建,这里只需要保证文件写入成功
printf '%s\n' "$mode" >"$BACKEND_FILE"
}
load_editor() {
local ed
if [[ -n "${SHOTEDITOR:-}" ]]; then
ed="$SHOTEDITOR"
elif [[ -f "$EDITOR_FILE" ]]; then
ed="$(<"$EDITOR_FILE")"
else
ed="$SHOTEDITOR_DEFAULT"
fi
ed="${ed,,}"
case "$ed" in
swappy|satty) ;;
*) ed="$SHOTEDITOR_DEFAULT" ;;
esac
printf '%s\n' "$ed"
}
save_editor() {
local ed="$1"
# 由于脚本开始时已创建,这里只需要保证文件写入成功
printf '%s\n' "$ed" >"$EDITOR_FILE"
}
load_edit_mode() {
local v="yes"
if [[ -f "$EDIT_MODE_FILE" ]]; then
v="$(<"$EDIT_MODE_FILE")"
fi
case "$v" in
yes|no) ;;
*) v="yes" ;; # 默认:编辑开启
esac
printf '%s\n' "$v"
}
save_edit_mode() {
local v="$1"
# 由于脚本开始时已创建,这里只需要保证文件写入成功
printf '%s\n' "$v" >"$EDIT_MODE_FILE"
}
detect_backend() {
case "$BACKEND_MODE" in
niri) echo "niri" ;;
grim) echo "grim" ;;
auto|*)
if command -v niri >/dev/null 2>&1 && pgrep -x niri >/dev/null 2>&1; then
echo "niri"
else
echo "grim"
fi
;;
esac
}
choose_editor() {
local choice
choice="$(menu_prompt "$PROMPT_EDITOR" "swappy" "satty" "$LABEL_BACK")"
case "$choice" in
swappy|Swappy)
SHOTEDITOR="swappy"
save_editor "$SHOTEDITOR"
;;
satty|Satty)
SHOTEDITOR="satty"
save_editor "$SHOTEDITOR"
;;
*) : ;;
esac
}
choose_backend_mode() {
local choice
choice="$(menu_prompt "$PROMPT_BACKEND" "$LABEL_BACKEND_AUTO" "$LABEL_BACKEND_GRIM" "$LABEL_BACK")"
case "$choice" in
"$LABEL_BACKEND_AUTO")
BACKEND_MODE="auto"
save_backend_mode "$BACKEND_MODE"
;;
"$LABEL_BACKEND_GRIM")
BACKEND_MODE="grim"
save_backend_mode "$BACKEND_MODE"
;;
*) : ;;
esac
}
settings_menu() {
while :; do
local backend_desc editor_line backend_line choice
if [[ -n "${SHOT_BACKEND:-}" ]]; then
backend_desc="${BACKEND_MODE} (env)"
else
if [[ "$BACKEND_MODE" == "grim" ]]; then
backend_desc="$LABEL_BACKEND_GRIM"
elif [[ "$BACKEND_MODE" == "niri" ]]; then
backend_desc="niri"
else
backend_desc="$LABEL_BACKEND_AUTO"
fi
fi
editor_line="$LABEL_SETTINGS_EDITOR: $SHOTEDITOR"
backend_line="$LABEL_SETTINGS_BACKEND: $backend_desc"
choice="$(menu_prompt "$PROMPT_SETTINGS" "$editor_line" "$backend_line" "$LABEL_BACK")"
case "$choice" in
"$editor_line") choose_editor ;;
"$backend_line")
if [[ -n "${SHOT_BACKEND:-}" ]]; then
: # 环境变量强制时不改持久化
else
choose_backend_mode
fi
;;
*) return ;; # 返回上一层
esac
done
}
latest_in_dir() {
local dir="$1"
find "$dir" -maxdepth 1 -type f -printf '%T@ %p\n' 2>/dev/null \
| sort -n | tail -1 | cut -d' ' -f2-
}
########################
# 剪贴板相关Niri 编辑用)
########################
clip_hash() {
wl-paste -t image/png 2>/dev/null \
| sha1sum 2>/dev/null \
| cut -d' ' -f1 2>/dev/null \
|| echo ""
}
wait_clipboard_change() {
local old new i
old="$(clip_hash)"
for i in {1..200}; do # 最多等 ~10 秒
new="$(clip_hash)"
if [[ -n "$new" && "$new" != "$old" ]]; then
return 0
fi
sleep 0.05
done
return 1
}
########################
# Niri 相关
########################
get_niri_shot_dir() {
[[ -f "$NIRI_CONFIG" ]] || { echo "Config not found: $NIRI_CONFIG" >&2; return 1; }
local line tpl dir
line="$(
grep -E '^[[:space:]]*screenshot-path[[:space:]]' "$NIRI_CONFIG" \
| grep -v '^[[:space:]]*//' \
| tail -n 1 || true
)"
[[ -n "$line" ]] || { echo "No screenshot-path in config" >&2; return 1; }
tpl="$(sed -E 's/.*screenshot-path[[:space:]]+"([^"]+)".*/\1/' <<<"$line")"
[[ -n "$tpl" ]] || { echo "Failed to parse screenshot-path: $line" >&2; return 1; }
tpl="${tpl/#\~/$HOME}"
dir="${tpl%/*}"
printf '%s\n' "$dir"
}
# Grim 用:从文件编辑
edit_file_image() {
local src="$1"
local backend="$2" # "niri" 或 "grim"
local dir ts dst
if [[ "$backend" == "niri" ]]; then
dir="$NIRI_EDIT_DIR"
else
dir="$SCREEN_DIR"
fi
mkdir -p "$dir"
ts="$(date +'%Y-%m-%d_%H-%M-%S')"
dst="$dir/$SHOTEDITOR-$ts.png"
case "$SHOTEDITOR" in
satty)
satty --filename "$src" --output-filename "$dst"
;;
swappy)
swappy -f "$src" -o "$dst"
;;
*)
echo "Unknown SHOTEDITOR: $SHOTEDITOR (use satty or swappy)" >&2
return 0
;;
esac
if [[ -f "$dst" ]]; then
if [[ "$backend" == "grim" && "$src" == /tmp/* ]]; then
rm -f "$src"
fi
"$COPY_CMD" < "$dst"
fi
}
# Niri 用:从剪贴板编辑
edit_from_clipboard() {
local backend="$1" # 目前只会传 "niri"
local dir ts dst
if [[ "$backend" == "niri" ]]; then
dir="$NIRI_EDIT_DIR"
else
dir="$SCREEN_DIR"
fi
mkdir -p "$dir"
ts="$(date +'%Y-%m-%d_%H-%M-%S')"
dst="$dir/$SHOTEDITOR-$ts.png"
case "$SHOTEDITOR" in
satty)
wl-paste -t image/png 2>/dev/null | satty -f - --output-filename "$dst"
;;
swappy)
wl-paste -t image/png 2>/dev/null | swappy -f - -o "$dst"
;;
*)
echo "Unknown SHOTEDITOR: $SHOTEDITOR (use satty or swappy)" >&2
return 0
;;
esac
if [[ -f "$dst" ]]; then
"$COPY_CMD" < "$dst"
fi
}
niri_capture_and_maybe_edit() {
local mode="$1" # fullscreen / window / region
local need_edit="$2" # yes / no
local action
case "$mode" in
fullscreen) action="screenshot-screen" ;;
window) action="screenshot-window" ;;
region) action="screenshot" ;;
*) return 0 ;;
esac
# 不编辑:用目录里的最新文件判断 screenshot 完成
if [[ "$need_edit" != "yes" ]]; then
local before shot
before="$(latest_in_dir "$NIRI_SHOT_DIR" || true)"
niri msg action "$action"
while :; do
shot="$(latest_in_dir "$NIRI_SHOT_DIR" || true)"
if [[ -z "$before" && -n "$shot" ]] || \
[[ -n "$before" && -n "$shot" && "$shot" != "$before" ]]; then
break
fi
sleep 0.05
done
return 0
fi
# 编辑:基于剪贴板
niri msg action "$action"
if ! wait_clipboard_change; then
echo "等待剪贴板中的截图超时" >&2
return 0
fi
edit_from_clipboard "niri"
return 0
}
run_niri_flow() {
NIRI_SHOT_DIR="$(get_niri_shot_dir)" || return 0
NIRI_EDIT_DIR="$NIRI_SHOT_DIR/Edited"
mkdir -p "$NIRI_SHOT_DIR" "$NIRI_EDIT_DIR"
while :; do
local choice mode edit_mode edit_label
edit_mode="$(load_edit_mode)"
if [[ "$edit_mode" == "yes" ]]; then
edit_label="$LABEL_EDIT_STATE_ON"
else
edit_label="$LABEL_EDIT_STATE_OFF"
fi
choice="$(menu_prompt "$PROMPT_MAIN" \
"$LABEL_NIRI_FULL" \
"$LABEL_NIRI_WINDOW" \
"$LABEL_NIRI_REGION" \
"$edit_label" \
"$LABEL_SETTINGS" \
"$LABEL_CANCEL"
)"
[[ -z "$choice" || "$choice" == "$LABEL_CANCEL" ]] && return 2
case "$choice" in
"$LABEL_NIRI_FULL") mode="fullscreen" ;;
"$LABEL_NIRI_WINDOW") mode="window" ;;
"$LABEL_NIRI_REGION") mode="region" ;;
"$edit_label")
if [[ "$edit_mode" == "yes" ]]; then
save_edit_mode "no"
else
save_edit_mode "yes"
fi
continue
;;
"$LABEL_SETTINGS")
# 进入设置菜单(可以在里面改 editor / backend / edit-mode
settings_menu
# 立即重新加载持久化配置,使修改即时生效
SHOTEDITOR="$(load_editor)"
BACKEND_MODE="$(load_backend_mode)"
# 检查后端是否被改动;若改动则通知上层切换后端
NEW_BACKEND="$(detect_backend)"
if [[ "$NEW_BACKEND" != "niri" ]]; then
return 1
fi
continue
;;
*)
return 2
;;
esac
edit_mode="$(load_edit_mode)"
if [[ "$edit_mode" == "yes" ]]; then
niri_capture_and_maybe_edit "$mode" "yes"
else
niri_capture_and_maybe_edit "$mode" "no"
fi
# 截图完成后退出(主循环会根据返回码决定是否结束脚本)
return 0
done
}
########################
# Grim + slurp 相关
########################
grim_capture_and_maybe_edit() {
local mode="$1" # fullscreen / region
local need_edit="$2" # yes / no
mkdir -p "$SCREEN_DIR"
local ts shot geo
ts="$(date +'%Y-%m-%d_%H-%M-%S')"
if [[ "$need_edit" == "yes" ]]; then
# 编辑模式:原图在 /tmp用完删只保留编辑后的图
shot="/tmp/waybar-shot-$ts.png"
case "$mode" in
fullscreen)
grim "$shot"
;;
region)
geo="$(slurp 2>/dev/null)" || return 0
grim -g "$geo" "$shot"
;;
*)
return 0 ;;
esac
edit_file_image "$shot" "grim"
return 0
else
# 不编辑:原图保存到 Screenshots
shot="$SCREEN_DIR/Screenshot_$ts.png"
case "$mode" in
fullscreen)
grim "$shot"
;;
region)
geo="$(slurp 2>/dev/null)" || return 0
grim -g "$geo" "$shot"
;;
*)
return 0 ;;
esac
return 0
fi
}
run_grim_flow() {
mkdir -p "$SCREEN_DIR"
while :; do
local choice mode edit_mode edit_label
edit_mode="$(load_edit_mode)"
if [[ "$edit_mode" == "yes" ]]; then
edit_label="$LABEL_EDIT_STATE_ON"
else
edit_label="$LABEL_EDIT_STATE_OFF"
fi
choice="$(menu_prompt "$PROMPT_MAIN" \
"$LABEL_GRIM_FULL" \
"$LABEL_GRIM_REGION" \
"$edit_label" \
"$LABEL_SETTINGS" \
"$LABEL_CANCEL"
)"
[[ -z "$choice" || "$choice" == "$LABEL_CANCEL" ]] && return 2
case "$choice" in
"$LABEL_GRIM_FULL") mode="fullscreen" ;;
"$LABEL_GRIM_REGION") mode="region" ;;
"$edit_label")
if [[ "$edit_mode" == "yes" ]]; then
save_edit_mode "no"
else
save_edit_mode "yes"
fi
continue
;;
"$LABEL_SETTINGS")
settings_menu
# 立即重新加载持久化配置,使修改即时生效
SHOTEDITOR="$(load_editor)"
BACKEND_MODE="$(load_backend_mode)"
NEW_BACKEND="$(detect_backend)"
if [[ "$NEW_BACKEND" != "grim" ]]; then
return 1
fi
continue
;;
*)
return 2
;;
esac
edit_mode="$(load_edit_mode)"
if [[ "$edit_mode" == "yes" ]]; then
grim_capture_and_maybe_edit "$mode" "yes"
else
grim_capture_and_maybe_edit "$mode" "no"
fi
return 0
done
}
########################
# 入口(主循环)
########################
while :; do
BACKEND_MODE="$(load_backend_mode)"
SHOTEDITOR="$(load_editor)"
BACKEND="$(detect_backend)"
case "$BACKEND" in
niri)
rc=0
run_niri_flow || rc=$?
if [[ "$rc" -eq 0 ]]; then
exit 0
elif [[ "$rc" -eq 1 ]]; then
# 后端切换:继续主循环以根据新后端重试
continue
else
# 取消或其他:退出
exit 0
fi
;;
grim)
rc=0
run_grim_flow || rc=$?
if [[ "$rc" -eq 0 ]]; then
exit 0
elif [[ "$rc" -eq 1 ]]; then
continue
else
exit 0
fi
;;
*)
run_grim_flow
exit 0
;;
esac
done