aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRubén Beltrán del Río <jj@r.bdr.sh>2026-03-14 12:40:57 +0100
committerRubén Beltrán del Río <jj@r.bdr.sh>2026-03-15 21:37:04 +0100
commit68fc4436c2565ff31f1499d868ac71bc560121ee (patch)
treea9558bc8772631ca6bed55b1e38f3266669a61b0
parent2cf84c3a5654a267b4628c443e205d1f8bef759a (diff)
Add contacts picker
-rw-r--r--config/niri/config.kdl2
-rwxr-xr-xscripts/contacts.sh57
2 files changed, 59 insertions, 0 deletions
diff --git a/config/niri/config.kdl b/config/niri/config.kdl
index 6cf18fc..b7f4c38 100644
--- a/config/niri/config.kdl
+++ b/config/niri/config.kdl
@@ -415,6 +415,8 @@ binds {
Ctrl+XF86MonBrightnessDown allow-when-locked=true { spawn "bash" "-c" "brightnessctl set 5%- -d 'kbd_backlight'| sed -En 's/.*\\(([0-9]+)%\\).*/\\1/p' > $XDG_RUNTIME_DIR/wob.sock"; }
Ctrl+XF86MonBrightnessUp allow-when-locked=true { spawn "bash" "-c" "brightnessctl set +5% -d 'kbd_backlight' | sed -En 's/.*\\(([0-9]+)%\\).*/\\1/p' > $XDG_RUNTIME_DIR/wob.sock"; }
+ XF86Search hotkey-overlay-title="Contacts" { spawn "bash" "-c" "~/.dotfiles/scripts/contacts.sh"; }
+
Mod+V hotkey-overlay-title="Open Clipboard Manager" { spawn "bash" "-c" "cliphist list | bemenu --single-instance -i -l 10 -n -H 26 --hp 10 --fn 'InputSansCompressed Light 10' -p '>' --ff '#0f261F' --tf '#23C17C' --nf '#0f261F' --cf '#23C17C2' --af '#00140d' --hb '#23C17C' --hf '#0f261F' --hf '#DAE6E3' --nb '#DAE6E3' --tb '#DAE6E3' --fb '#DAE6E3' --cb '#DAE6E3' --ab '#DAE6E3' | cliphist decode | wl-copy"; }
Mod+grave hotkey-overlay-title="Quick Terminal" { spawn "kitten" "quick-access-terminal"; }
diff --git a/scripts/contacts.sh b/scripts/contacts.sh
new file mode 100755
index 0000000..b13bdfb
--- /dev/null
+++ b/scripts/contacts.sh
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+
+export BEMENU_OPTS='--single-instance -i -n -H 26 --hp 10 --fn "InputSansCompressed Light 10" -p ">" --ff "#0f261F" --tf "#23C17C" --nf "#0f261F" --cf "#23C17C2" --af "#00140d" --hb "#23C17C" --hf "#DAE6E3" --nb "#DAE6E3" --tb "#DAE6E3" --fb "#DAE6E3" --cb "#DAE6E3" --ab "#DAE6E3"'
+
+CACHE="$HOME/.cache/khard-contacts.tsv"
+CACHE_MAX_AGE=30
+
+if [[ ! -f "$CACHE" ]] || [[ -n "$(find "$CACHE" -mmin +"$CACHE_MAX_AGE" -print)" ]]; then
+ khard list --parsable --fields uid,name --search-in-source-files | tail -n +2 > "$CACHE.tmp" \
+ && mv "$CACHE.tmp" "$CACHE"
+fi
+
+declare -A contacts
+while IFS=$'\t' read -r uid name; do
+ contacts["$name"]="$uid"
+done < "$CACHE"
+
+name=$(printf '%s\n' "${!contacts[@]}" | sort | bemenu)
+[[ -z "$name" ]] && exit 1
+
+selection=$(
+ khard show --format=yaml "uid:${contacts[$name]}" --search-in-source-files \
+ | python3 -c '
+import sys, yaml
+
+data = yaml.safe_load(sys.stdin)
+fields = [
+ "Formatted name", "First name", "Last name",
+ "Nickname", "Birthday", "Phone", "Email", "Address"
+]
+
+def leaves(val):
+ if isinstance(val, dict):
+ for v in val.values():
+ yield from leaves(v)
+ elif isinstance(val, list):
+ for v in val:
+ yield from leaves(v)
+ else:
+ if val is not None:
+ yield str(val)
+
+seen = set()
+for key in fields:
+ if key in data:
+ for leaf in leaves(data[key]):
+ leaf = leaf.strip()
+ if leaf and leaf not in seen:
+ seen.add(leaf)
+ print(leaf)
+' \
+ | bemenu
+)
+[[ -z "$selection" ]] && exit 1
+
+printf '%s' "$selection" | wl-copy
+notify-send "Copied to clipboard" "$selection"