Make Applications a real app manager, and clean up storage without the racket

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Gabriel Brown
2026-08-24 17:18:14 -04:00
parent b30bf40407
commit 5a0643357f
29 changed files with 5024 additions and 314 deletions
+80
View File
@@ -313,4 +313,84 @@ rg --quiet 'image/png' "$call_log" \
rm -f "$config_home/mimeapps.list"
# ── One type on its own ──────────────────────────────────────────────────────
#
# The role rows set a whole family together, which is right almost always and
# wrong for the person whose .heic files should open somewhere other than the
# rest of their pictures. `set-type` is that escape hatch, and it is the one
# verb here that writes a type nobody curated -- so what it accepts is the
# question.
#
# Two guards, and they do different jobs: the type has to be one this system
# knows (a typo becomes a handler entry for a MIME type that will never exist),
# and the application has to be one that is installed (the same rule the role
# rows already keep).
assert_service_contains 'function searchTypes(query: string): void'
assert_service_contains 'function setType(mime: string, desktopId: string): void'
# The type database, as this fake machine has it.
mkdir -p "$data_home/mime"
cat >"$data_home/mime/globs2" <<'GLOBS'
# weight:type:glob
50:image/png:*.png
50:image/jpeg:*.jpg
50:image/heic:*.heic
50:text/markdown:*.md
50:application/pdf:*.pdf
GLOBS
# Two applications that declare what they open, so "candidates" has something
# true to report rather than passing on an empty list.
printf 'MimeType=image/png;image/jpeg;image/heic;\n' >>"$data_home/applications/org.gnome.Loupe.desktop"
printf 'MimeType=text/markdown;text/plain;\n' >>"$data_home/applications/panama-nvim.desktop"
: >"$call_log"
search="$($helper search-types heic)" || fail 'search-types failed'
jq -e '(.types | type == "array") and has("truncated")' <<<"$search" >/dev/null \
|| fail "search-types does not report a list of types and whether it was cut short: $search"
jq -e '[.types[] | has("mime") and has("handler") and has("candidates")] | all' <<<"$search" >/dev/null \
|| fail "a search result is missing its type, its current handler, or the applications that could open it: $search"
jq -e '[.types[].mime] | index("image/heic") != null' <<<"$search" >/dev/null \
|| fail "searching an extension did not find the type it belongs to: $search"
jq -e '[.types[] | select(.mime == "image/heic") | .candidates[].id]
| index("org.gnome.Loupe.desktop") != null' <<<"$search" >/dev/null \
|| fail "an application that declares the type is not offered for it: $search"
# Candidates are installed applications, never a name read out of a registry.
discovered="$(cd "$data_home/applications" && ls)"
while read -r candidate; do
[[ -n "$candidate" ]] || continue
rg -Fxq "$candidate" <<<"$discovered" \
|| fail "search-types offers '$candidate', which is not installed on this machine"
done < <(jq -r '[.types[].candidates[].id] | unique[]' <<<"$search")
# A one-character query is not a search: it would return the whole database.
jq -e '(.types | length) == 0' <<<"$($helper search-types a)" >/dev/null \
|| fail 'a single character was treated as a search of the whole type database'
: >"$call_log"
$helper set-type image/heic org.gnome.Loupe.desktop
assert_call $'default\norg.gnome.Loupe.desktop\nimage/heic'
# One type, and only that type: the whole point of the escape hatch is that it
# leaves the rest of the family where it was.
[[ "$(rg --count '^default$' "$call_log")" == "1" ]] \
|| fail 'setting one file type wrote more than one'
: >"$call_log"
if $helper set-type image/heic org.example.Missing.desktop >/dev/null 2>&1; then
fail 'set-type accepted an application that is not installed'
fi
if $helper set-type image/heic ../escape.desktop >/dev/null 2>&1; then
fail 'set-type accepted an unsafe desktop id'
fi
for bad_type in 'image' 'image/' '/png' 'image/png;rm -rf /' '' '../../etc/passwd' \
'image/does-not-exist'; do
if $helper set-type "$bad_type" org.gnome.Loupe.desktop >/dev/null 2>&1; then
fail "set-type accepted a type this system does not have: ${bad_type@Q}"
fi
done
[[ ! -s "$call_log" ]] || fail "a refused set-type still reached xdg-mime: $(cat "$call_log")"
printf 'default apps contract: PASS\n'