1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
|
#!/usr/bin/env bash
# ==================================================
# KoolDots (2026)
# Project URL: https://github.com/LinuxBeginnings
# License: GNU GPLv3
# SPDX-License-Identifier: GPL-3.0-or-later
# ==================================================
# Smooth border cycling effect using Wallust palette or full rainbow
#
# Disabled by default as RainbowBorders.bak.sh.
# Enable / choose mode via Quick Settings (SUPER SHIFT + E → Rainbow Borders Mode).
# That renames this file to RainbowBorders.sh.
#
# Under Hyprland Lua config mode, hyprctl keyword is rejected
# ("keyword can't work with non-legacy parsers"). This script falls back to:
# hyprctl eval 'hl.config({ general = { col = { active_border = { colors = {...}, angle = N } } } })'
# Possible values: "wallust_random", "rainbow", "gradient_flow"
EFFECT_TYPE="gradient_flow"
WALLUST_COLORS_SOURCE="${XDG_CONFIG_HOME:-$HOME/.config}/hypr/wallust/wallust-hyprland.conf"
WALLUST_COLORS=()
# ---------- LOAD WALLUST COLORS ----------
if [[ "$EFFECT_TYPE" == "wallust_random" || "$EFFECT_TYPE" == "gradient_flow" ]]; then
# Accept either hex (0xffRRGGBB) or rgb(r,g,b) and normalize to 0xffRRGGBB
mapfile -t WALLUST_COLORS < <(
grep -E '^\$color[0-9]+' "$WALLUST_COLORS_SOURCE" | awk '
function hex2(s){ return (length(s)==6 ? "0xff"s : ""); }
function rgb2(r,g,b){ return sprintf("0xff%02x%02x%02x", r, g, b); }
{
if (match($0, /0x([0-9a-fA-F]{8})/, m)) { print "0x" m[1]; next }
if (match($0, /#([0-9a-fA-F]{6})/, m)) { print hex2(m[1]); next }
if (match($0, /rgb\(([0-9]+),[ ]*([0-9]+),[ ]*([0-9]+)\)/, m)) {
print rgb2(m[1], m[2], m[3]); next
}
}'
)
if ((${#WALLUST_COLORS[@]} == 0)); then
# If wallust colors can't be loaded, fall back to random_hex
EFFECT_TYPE="rainbow"
fi
fi
# ---------- RANDOM WALLUST COLORS ----------
function wallust_random() {
echo "${WALLUST_COLORS[RANDOM % ${#WALLUST_COLORS[@]}]}"
}
# ---------- RAINBOW COLORS ----------
# Fixed spectrum so "Original Rainbow" is visibly multi-hue (not random pastels).
RAINBOW_PALETTE=(
"0xffff0000" "0xffff8000" "0xffffff00" "0xff80ff00" "0xff00ff00"
"0xff00ff80" "0xff00ffff" "0xff0080ff" "0xff0000ff" "0xff8000ff"
)
function random_hex() {
# Keep name for compatibility; prefer palette entry by index when available.
if ((${#RAINBOW_PALETTE[@]} > 0)); then
echo "${RAINBOW_PALETTE[RANDOM % ${#RAINBOW_PALETTE[@]}]}"
return
fi
echo "0xff$(openssl rand -hex 3)"
}
function rainbow_color() {
local idx="${1:-0}"
if ((${#RAINBOW_PALETTE[@]} > 0)); then
echo "${RAINBOW_PALETTE[idx % ${#RAINBOW_PALETTE[@]}]}"
return
fi
random_hex
}
# ---------- FLOW MODE ----------
BASE_COLOR="${WALLUST_COLORS[10]}"
GRAD1_COLOR="${WALLUST_COLORS[14]}"
GRAD2_COLOR="${WALLUST_COLORS[13]}"
GLOW_COLOR="${WALLUST_COLORS[15]}"
MAX_POS=10
GLOW_POS=0
function gradient_flow_color() {
local pos=$1
local d=$((pos - GLOW_POS))
# wrap distance (-9..9)
if ((d > MAX_POS / 2)); then d=$((d - MAX_POS)); fi
if ((d < -MAX_POS / 2)); then d=$((d + MAX_POS)); fi
case "${d#-}" in
0) echo "$GLOW_COLOR" ;;
1) echo "$GRAD1_COLOR" ;;
2) echo "$GRAD2_COLOR" ;;
*) echo "$BASE_COLOR" ;;
esac
if ((pos == MAX_POS - 1)); then
GLOW_POS=$(((GLOW_POS + 1) % MAX_POS))
fi
}
# ---------- Main function ----------
function get_color() {
if [[ "$EFFECT_TYPE" == "wallust_random" && ${#WALLUST_COLORS[@]} -gt 0 ]]; then
wallust_random
elif [[ "$EFFECT_TYPE" == "gradient_flow" && ${#WALLUST_COLORS[@]} -ge 16 ]]; then
gradient_flow_color "$1"
elif [[ "$EFFECT_TYPE" == "rainbow" ]]; then
rainbow_color "$1"
else
random_hex
fi
}
# Set a gradient border option for both legacy hyprlang and Lua config modes.
# Usage: set_gradient_border general:col.active_border color0 color1 ... [angledeg]
set_gradient_border() {
local option="$1"
shift
local angle="270"
local -a colors=()
local arg c lua_colors expr out
for arg in "$@"; do
if [[ "$arg" =~ ^([0-9]+)deg$ ]]; then
angle="${BASH_REMATCH[1]}"
else
colors+=("$arg")
fi
done
((${#colors[@]} > 0)) || return 1
# Legacy conf mode.
# Note: under Lua config mode hyprctl may exit 0 while still printing
# "keyword can't work with non-legacy parsers", so check output too.
out="$(hyprctl keyword "$option" "${colors[@]}" "${angle}deg" 2>&1)"
rc=$?
if [[ $rc -eq 0 && "$out" != *"non-legacy parsers"* && "$out" != *"Use eval"* && "$out" != *"error"* && "$out" != *"invalid"* ]]; then
return 0
fi
# Lua config mode rejects keyword; use hl.config via eval.
lua_colors=""
for c in "${colors[@]}"; do
[[ -n "$lua_colors" ]] && lua_colors+=", "
lua_colors+="\"${c}\""
done
case "$option" in
general:col.active_border)
expr="hl.config({ general = { col = { active_border = { colors = { ${lua_colors} }, angle = ${angle} } } } })"
;;
general:col.inactive_border)
expr="hl.config({ general = { col = { inactive_border = { colors = { ${lua_colors} }, angle = ${angle} } } } })"
;;
*)
printf '[RainbowBorders] unsupported option for Lua fallback: %s\n' "$option" >&2
printf '[RainbowBorders] keyword error: %s\n' "$out" >&2
return 1
;;
esac
if out="$(hyprctl eval "$expr" 2>&1)"; then
return 0
fi
printf '[RainbowBorders] failed to set %s\n' "$option" >&2
printf '[RainbowBorders] keyword error: %s\n' "$out" >&2
printf '[RainbowBorders] eval error: %s\n' "$out" >&2
return 1
}
# border effect for ACTIVE window
set_gradient_border general:col.active_border \
"$(get_color 0)" "$(get_color 1)" "$(get_color 2)" "$(get_color 3)" "$(get_color 4)" \
"$(get_color 5)" "$(get_color 6)" "$(get_color 7)" "$(get_color 8)" "$(get_color 9)" \
270deg
# border effect for INACTIVE windows (optional)
# set_gradient_border general:col.inactive_border \
# "$(get_color 0)" "$(get_color 1)" "$(get_color 2)" "$(get_color 3)" "$(get_color 4)" \
# "$(get_color 5)" "$(get_color 6)" "$(get_color 7)" "$(get_color 8)" "$(get_color 9)" \
# 270deg
|