Give Input keycaps, a shortcut search, and the missing pointer basics

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Gabriel Brown
2026-08-24 15:21:01 -04:00
parent b8f88a91f3
commit aba2d16ffa
25 changed files with 2346 additions and 191 deletions
+42 -6
View File
@@ -17,6 +17,18 @@
#
# So this checks the schema's enum values against that map, and against the
# min/max range for mapped options that have no named map.
#
# String-valued options are checked too, less strictly and for a reason. They
# carry no `map` at all -- Hyprland states their accepted values inside the
# description, as `input:scroll_method` does with "[2fg/edge/on_button_down/
# no_scroll]". That list is prose, so it is trustworthy in one direction only:
# a value the compositor does not name is a value it will reject or silently
# ignore, which this fails on; a value the compositor names but Settings does
# not offer may simply be one Panama has decided against (accel_profile's
# `custom` needs a scroll_points curve to mean anything, and is a stated
# non-goal), so that direction is not an error here. The empty string is always
# allowed: it is how a schema entry says "leave it at the compositor's default",
# which is what `[[EMPTY]]` reads back as.
set -uo pipefail
@@ -33,7 +45,8 @@ descriptions="$(hyprctl descriptions 2>/dev/null)" || fail 'could not read hyprc
jq -e 'type == "array" and length > 0' >/dev/null <<<"$descriptions" \
|| fail 'hyprctl descriptions did not return a list'
# Pull every enum entry that carries a hypr option, as: key<TAB>option<TAB>values
# Pull every enum entry that carries a hypr option, as:
# key<TAB>option<TAB>kind<TAB>values (kind is "int" or "str")
entries="$(python3 - "$schema" <<'PY'
import re, sys
@@ -46,22 +59,45 @@ for block in re.findall(r'\{\s*\n?\s*key:\s*"([^"]+)"(.*?)\n \}', text, r
option = re.search(r'option:\s*"([^"]+)"', body)
if not option:
continue
values = re.findall(r'value:\s*(-?\d+)', body)
if not values:
continue
print(f"{name}\t{option.group(1)}\t{','.join(values)}")
numbers = re.findall(r'value:\s*(-?\d+)\s*[,}]', body)
strings = re.findall(r'value:\s*"([^"]*)"\s*[,}]', body)
if numbers:
print(f"{name}\t{option.group(1)}\tint\t{','.join(numbers)}")
elif strings:
print(f"{name}\t{option.group(1)}\tstr\t{','.join(strings)}")
PY
)"
[[ -n "$entries" ]] || fail 'found no compositor-backed enums in the schema -- this contract is not reading it correctly'
checked=0
while IFS=$'\t' read -r key option values; do
while IFS=$'\t' read -r key option kind values; do
[[ -n "$key" ]] || continue
entry="$(jq -c --arg name "$option" '.[] | select(.name == $name)' <<<"$descriptions")"
[[ -n "$entry" ]] || fail "$key maps to \"$option\", which the compositor does not publish"
# ── String-valued options: no map, an accepted-value list in the prose ────
if [[ "$kind" == "str" ]]; then
published="$(jq -r '.description // ""' <<<"$entry" \
| grep -oE '\[[a-z0-9_/-]+\]$' | tr -d '[]' | tr '/' '\n')"
if [[ -z "$published" ]]; then
printf 'enum hypr map contract: %s maps to "%s", which publishes no value list to check against\n' \
"$key" "$option" >&2
checked=$((checked + 1))
continue
fi
IFS=',' read -ra wanted <<<"$values"
for value in "${wanted[@]}"; do
# "" is the schema saying "leave the compositor's own default".
[[ -n "$value" ]] || continue
grep -qx "$value" <<<"$published" \
|| fail "$key offers \"$value\" for $option, which the compositor does not accept (it names: $(tr '\n' ' ' <<<"$published"))"
done
checked=$((checked + 1))
continue
fi
map_values="$(jq -r 'if .map then (.map | map(to_entries[].value) | join(",")) else "" end' <<<"$entry")"
IFS=',' read -ra wanted <<<"$values"