diff --git a/README.md b/README.md index 70a0cac..b432c85 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/color-tool b/color-tool index 9df24bd..96f09e4 100755 --- a/color-tool +++ b/color-tool @@ -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} diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 12d3037..a8283ab 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -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."