12 Commits

Author SHA1 Message Date
rootiest 2b58cc8a13 Merge pull request 'feat(config): enable desktop swatch and improve comment alignment' (#11) from feat/desktop-swatch-and-config-alignment into main
Reviewed-on: #11
2026-05-03 18:58:02 +00:00
rootiest e0967a8388 feat(config): maintain comment alignment in set_config
Auto Label PRs / label (pull_request) Successful in 3s
Test PR / test (pull_request) Successful in 6s
Release on Merge / release (pull_request) Successful in 6s
Updated set_config to dynamically adjust leading whitespace of trailing
comments when toggling between true/false (or any values of different
lengths). This ensures that inline comments in config.toml remain
vertically aligned after programmatic updates.
2026-05-03 14:57:02 -04:00
rootiest 8d94bc4006 feat(config): enable swatch by default for desktop mode
Set swatch = true as the default in the stock [desktop] configuration
and internal defaults. This ensures that desktop notifications include
a visual color swatch by default. Updated README and tests accordingly.
2026-05-03 14:56:31 -04:00
rootiest 10014d5ee9 Merge pull request 'fix(help): clarify --swatch applies to terminal output and desktop notifications' (#10) from fix/swatch-help-text into main
Reviewed-on: #10
2026-05-03 18:30:19 +00:00
rootiest 8333d249d9 fix(help): clarify --swatch applies to terminal and notifications
Auto Label PRs / label (pull_request) Successful in 3s
Test PR / test (pull_request) Successful in 6s
Release on Merge / release (pull_request) Successful in 6s
The --swatch flag now controls both the inline terminal color block and
the desktop notification icon (via ImageMagick). Update the help text
to reflect this so users know the flag affects both output contexts.
2026-05-03 14:29:30 -04:00
rootiest 383d547a32 Merge pull request 'docs: update curl download instructions from latest release' (#9) from docs/update-download-url into main
Reviewed-on: #9
2026-05-03 18:22:54 +00:00
rootiest 1d92dc911e docs: add curl download instructions from latest release
Auto Label PRs / label (pull_request) Successful in 3s
Test PR / test (pull_request) Successful in 6s
Release on Merge / release (pull_request) Has been skipped
2026-05-03 14:20:29 -04:00
rootiest 24747230e1 Merge pull request 'feat: implement notification color swatch and dependency verification' (#8) from feat/notify-swatch into main
Reviewed-on: #8
2026-05-03 18:09:54 +00:00
rootiest 867b8ccf26 docs: update README with swatch and dependency checks
Test PR / test (pull_request) Successful in 7s
Release on Merge / release (pull_request) Successful in 6s
2026-05-03 14:08:53 -04:00
rootiest 7c882bb26c feat: implement notification color swatch and dependency verification
Auto Label PRs / label (pull_request) Successful in 3s
Test PR / test (pull_request) Successful in 6s
2026-05-03 14:05:11 -04:00
rootiest f2be6c151e Merge pull request 'ci: add workflow to automatically label PRs based on title' (#7) from ci/auto-label-prs into main 2026-05-03 03:38:42 +00:00
rootiest b78becad5f ci: add workflow to automatically label PRs based on title
Auto Label PRs / label (pull_request) Successful in 3s
Test PR / test (pull_request) Successful in 5s
Release on Merge / release (pull_request) Has been skipped
2026-05-02 23:38:21 -04:00
4 changed files with 185 additions and 11 deletions
+42
View File
@@ -0,0 +1,42 @@
name: Auto Label PRs
on:
pull_request:
types: [opened, edited]
jobs:
label:
runs-on: ubuntu-latest
steps:
- name: Apply labels based on PR title
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO_OWNER: ${{ github.repository_owner }}
REPO_NAME: ${{ github.event.repository.name }}
API_URL: ${{ github.api_url }}
run: |
shopt -s nocasematch
labels="["
if [[ "$PR_TITLE" =~ ^(ci|test)(/|:) ]]; then
labels="${labels}13," # 13 is Kind/Testing
fi
if [[ "$PR_TITLE" =~ ^docs(/|:) ]]; then
labels="${labels}14," # 14 is Kind/Documentation
fi
labels="${labels%,}]"
if [ "$labels" != "[]" ]; then
echo "Adding labels to PR #${PR_NUMBER}: ${labels}"
curl -X POST "${API_URL}/repos/${REPO_OWNER}/${REPO_NAME}/issues/${PR_NUMBER}/labels" \
-H "Authorization: token ${GITHUB_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"labels\": ${labels}}"
else
echo "No matching prefixes found in title: ${PR_TITLE}"
fi
+6 -2
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,12 +22,14 @@ 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
Simply download the `color-tool` script and run the install command:
You can download the script directly from the [latest release](https://git.rootiest.dev/rootiest/color-tool/releases/latest) and install it:
```bash
curl -sSLO https://git.rootiest.dev/rootiest/color-tool/releases/download/latest/color-tool
chmod +x color-tool
./color-tool --install
```
@@ -37,6 +39,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
@@ -103,6 +106,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 = true # show color swatch in notification
```
You can also use the CLI to manage your configuration:
+111 -9
View File
@@ -45,6 +45,7 @@ desktop_name=0
desktop_notify=1
desktop_copy=1
desktop_output="hex"
desktop_swatch=1
# Track which settings were explicitly set via CLI flags to ensure they override config
cli_json=""
@@ -193,7 +194,33 @@ set_config() {
if [[ -n "$new_val" ]]; then
if [[ "$original_line" =~ ^([[:space:]]*[A-Za-z_]+[[:space:]]*=[[:space:]]*)([^[:space:]#]+)(.*)$ ]]; then
echo "${BASH_REMATCH[1]}$new_val${BASH_REMATCH[3]}" >> "$temp_file"
local prefix="${BASH_REMATCH[1]}"
local old_val="${BASH_REMATCH[2]}"
local suffix="${BASH_REMATCH[3]}"
# Adjust whitespace to keep comments aligned if the value length changed
if [[ "$key" != "output" ]]; then
local old_len=${#old_val}
local new_len=${#new_val}
local diff=$((old_len - new_len))
if [[ $diff -gt 0 ]]; then
# new value is shorter, add spaces to suffix
local spaces=""
for ((i=0; i<diff; i++)); do spaces+=" "; done
suffix="$spaces$suffix"
elif [[ $diff -lt 0 ]]; then
# new value is longer, remove spaces from suffix
local remove=$((-diff))
for ((i=0; i<remove; i++)); do
if [[ "$suffix" == " "* ]]; then
suffix="${suffix# }"
fi
done
fi
fi
echo "$prefix$new_val$suffix" >> "$temp_file"
continue
elif [[ "$original_line" =~ ^([[:space:]]*[A-Za-z_]+[[:space:]]*=[[:space:]]*)(.*)$ ]]; then
echo "${BASH_REMATCH[1]}$new_val" >> "$temp_file"
@@ -242,6 +269,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 +300,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 = true # show color swatch in notification
EOF
}
@@ -302,7 +331,7 @@ show_help() {
printf " ${bold}${cyan}--output${reset} ${dim}FMT${reset} Format(s) to output: ${cyan}hex, rgb, hsl, rgba, hsla, hexa, all${reset} ${dim}(comma-separated)${reset}\n"
printf " ${bold}${cyan}--[no-]json${reset} Output as a JSON table of selected formats\n"
printf " ${bold}${cyan}--[no-]name${reset} Fetch nearest color name from thecolorapi.com ${dim}(requires curl, jq)${reset}\n"
printf " ${bold}${cyan}--[no-]swatch${reset} Include a color swatch in the terminal output\n"
printf " ${bold}${cyan}--[no-]swatch${reset} Include a color swatch in the terminal output and desktop notification\n"
printf " ${bold}${cyan}--[no-]copy${reset} Copy result to clipboard ${dim}(wl-clipboard preferred, xclip as fallback)${reset}\n"
printf " ${bold}${cyan}--[no-]notify${reset} Show desktop notification ${dim}(on by default in --desktop)${reset}\n"
printf " ${bold}${cyan}--desktop${reset} GUI mode: pick → copy → notify ${dim}(for app menu / .desktop launcher)${reset}\n"
@@ -319,7 +348,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 +382,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 +493,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 +596,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 +672,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 +743,7 @@ process_color() {
Value: $output_text"
else
notify_result "$output_text"
notify_result "$output_text" "$hex_color"
fi
fi
}
@@ -706,7 +809,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 +816,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."