Four things a Hyprland desktop can do that this one was not. Searching from the launcher needed no launcher work at all: Vicinae already models it, so this is a script command with one percent-encoded argument. Make it the fallback command and anything typed that matches nothing else offers to search it. Bangs come free -- they are a property of where the query is sent, not of the launcher -- so !yt reaches YouTube without a line of bang parsing. Suggestions could not be a script command. They need a view that reacts as you type, which is an extension: TypeScript, compiled, querying the same endpoint Firefox's address bar uses. It debounces, and aborts the request in flight on every keystroke -- typing is faster than the network, and an older answer landing after a newer one leaves the list describing a query that is no longer on screen. A bang skips suggestions entirely, because Google has no useful guesses about "!yt". The engine is now written down twice, once in each. The contract pins that they agree, since searching from the fallback and searching from the suggestions reaching different places is the kind of wrong that looks fine. Gestures mirror GNOME: three fingers sideways for workspaces, up for the overview, down to dismiss it. Open and close rather than toggle both ways -- toggling means swiping up from an open overview closes it, which is not what the fingers meant. Hyprland reads gesture registrations at startup so they cannot be a setting, but distance and direction can be, and are. Window swallowing is off by default and a preference like every other misc setting here. A terminal that vanishes when you did not ask for it is confusing rather than broken, which is worse. Claude-Session: https://claude.ai/code/session_01Q84axqUE5inJhf5Jz9CFy1
91 lines
3.7 KiB
Bash
Executable File
91 lines
3.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Install Panama's searchable launcher actions as one narrow directory link.
|
|
# Keeping the runtime path stable lets Vicinae watch it while every command
|
|
# remains authored and reviewed in the Panama repository.
|
|
|
|
set -euo pipefail
|
|
|
|
panama_path="${PANAMA_PATH:-$HOME/.local/share/Panama}"
|
|
vicinae_data_dir="${VICINAE_DATA_DIR:-$HOME/.local/share/vicinae}"
|
|
source_dir="$panama_path/config/local/share/vicinae/scripts"
|
|
scripts_dir="$vicinae_data_dir/scripts"
|
|
target_dir="$scripts_dir/panama"
|
|
backup_root="$vicinae_data_dir/backups"
|
|
backup_dir="$backup_root/panama.pre-panama"
|
|
legacy_backup="$scripts_dir/panama.pre-panama"
|
|
|
|
[[ -d "$source_dir" ]] || {
|
|
printf 'Panama command source is missing: %s\n' "$source_dir" >&2
|
|
exit 1
|
|
}
|
|
|
|
mkdir -p "$scripts_dir" "$backup_root"
|
|
|
|
# Older Panama builds preserved this directory alongside searchable commands.
|
|
# Move it out before reloading so those scripts cannot appear twice.
|
|
if [[ -e "$legacy_backup" ]]; then
|
|
if [[ -e "$backup_dir" ]]; then
|
|
printf 'Refusing to move %s; backup already exists at %s\n' \
|
|
"$legacy_backup" "$backup_dir" >&2
|
|
exit 1
|
|
fi
|
|
mv "$legacy_backup" "$backup_dir"
|
|
fi
|
|
|
|
if [[ -L "$target_dir" ]]; then
|
|
rm "$target_dir"
|
|
elif [[ -e "$target_dir" ]]; then
|
|
if [[ -e "$backup_dir" ]]; then
|
|
printf 'Refusing to replace %s; backup already exists at %s\n' \
|
|
"$target_dir" "$backup_dir" >&2
|
|
exit 1
|
|
fi
|
|
mv "$target_dir" "$backup_dir"
|
|
fi
|
|
|
|
ln -s "$source_dir" "$target_dir"
|
|
|
|
# ── Extensions ───────────────────────────────────────────────────────────────
|
|
#
|
|
# Script commands are a file and a shebang, so they are linked. Extensions are
|
|
# not: they are TypeScript that has to be compiled, and `vici build` writes its
|
|
# output straight into Vicinae's data directory rather than leaving a bundle to
|
|
# link. So the source lives in this repository and the build is what installs
|
|
# it.
|
|
#
|
|
# Never fatal, and never a reason to fail a stage. A build wants npm and the
|
|
# network, and neither is guaranteed at this point in an install -- npm arrives
|
|
# with nvm earlier in install-packages, which can itself be skipped. An
|
|
# extension that did not build is a launcher missing one command, not a desktop
|
|
# that failed to install.
|
|
extensions_source="$panama_path/config/local/share/vicinae/extensions"
|
|
if [[ -d "$extensions_source" ]] && command -v npm >/dev/null 2>&1; then
|
|
for extension in "$extensions_source"/*/; do
|
|
[[ -f "$extension/package.json" ]] || continue
|
|
name="$(basename "$extension")"
|
|
|
|
# Skip a build that would produce what is already there. `npm install`
|
|
# alone takes long enough to be worth not repeating on every re-run of
|
|
# a stage that is otherwise nearly instant.
|
|
built="$vicinae_data_dir/extensions/$name"
|
|
if [[ -d "$built" && "$extension/src" -ot "$built" ]]; then
|
|
printf 'Vicinae extension %s is already built\n' "$name"
|
|
continue
|
|
fi
|
|
|
|
printf 'Building Vicinae extension %s\n' "$name"
|
|
if ! (cd "$extension" && npm install --silent >/dev/null 2>&1 && npm run build >/dev/null 2>&1); then
|
|
printf 'Vicinae extension %s did not build; skipping\n' "$name" >&2
|
|
fi
|
|
done
|
|
elif [[ -d "$extensions_source" ]]; then
|
|
printf 'npm is not available, so Vicinae extensions were not built\n' >&2
|
|
fi
|
|
|
|
# The server also rescans periodically, but an explicit reload makes a setup
|
|
# run deterministic. If Vicinae is not active yet, its startup scan is enough.
|
|
if command -v vicinae >/dev/null 2>&1 && vicinae ping >/dev/null 2>&1; then
|
|
vicinae cmd launch core:reload-scripts >/dev/null 2>&1 || true
|
|
fi
|