feat: implement notification color swatch and dependency verification #8

Merged
rootiest merged 2 commits from feat/notify-swatch into main 2026-05-03 18:09:55 +00:00
3 changed files with 112 additions and 8 deletions
+3 -1
View File
@@ -10,7 +10,7 @@ A feature-rich, portable CLI color utility for Linux, specializing in color pick
- **Smart JSON Output:** Generate machine-readable JSON tables for easy integration with other tools.
- **Color Naming:** Resolve hex values to human-readable names via thecolorapi.com.
- **Visual Previews:** Render color swatches directly in your terminal using truecolor ANSI escapes.
- **Desktop Integration:** Built-in support for clipboard copying (`wl-copy`/`xclip`) and desktop notifications.
- **Desktop Integration:** Built-in support for clipboard copying (`wl-copy`/`xclip`) and rich desktop notifications with visual color swatches.
- **Highly Configurable:** Separate configuration profiles for standard terminal usage and desktop/launcher mode.
- **Portable Design:** The entire tool is contained within a single Bash script, with embedded Python helpers.
@@ -22,6 +22,7 @@ To utilize all features, ensure the following are installed:
- **curl + jq:** Required for color naming support.
- **wl-clipboard** (Wayland) or **xclip** (X11): Required for clipboard support.
- **libnotify:** Required for desktop notifications (`notify-send`).
- **ImageMagick (`magick` or `convert`):** Required for generating visual color swatches in desktop notifications.
## 🚀 Installation
@@ -37,6 +38,7 @@ This will:
2. Symlink the binary to `~/.local/bin/color-tool`.
3. Generate a sample configuration at `~/.config/color-tool/config.toml`.
4. Create a `.desktop` entry so you can launch the picker from your application menu.
5. Verify and warn about any missing optional or required system dependencies.
## 🛠 Usage
+83 -7
View File
@@ -45,6 +45,7 @@ desktop_name=0
desktop_notify=1
desktop_copy=1
desktop_output="hex"
desktop_swatch=0
# Track which settings were explicitly set via CLI flags to ensure they override config
cli_json=""
@@ -242,6 +243,7 @@ load_config() {
desktop:alpha) [[ "$val" == "true" ]] && desktop_alpha=1 || desktop_alpha=0 ;;
desktop:name) [[ "$val" == "true" ]] && desktop_name=1 || desktop_name=0 ;;
desktop:notify) [[ "$val" == "true" ]] && desktop_notify=1 || desktop_notify=0 ;;
desktop:swatch) [[ "$val" == "true" ]] && desktop_swatch=1 || desktop_swatch=0 ;;
desktop:copy) [[ "$val" == "true" ]] && desktop_copy=1 || desktop_copy=0 ;;
desktop:output) desktop_output="$val" ;;
esac
@@ -272,6 +274,7 @@ json = false # copy JSON format instead of plain text
name = false # fetch color name (requires network)
copy = true # copy result to clipboard
notify = true # show desktop notification with the copied value
swatch = false # show color swatch in notification
EOF
}
@@ -319,7 +322,7 @@ show_help() {
printf " ${bold}${yellow}Config${reset} ${dim}~/.config/color-tool/config.toml${reset}\n"
printf " ${dim}[defaults]${reset} keys: ${cyan}output json swatch name copy pick notify${reset}\n"
printf " ${dim}[desktop]${reset} keys: ${cyan}output json name notify copy${reset} ${dim}(pick always on)${reset}\n\n"
printf " ${dim}[desktop]${reset} keys: ${cyan}output json name notify copy swatch${reset} ${dim}(pick always on)${reset}\n\n"
}
# ── Environment detection ─────────────────────────────────────────────────────
@@ -353,8 +356,24 @@ copy_to_clipboard() {
notify_result() {
local value="$1"
local hex_color="${2:-}"
command -v notify-send &>/dev/null || return 0
notify-send -i "color-picker" "color-tool" "$value" || true
local icon_name="color-picker"
if [[ $swatch_mode -eq 1 && -n "$hex_color" ]]; then
local swatch_path="/tmp/color_swatch_${hex_color//\#/}.png"
if command -v magick >/dev/null 2>&1; then
if magick -size 64x64 xc:"$hex_color" "$swatch_path" 2>/dev/null; then
icon_name="$swatch_path"
fi
elif command -v convert >/dev/null 2>&1; then
if convert -size 64x64 xc:"$hex_color" "$swatch_path" 2>/dev/null; then
icon_name="$swatch_path"
fi
fi
fi
notify-send -i "$icon_name" "color-tool" "$value" || true
}
notify_error() {
@@ -448,6 +467,60 @@ suggest_path_add() {
fi
}
check_dependencies() {
local missing=()
if ! command -v magick >/dev/null 2>&1 && ! command -v convert >/dev/null 2>&1; then
missing+=("ImageMagick (magick or convert) — required for notification color swatches")
fi
if ! command -v wl-copy >/dev/null 2>&1 && ! command -v xclip >/dev/null 2>&1; then
missing+=("wl-clipboard (wl-copy) or xclip — required for clipboard copy support")
fi
if ! command -v python3 >/dev/null 2>&1; then
missing+=("python3 — required for color conversions and picker GUI")
elif ! python3 -c "import PyQt6.QtDBus" >/dev/null 2>&1; then
missing+=("python3-pyqt6 — required for the KDE color picker DBus interface")
fi
if ! command -v jq >/dev/null 2>&1; then
missing+=("jq — required for JSON formatting and color names")
fi
if ! command -v curl >/dev/null 2>&1; then
missing+=("curl — required for fetching color names from the API")
fi
if ! command -v notify-send >/dev/null 2>&1; then
missing+=("libnotify (notify-send) — required for desktop notifications")
fi
local env_ok=1
if [[ -z "${WAYLAND_DISPLAY:-}" ]]; then
env_ok=0
fi
local desktop="${XDG_CURRENT_DESKTOP:-}"
if [[ "$desktop" != *KDE* ]] && [[ -z "${KDE_FULL_SESSION:-}" ]]; then
env_ok=0
fi
if [[ $env_ok -eq 0 ]]; then
missing+=("KDE Plasma on Wayland — required for the screen color picker feature")
fi
if [[ ${#missing[@]} -gt 0 ]]; then
local yellow='\033[33m'
local bold='\033[1m'
local reset='\033[0m'
printf "\n ${bold}${yellow}Warnings — Missing optional dependencies:${reset}\n"
for item in "${missing[@]}"; do
printf " - %s\n" "$item"
done
printf " ${yellow}Functionality will be limited without these installed.${reset}\n"
fi
}
do_install() {
local data_dir="$HOME/.local/share/color-tool"
local bin_dir="$HOME/.local/bin"
@@ -497,6 +570,8 @@ EOF
printf "\nNote: %s is not in your PATH.\n" "$bin_dir"
suggest_path_add "$bin_dir"
fi
check_dependencies
}
# ── Color conversion ──────────────────────────────────────────────────────────
@@ -571,10 +646,12 @@ process_color() {
return 1
}
local hex_color
hex_color=$(echo "$formats_json" | jq -r '.hex // empty')
local name=""
if [[ $name_mode -eq 1 ]]; then
local hex_for_api
hex_for_api=$(echo "$formats_json" | jq -r '.hex' | tr -d '#')
local hex_for_api="${hex_color//#/}"
name=$(curl -s "https://www.thecolorapi.com/id?hex=$hex_for_api" | jq -r '.name.value // empty')
fi
@@ -640,7 +717,7 @@ process_color() {
Value: $output_text"
else
notify_result "$output_text"
notify_result "$output_text" "$hex_color"
fi
fi
}
@@ -706,7 +783,6 @@ while [[ $# -gt 0 ]]; do
shift
done
# Apply final hierarchy: CLI Flags > Desktop Config (if --desktop) > Default Config
if [[ $desktop_mode -eq 1 ]]; then
json_mode=${cli_json:-$desktop_json}
alpha_mode=${cli_alpha:-$desktop_alpha}
@@ -714,8 +790,8 @@ if [[ $desktop_mode -eq 1 ]]; then
notify_mode=${cli_notify:-$desktop_notify}
output_formats=${cli_output:-$desktop_output}
copy_mode=${cli_copy:-$desktop_copy}
swatch_mode=${cli_swatch:-$desktop_swatch}
do_pick=${cli_pick:-1}
swatch_mode=${cli_swatch:-0} # swatch usually off in desktop mode
else
json_mode=${cli_json:-$json_mode}
alpha_mode=${cli_alpha:-$alpha_mode}
+26
View File
@@ -206,6 +206,32 @@ it "rejects invalid formats"
output=$("$COLOR_TOOL" "#000000" --output badfmt --no-copy --no-notify 2>&1 || true)
assert_contains "$output" "Error: Invalid output format: badfmt"
# 14. Installation Dependency Warnings
it "warns about missing dependencies during install"
mkdir -p "$TEST_DIR/install_bin"
for cmd in bash cat echo printf mkdir mktemp chmod rm cp ln dirname readlink ps; do
if [[ -e "/usr/bin/$cmd" || -L "/usr/bin/$cmd" ]]; then
/usr/bin/ln -s "/usr/bin/$cmd" "$TEST_DIR/install_bin/$cmd"
elif [[ -e "/bin/$cmd" || -L "/bin/$cmd" ]]; then
/usr/bin/ln -s "/bin/$cmd" "$TEST_DIR/install_bin/$cmd"
fi
done
output=$(PATH="$TEST_DIR/install_bin" HOME="$TEST_DIR" WAYLAND_DISPLAY="" XDG_CURRENT_DESKTOP="" KDE_FULL_SESSION="" "$COLOR_TOOL" --install 2>&1 || true)
if [[ "$output" == *"python3 — required"* ]] &&
[[ "$output" == *"jq — required"* ]] &&
[[ "$output" == *"curl — required"* ]] &&
[[ "$output" == *"libnotify (notify-send)"* ]] &&
[[ "$output" == *"KDE Plasma on Wayland"* ]] &&
[[ "$output" == *"ImageMagick"* ]] &&
[[ "$output" == *"wl-clipboard"* ]]; then
echo "PASS"
passed=$((passed + 1))
else
echo "FAIL"
echo " Actual output: $output"
fi
# ── Cleanup ───────────────────────────────────────────────────────────────────
echo "---------------------------------------"
echo "Result: $passed/$total tests passed."