Restore the browser, gaming, development, and Windows VM menus
This commit is contained in:
@@ -20,7 +20,7 @@ and nothing to re-apply after somebody else's release.
|
|||||||
- **[Quickshell](https://quickshell.org/)** as the desktop: `blob-shell` is one
|
- **[Quickshell](https://quickshell.org/)** as the desktop: `blob-shell` is one
|
||||||
process hosting the bar, both menus, the panels, notifications, the lock
|
process hosting the bar, both menus, the panels, notifications, the lock
|
||||||
screen, and the OSD as plugins.
|
screen, and the OSD as plugins.
|
||||||
- **A `blob-*` CLI** of 251 commands, dispatched by `blob`.
|
- **A `blob-*` CLI** of 290 commands, dispatched by `blob`.
|
||||||
- **24 themes** with a palette pipeline that retints the terminal, editor,
|
- **24 themes** with a palette pipeline that retints the terminal, editor,
|
||||||
browser, shell, and lock screen from one `colors.toml`.
|
browser, shell, and lock screen from one `colors.toml`.
|
||||||
|
|
||||||
|
|||||||
Executable
+50
@@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Native messaging host: copy a Chromium tab URL to the clipboard
|
||||||
|
# blob:hidden=true
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_PATH="${BASH_SOURCE[0]}"
|
||||||
|
export BLOB_PATH="${BLOB_PATH:-$(cd -- "$(dirname -- "$SCRIPT_PATH")/.." && pwd)}"
|
||||||
|
export PATH="$BLOB_PATH/bin:/usr/local/bin:/usr/bin:$PATH"
|
||||||
|
|
||||||
|
parse_url() {
|
||||||
|
jq -r '.url // empty' 2>/dev/null <<<"$1" || true
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_url() {
|
||||||
|
local url="$1"
|
||||||
|
|
||||||
|
[[ -n $url ]] || return 1
|
||||||
|
printf '%s' "$url" | wl-copy --type text/plain
|
||||||
|
blob-notify-send -g "URL copied to clipboard"
|
||||||
|
}
|
||||||
|
|
||||||
|
reply_copied() {
|
||||||
|
if [[ $1 == "true" ]]; then
|
||||||
|
printf '\x0f\x00\x00\x00{"copied":true}'
|
||||||
|
else
|
||||||
|
printf '\x10\x00\x00\x00{"copied":false}'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
local length payload url
|
||||||
|
|
||||||
|
length=$(head -c4 | od -An -v -tu4 --endian=little | tr -d ' ')
|
||||||
|
[[ -n ${length:-} ]] && (( length > 0 )) || exit 0
|
||||||
|
|
||||||
|
payload=$(head -c "$length")
|
||||||
|
url=$(parse_url "$payload")
|
||||||
|
|
||||||
|
if copy_url "$url"; then
|
||||||
|
reply_copied true
|
||||||
|
else
|
||||||
|
reply_copied false
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ ${BASH_SOURCE[0]} == "$0" ]]; then
|
||||||
|
main "$@"
|
||||||
|
fi
|
||||||
Executable
+196
@@ -0,0 +1,196 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Native messaging host: download the URL sent by the yt-dlp Chromium extension
|
||||||
|
# blob:hidden=true
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_PATH="${BASH_SOURCE[0]}"
|
||||||
|
|
||||||
|
# The browser launches us without Blob's environment, so locate the repo from
|
||||||
|
# our own path when BLOB_PATH isn't already set. Export it — blob-shell (used
|
||||||
|
# by blob-osd) needs it to find the running shell, and silently no-ops without it.
|
||||||
|
export BLOB_PATH="${BLOB_PATH:-$(cd -- "$(dirname -- "$SCRIPT_PATH")/.." && pwd)}"
|
||||||
|
|
||||||
|
# Make sure the Blob bin and yt-dlp are reachable when launched by the browser.
|
||||||
|
export PATH="$BLOB_PATH/bin:/usr/local/bin:/usr/bin:$PATH"
|
||||||
|
|
||||||
|
DOWNLOAD_DIR="${BLOB_YTDLP_DIR:-$HOME/Videos}"
|
||||||
|
|
||||||
|
parse_url() {
|
||||||
|
jq -r '.url // empty' 2>/dev/null <<<"$1" || true
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_url() {
|
||||||
|
[[ $1 =~ ^https?:// ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# A printed path is only usable if it is a regular file inside DOWNLOAD_DIR.
|
||||||
|
# Forged records (leading-dash mpv options, paths with control chars, or
|
||||||
|
# anything that escaped the download directory) must not reach the click command.
|
||||||
|
resolve_download_file() {
|
||||||
|
local candidate=$1 file_real dir_real
|
||||||
|
|
||||||
|
[[ -n $candidate ]] || return 1
|
||||||
|
[[ $candidate != *$'\n'* && $candidate != *$'\r'* && $candidate != *$'\t'* ]] || return 1
|
||||||
|
[[ -f $candidate ]] || return 1
|
||||||
|
|
||||||
|
# Read to a NUL: command substitution strips trailing newlines, which would
|
||||||
|
# resolve a name ending in one to a different file that may well exist.
|
||||||
|
IFS= read -r -d '' file_real < <(realpath -ze -- "$candidate") || return 1
|
||||||
|
IFS= read -r -d '' dir_real < <(realpath -ze -- "$DOWNLOAD_DIR") || return 1
|
||||||
|
|
||||||
|
[[ $file_real != *$'\n'* && $file_real != *$'\r'* && $file_real != *$'\t'* ]] || return 1
|
||||||
|
# Trim the slash so a download directory of "/" still leaves a usable prefix.
|
||||||
|
[[ $file_real == "${dir_real%/}"/* ]] || return 1
|
||||||
|
|
||||||
|
printf '%s' "$file_real"
|
||||||
|
}
|
||||||
|
|
||||||
|
# yt-dlp prints the title JSON-encoded, so a newline or tab in page metadata is an
|
||||||
|
# escape sequence rather than a record boundary. This is toast text, never a command.
|
||||||
|
decode_title() {
|
||||||
|
local decoded
|
||||||
|
|
||||||
|
decoded=$(jq -r 'if type == "string" then . else empty end' <<<"$1" 2>/dev/null) || return 1
|
||||||
|
decoded=${decoded%%[[:cntrl:]]*} # keep what a person would read, drop the forgery
|
||||||
|
[[ -n $decoded && $decoded != -* ]] || return 1
|
||||||
|
|
||||||
|
printf '%s' "$decoded"
|
||||||
|
}
|
||||||
|
|
||||||
|
title_from_file() {
|
||||||
|
local name=${1##*/}
|
||||||
|
name=${name%.*}
|
||||||
|
name=${name//[$'\n\r\t']/}
|
||||||
|
if [[ -z $name || $name == -* ]]; then
|
||||||
|
printf '%s' "Video"
|
||||||
|
else
|
||||||
|
printf '%s' "$name"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Drive the Quickshell OSD — a single overlay that updates in place (like the
|
||||||
|
# volume/brightness bar), so download progress never stacks like notifications.
|
||||||
|
osd_progress() {
|
||||||
|
blob-osd -i -p "$1" -d 8000 >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
|
osd_close() {
|
||||||
|
blob-shell -q osd close >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
|
download_url() {
|
||||||
|
local url="$1"
|
||||||
|
|
||||||
|
mkdir -p "$DOWNLOAD_DIR"
|
||||||
|
|
||||||
|
# Don't show anything until yt-dlp confirms there's actually a video to grab.
|
||||||
|
if ! yt-dlp --no-playlist --simulate --quiet --no-warnings --no-exec --no-exec-before-download -- "$url" >/dev/null 2>&1; then
|
||||||
|
blob-notify-send -u critical -g "No video found for download" "$url"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
osd_progress 0
|
||||||
|
|
||||||
|
# Stream the download: BLOB_PROG carries the percent (drives the OSD), and
|
||||||
|
# BLOB_FILE and BLOB_TITLE (printed only after a successful move) carry the
|
||||||
|
# path and the title. The title is JSON-encoded so metadata cannot forge a record,
|
||||||
|
# and the file is named after it: yt-dlp strips control characters from a filename
|
||||||
|
# with or without --restrict-filenames, so a record is still only ever one line.
|
||||||
|
local line pct intpct last="" er nowms lastms=0 title="" filepath="" resolved
|
||||||
|
while IFS= read -r line; do
|
||||||
|
case $line in
|
||||||
|
BLOB_PROG*)
|
||||||
|
pct=${line#BLOB_PROG$'\t'}
|
||||||
|
intpct=${pct%%.*}
|
||||||
|
intpct=${intpct//[^0-9]/}
|
||||||
|
[[ -n $intpct && $intpct != "$last" ]] || continue # skip no-op repeats
|
||||||
|
# Throttle to ~4 redraws/sec so fast downloads don't spawn a flurry of processes.
|
||||||
|
er=$EPOCHREALTIME
|
||||||
|
nowms=$((${er%[.,]*} * 1000 + 10#${er##*[.,]} / 1000))
|
||||||
|
((nowms - lastms >= 250)) || continue
|
||||||
|
last=$intpct
|
||||||
|
lastms=$nowms
|
||||||
|
osd_progress "$intpct"
|
||||||
|
;;
|
||||||
|
BLOB_FILE*)
|
||||||
|
resolved=$(resolve_download_file "${line#BLOB_FILE$'\t'}") || continue
|
||||||
|
filepath=$resolved
|
||||||
|
;;
|
||||||
|
BLOB_TITLE*)
|
||||||
|
title=$(decode_title "${line#BLOB_TITLE$'\t'}") || title=""
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < <(PYTHONUNBUFFERED=1 yt-dlp --no-playlist --no-simulate \
|
||||||
|
--quiet --no-warnings --no-exec --no-exec-before-download --progress --newline \
|
||||||
|
--progress-template $'download:BLOB_PROG\t%(progress._percent_str)s' \
|
||||||
|
--paths "$DOWNLOAD_DIR" -o '%(title)s.%(ext)s' \
|
||||||
|
--print $'after_move:BLOB_FILE\t%(filepath)s' \
|
||||||
|
--print $'after_move:BLOB_TITLE\t%(title)j' \
|
||||||
|
-- "$url" 2>&1)
|
||||||
|
|
||||||
|
osd_close
|
||||||
|
|
||||||
|
# after_move only prints on a successful download+move, so a captured path == success.
|
||||||
|
if [[ -n $filepath ]]; then
|
||||||
|
[[ -n $title ]] || title=$(title_from_file "$filepath")
|
||||||
|
((${#title} > 50)) && title="${title:0:50}…" # keep the toast compact
|
||||||
|
|
||||||
|
# Square, center-cropped thumbnail so the notification preview isn't stretched.
|
||||||
|
local preview
|
||||||
|
preview="$(mktemp --suffix=.jpg)"
|
||||||
|
ffmpeg -y -i "$filepath" -ss 00:00:00.1 -vframes 1 \
|
||||||
|
-vf "crop='min(iw,ih)':'min(iw,ih)',scale=256:256" -q:v 2 \
|
||||||
|
"$preview" -loglevel quiet 2>/dev/null || true
|
||||||
|
|
||||||
|
# Best-effort: the download already succeeded, and under `set -e` a failed
|
||||||
|
# toast would exit before the thumbnail cleanup below is ever scheduled.
|
||||||
|
# `--` keeps mpv from parsing a leading-dash filename as an option; the path
|
||||||
|
# is one discrete argument, so it never reaches a shell.
|
||||||
|
blob-notify-send -g "Download complete" "$title" \
|
||||||
|
-t 10000 --image "${preview:-$filepath}" \
|
||||||
|
--exec mpv -- "$filepath" || true
|
||||||
|
|
||||||
|
# The shell loads the thumbnail into memory when the toast appears and never
|
||||||
|
# re-reads the file, so the preview only has to outlive that load, not the
|
||||||
|
# toast.
|
||||||
|
(
|
||||||
|
sleep 2
|
||||||
|
rm -f "$preview"
|
||||||
|
) &
|
||||||
|
else
|
||||||
|
blob-notify-send -u critical -g "Download failed" "$url"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
local length payload url
|
||||||
|
|
||||||
|
# Detached worker: this is what actually runs yt-dlp and fires notifications.
|
||||||
|
if [[ "${1:-}" == "--download" ]]; then
|
||||||
|
download_url "$2"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Native messaging frame: 4-byte little-endian length prefix, then UTF-8 JSON.
|
||||||
|
length=$(head -c4 | od -An -v -tu4 --endian=little | tr -d ' ')
|
||||||
|
[[ -n ${length:-} ]] && ((length > 0)) || exit 0
|
||||||
|
|
||||||
|
payload=$(head -c "$length")
|
||||||
|
|
||||||
|
# Ack with an empty message so the extension's sendNativeMessage callback resolves cleanly.
|
||||||
|
printf '\x02\x00\x00\x00{}'
|
||||||
|
|
||||||
|
url=$(parse_url "$payload")
|
||||||
|
[[ -n $url ]] || exit 0
|
||||||
|
valid_url "$url" || exit 0
|
||||||
|
|
||||||
|
# Detach the download so this host exits promptly and frees the browser's port.
|
||||||
|
setsid -f "$SCRIPT_PATH" --download "$url" </dev/null >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ ${BASH_SOURCE[0]} == "$0" ]]; then
|
||||||
|
main "$@"
|
||||||
|
fi
|
||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=List installed RetroArch core names
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
core_dir="/usr/lib/libretro"
|
||||||
|
preferred_cores=(
|
||||||
|
"Amstrad CPC|cap32"
|
||||||
|
"Arcade FBNeo|fbneo"
|
||||||
|
"Arcade MAME|mame"
|
||||||
|
"Commodore Amiga|puae"
|
||||||
|
"Commodore C128|vice_x128"
|
||||||
|
"Commodore C64|vice_x64"
|
||||||
|
"Commodore VIC-20|vice_xvic"
|
||||||
|
"Nintendo DS|desmume"
|
||||||
|
"Nintendo Game Boy / Color|gambatte"
|
||||||
|
"Nintendo Game Boy Advance|mgba"
|
||||||
|
"Nintendo GameCube / Wii|dolphin"
|
||||||
|
"Nintendo NES / Famicom|mesen"
|
||||||
|
"Nintendo 64|parallel_n64"
|
||||||
|
"Nintendo SNES / SFC|snes9x"
|
||||||
|
"NEC PC Engine / TurboGrafx-16|mednafen_pce_fast"
|
||||||
|
"NEC PC Engine CD / TurboGrafx-CD|mednafen_pce"
|
||||||
|
"NEC PC Engine SuperGrafx|mednafen_supergrafx"
|
||||||
|
"Sega Dreamcast|flycast"
|
||||||
|
"Sega Mega Drive / Master System / Game Gear|genesis_plus_gx"
|
||||||
|
"Sega Saturn|kronos"
|
||||||
|
"Sony PlayStation|mednafen_psx_hw"
|
||||||
|
"Sony PlayStation Portable|ppsspp"
|
||||||
|
)
|
||||||
|
|
||||||
|
[[ -d $core_dir ]] || exit 0
|
||||||
|
|
||||||
|
for preferred_core in "${preferred_cores[@]}"; do
|
||||||
|
label="${preferred_core%%|*}"
|
||||||
|
core="${preferred_core#*|}"
|
||||||
|
[[ -f $core_dir/${core}_libretro.so ]] && printf '%s (%s)\n' "$label" "$core"
|
||||||
|
done
|
||||||
Executable
+72
@@ -0,0 +1,72 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Create a desktop launcher for a RetroArch game
|
||||||
|
# blob:args=[core path-to-game]
|
||||||
|
# blob:examples=blob games retro install snes9x ~/Games/roms/snes/game.sfc | blob-games-retro-install /usr/lib/libretro/mgba_libretro.so ~/Games/roms/gba/game.gba
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if (( $# == 0 )); then
|
||||||
|
mapfile -t cores < <(blob-games-retro-cores)
|
||||||
|
|
||||||
|
if (( ${#cores[@]} == 0 )); then
|
||||||
|
blob-notify-send -g "No RetroArch cores found" "/usr/lib/libretro"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
core=$(blob-menu-select "RetroArch core" "${cores[@]}") || exit 0
|
||||||
|
[[ -n $core ]] || exit 0
|
||||||
|
core="${core##*(}"
|
||||||
|
core="${core%)}"
|
||||||
|
|
||||||
|
game_path=$(blob-menu-file "Retro game" "$HOME/Games/roms" "7z bin ccd chd cue dmg elf fds gb gba gbc iso lha m3u md n64 nds nes pbp sfc smc swc zip z64") || exit 0
|
||||||
|
[[ -n $game_path ]] || exit 0
|
||||||
|
elif (( $# == 2 )); then
|
||||||
|
core="$1"
|
||||||
|
game_path="$2"
|
||||||
|
else
|
||||||
|
echo "Usage: blob-games-retro-install [core path-to-game]"
|
||||||
|
echo "Example: blob-games-retro-install snes9x ~/Games/roms/snes/game.sfc"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f $game_path ]]; then
|
||||||
|
echo "Game not found: $game_path"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $core == */* ]]; then
|
||||||
|
core_path="$core"
|
||||||
|
else
|
||||||
|
core_path="/usr/lib/libretro/${core}_libretro.so"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f $core_path ]]; then
|
||||||
|
echo "Core not found: $core_path"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
game_name=$(printf '%s' "${game_path##*/}" | sed 's/\.[^.]*$//; s/[[:space:]]*([^)]*)//g; s/[[:space:]]\+/ /g; s/^ //; s/ $//' | perl -Mopen=locale -pe 's/(^|[[:space:]])([^[:space:]])/$1\U$2/g')
|
||||||
|
desktop_name="$game_name"
|
||||||
|
desktop_id=$(printf '%s' "$desktop_name" | tr '[:upper:]' '[:lower:]' | tr -cs '[:alnum:]' '-' | sed 's/^-//; s/-$//')
|
||||||
|
desktop_dir="$HOME/.local/share/applications"
|
||||||
|
desktop_file="$desktop_dir/$desktop_id.desktop"
|
||||||
|
mkdir -p "$desktop_dir"
|
||||||
|
|
||||||
|
cat >"$desktop_file" <<EOF
|
||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Name=$desktop_name
|
||||||
|
Comment=Play $game_name with RetroArch
|
||||||
|
Exec=retroarch -L "$core_path" "$game_path"
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Icon=retro-gaming
|
||||||
|
StartupNotify=true
|
||||||
|
Categories=Game;Emulator;
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x "$desktop_file"
|
||||||
|
update-desktop-database "$desktop_dir" &>/dev/null || true
|
||||||
|
|
||||||
|
blob-notify-send -g "$game_name installed" "Start it with Super + Space"
|
||||||
Executable
+87
@@ -0,0 +1,87 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Install a supported browser
|
||||||
|
# blob:args=<chrome|brave|brave-origin|edge|firefox|zen>
|
||||||
|
# blob:examples=blob install browser firefox | blob install browser brave
|
||||||
|
|
||||||
|
source "$BLOB_PATH/install/helpers/browser-policy.sh"
|
||||||
|
|
||||||
|
setup_chromium_policy_directory() {
|
||||||
|
browser_policy_setup_dir "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
announce_browser_installed() {
|
||||||
|
echo ""
|
||||||
|
echo "$1 browser installed. Make it the default via Setup > Defaults > Browser."
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_chromium_flags() {
|
||||||
|
mkdir -p ~/.config
|
||||||
|
cp -f "$BLOB_PATH/config/chromium-flags.conf" "$1"
|
||||||
|
blob-install-chromium-copy-url
|
||||||
|
blob-install-chromium-ytdlp
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_firefox_wayland() {
|
||||||
|
mkdir -p ~/.config/environment.d
|
||||||
|
echo "MOZ_ENABLE_WAYLAND=1" > ~/.config/environment.d/blob-firefox-wayland.conf
|
||||||
|
}
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
chrome)
|
||||||
|
echo "Installing Chrome..."
|
||||||
|
blob-pkg-aur-add google-chrome || exit 1
|
||||||
|
|
||||||
|
setup_chromium_policy_directory /etc/opt/chrome/policies/managed
|
||||||
|
copy_chromium_flags ~/.config/chrome-flags.conf
|
||||||
|
blob-theme-browser
|
||||||
|
announce_browser_installed "Chrome"
|
||||||
|
;;
|
||||||
|
edge)
|
||||||
|
echo "Installing Edge..."
|
||||||
|
blob-pkg-aur-add microsoft-edge-stable-bin || exit 1
|
||||||
|
|
||||||
|
setup_chromium_policy_directory /etc/opt/edge/policies/managed
|
||||||
|
copy_chromium_flags ~/.config/microsoft-edge-stable-flags.conf
|
||||||
|
blob-theme-browser
|
||||||
|
announce_browser_installed "Edge"
|
||||||
|
;;
|
||||||
|
brave)
|
||||||
|
echo "Installing Brave..."
|
||||||
|
blob-pkg-aur-add brave-bin || exit 1
|
||||||
|
|
||||||
|
setup_chromium_policy_directory /etc/brave/policies/managed
|
||||||
|
copy_chromium_flags ~/.config/brave-flags.conf
|
||||||
|
blob-theme-browser
|
||||||
|
announce_browser_installed "Brave"
|
||||||
|
;;
|
||||||
|
brave-origin)
|
||||||
|
echo "Installing Brave Origin..."
|
||||||
|
blob-pkg-aur-add brave-origin-bin || exit 1
|
||||||
|
|
||||||
|
setup_chromium_policy_directory /etc/brave/policies/managed
|
||||||
|
copy_chromium_flags ~/.config/brave-origin-flags.conf
|
||||||
|
blob-theme-browser
|
||||||
|
announce_browser_installed "Brave Origin"
|
||||||
|
;;
|
||||||
|
firefox)
|
||||||
|
echo "Installing Firefox..."
|
||||||
|
blob-pkg-add firefox || exit 1
|
||||||
|
|
||||||
|
browser_policy_setup_firefox_distribution /usr/lib/firefox/distribution
|
||||||
|
setup_firefox_wayland
|
||||||
|
announce_browser_installed "Firefox"
|
||||||
|
;;
|
||||||
|
zen)
|
||||||
|
echo "Installing Zen..."
|
||||||
|
blob-pkg-aur-add zen-browser-bin || exit 1
|
||||||
|
|
||||||
|
browser_policy_setup_firefox_distribution /opt/zen-browser/distribution
|
||||||
|
setup_firefox_wayland
|
||||||
|
announce_browser_installed "Zen"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: blob-install-browser <chrome|brave|brave-origin|edge|firefox|zen>"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Executable
+28
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Install the native messaging host for the Copy URL Chromium extension
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HOST_NAME="com.blob.copy_url"
|
||||||
|
HOST_PATH="$BLOB_PATH/bin/blob-chromium-copy-url-host"
|
||||||
|
TEMPLATE="$BLOB_PATH/default/chromium/native-messaging-hosts/$HOST_NAME.json"
|
||||||
|
|
||||||
|
browser_dirs=(
|
||||||
|
"$HOME/.config/chromium"
|
||||||
|
"$HOME/.config/google-chrome"
|
||||||
|
"$HOME/.config/google-chrome-beta"
|
||||||
|
"$HOME/.config/google-chrome-unstable"
|
||||||
|
"$HOME/.config/BraveSoftware/Brave-Browser"
|
||||||
|
"$HOME/.config/BraveSoftware/Brave-Browser-Beta"
|
||||||
|
"$HOME/.config/BraveSoftware/Brave-Browser-Nightly"
|
||||||
|
"$HOME/.config/microsoft-edge"
|
||||||
|
"$HOME/.config/microsoft-edge-dev"
|
||||||
|
)
|
||||||
|
|
||||||
|
manifest=$(sed "s|__HOST_PATH__|$HOST_PATH|g" "$TEMPLATE")
|
||||||
|
|
||||||
|
for dir in "${browser_dirs[@]}"; do
|
||||||
|
mkdir -p "$dir/NativeMessagingHosts"
|
||||||
|
printf '%s\n' "$manifest" >"$dir/NativeMessagingHosts/$HOST_NAME.json"
|
||||||
|
done
|
||||||
Executable
+29
@@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Install the native messaging host for the yt-dlp Chromium extension
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HOST_NAME="com.blob.ytdlp"
|
||||||
|
HOST_PATH="$BLOB_PATH/bin/blob-chromium-ytdlp-host"
|
||||||
|
TEMPLATE="$BLOB_PATH/default/chromium/native-messaging-hosts/$HOST_NAME.json"
|
||||||
|
|
||||||
|
# Chromium-based browser profile roots that use the NativeMessagingHosts layout.
|
||||||
|
browser_dirs=(
|
||||||
|
"$HOME/.config/chromium"
|
||||||
|
"$HOME/.config/google-chrome"
|
||||||
|
"$HOME/.config/google-chrome-beta"
|
||||||
|
"$HOME/.config/google-chrome-unstable"
|
||||||
|
"$HOME/.config/BraveSoftware/Brave-Browser"
|
||||||
|
"$HOME/.config/BraveSoftware/Brave-Browser-Beta"
|
||||||
|
"$HOME/.config/BraveSoftware/Brave-Browser-Nightly"
|
||||||
|
"$HOME/.config/microsoft-edge"
|
||||||
|
"$HOME/.config/microsoft-edge-dev"
|
||||||
|
)
|
||||||
|
|
||||||
|
manifest=$(sed "s|__HOST_PATH__|$HOST_PATH|g" "$TEMPLATE")
|
||||||
|
|
||||||
|
for dir in "${browser_dirs[@]}"; do
|
||||||
|
mkdir -p "$dir/NativeMessagingHosts"
|
||||||
|
printf '%s\n' "$manifest" >"$dir/NativeMessagingHosts/$HOST_NAME.json"
|
||||||
|
done
|
||||||
Executable
+155
@@ -0,0 +1,155 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Install a supported development environment
|
||||||
|
# blob:name=dev-env
|
||||||
|
# blob:args=<ruby|node|bun|deno|go|laravel|symfony|php|python|elixir|phoenix|rust|java|zig|ocaml|dotnet|clojure|scala>
|
||||||
|
# blob:examples=blob install dev-env ruby | blob install dev-env node
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
if [[ -z $1 ]]; then
|
||||||
|
echo "Usage: blob-install-dev-env <ruby|node|bun|deno|go|laravel|symfony|php|python|elixir|phoenix|rust|java|zig|ocaml|dotnet|clojure|scala>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
install_php() {
|
||||||
|
blob-pkg-add php composer php-sqlite xdebug
|
||||||
|
|
||||||
|
# Install Path for Composer
|
||||||
|
if [[ :$PATH: != *:$HOME/.config/composer/vendor/bin:* ]]; then
|
||||||
|
echo 'export PATH="$HOME/.config/composer/vendor/bin:$PATH"' >>"$HOME/.bashrc"
|
||||||
|
source "$HOME/.bashrc"
|
||||||
|
echo "Added Composer global bin directory to PATH."
|
||||||
|
else
|
||||||
|
echo "Composer global bin directory already in PATH."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enable some extensions
|
||||||
|
local php_ini_path="/etc/php/php.ini"
|
||||||
|
local extensions_to_enable=(
|
||||||
|
"bcmath"
|
||||||
|
"intl"
|
||||||
|
"iconv"
|
||||||
|
"openssl"
|
||||||
|
"pdo_sqlite"
|
||||||
|
"pdo_mysql"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Enable Xdebug
|
||||||
|
sudo sed -i \
|
||||||
|
-e 's/^;zend_extension=xdebug.so/zend_extension=xdebug.so/' \
|
||||||
|
-e 's/^;xdebug.mode=debug/xdebug.mode=debug/' \
|
||||||
|
/etc/php/conf.d/xdebug.ini
|
||||||
|
|
||||||
|
for ext in "${extensions_to_enable[@]}"; do
|
||||||
|
sudo sed -i "s/^;extension=${ext}/extension=${ext}/" "$php_ini_path"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
install_node() {
|
||||||
|
echo -e "Installing Node.js...\n"
|
||||||
|
mise use --global node
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
ruby)
|
||||||
|
echo -e "Installing Ruby on Rails...\n"
|
||||||
|
blob-pkg-add libyaml
|
||||||
|
mise settings add ruby.compile false
|
||||||
|
mise settings add idiomatic_version_file_enable_tools ruby
|
||||||
|
mise use --global ruby@latest
|
||||||
|
echo "gem: --no-document" >~/.gemrc
|
||||||
|
mise x ruby -- gem install rails --no-document
|
||||||
|
echo -e "\nYou can now run: rails new myproject"
|
||||||
|
;;
|
||||||
|
node)
|
||||||
|
install_node
|
||||||
|
;;
|
||||||
|
bun)
|
||||||
|
echo -e "Installing Bun...\n"
|
||||||
|
mise use -g bun@latest
|
||||||
|
;;
|
||||||
|
deno)
|
||||||
|
echo -e "Installing Deno...\n"
|
||||||
|
mise use -g deno@latest
|
||||||
|
;;
|
||||||
|
go)
|
||||||
|
echo -e "Installing Go...\n"
|
||||||
|
mise use --global go@latest
|
||||||
|
;;
|
||||||
|
php)
|
||||||
|
echo -e "Installing PHP...\n"
|
||||||
|
install_php
|
||||||
|
;;
|
||||||
|
laravel)
|
||||||
|
echo -e "Installing PHP and Laravel...\n"
|
||||||
|
install_php
|
||||||
|
install_node
|
||||||
|
composer global require laravel/installer
|
||||||
|
echo -e "\nYou can now run: laravel new myproject"
|
||||||
|
;;
|
||||||
|
symfony)
|
||||||
|
echo -e "Installing PHP and Symfony...\n"
|
||||||
|
install_php
|
||||||
|
blob-pkg-add symfony-cli
|
||||||
|
echo -e "\nYou can now run: symfony new --webapp myproject"
|
||||||
|
;;
|
||||||
|
python)
|
||||||
|
echo -e "Installing Python...\n"
|
||||||
|
mise use --global python@latest
|
||||||
|
echo -e "\nInstalling uv...\n"
|
||||||
|
curl -fsSL https://astral.sh/uv/install.sh | sh
|
||||||
|
;;
|
||||||
|
elixir)
|
||||||
|
echo -e "Installing Elixir...\n"
|
||||||
|
mise use --global erlang@latest
|
||||||
|
mise use --global elixir@latest
|
||||||
|
mise x elixir -- mix local.hex --force
|
||||||
|
;;
|
||||||
|
phoenix)
|
||||||
|
echo -e "Installing Phoenix Framework...\n"
|
||||||
|
# Ensure Erlang/Elixir first
|
||||||
|
mise use --global erlang@latest
|
||||||
|
mise use --global elixir@latest
|
||||||
|
# Hex & Rebar
|
||||||
|
mise x elixir -- mix local.hex --force
|
||||||
|
mise x elixir -- mix local.rebar --force
|
||||||
|
# Phoenix project (phx_new)
|
||||||
|
mise x elixir -- mix archive.install hex phx_new --force
|
||||||
|
echo -e "\nYou can now run: mix phx.new my_app"
|
||||||
|
;;
|
||||||
|
rust)
|
||||||
|
echo -e "Installing Rust...\n"
|
||||||
|
bash -c "$(curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs)" -- -y
|
||||||
|
;;
|
||||||
|
java)
|
||||||
|
echo -e "Installing Java...\n"
|
||||||
|
mise use --global java@latest
|
||||||
|
;;
|
||||||
|
zig)
|
||||||
|
echo -e "Installing Zig...\n"
|
||||||
|
mise use --global zig@latest
|
||||||
|
mise use -g zls@latest
|
||||||
|
;;
|
||||||
|
ocaml)
|
||||||
|
echo -e "Installing OCaml...\n"
|
||||||
|
bash -c "$(curl -fsSL https://raw.githubusercontent.com/ocaml/opam/master/shell/install.sh)"
|
||||||
|
opam init --yes
|
||||||
|
eval "$(opam env)"
|
||||||
|
opam install ocaml-lsp-server odoc ocamlformat utop --yes
|
||||||
|
;;
|
||||||
|
dotnet)
|
||||||
|
echo -e "Installing .NET...\n"
|
||||||
|
mise use --global dotnet@latest
|
||||||
|
;;
|
||||||
|
clojure)
|
||||||
|
echo -e "Installing Clojure...\n"
|
||||||
|
blob-pkg-add rlwrap
|
||||||
|
mise use --global clojure@latest
|
||||||
|
;;
|
||||||
|
scala)
|
||||||
|
echo -e "Installing Scala...\n"
|
||||||
|
mise use --global java@latest
|
||||||
|
mise use --global scala@latest
|
||||||
|
mise use --global scala-cli@latest
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Executable
+28
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Install one of the supported databases in a Docker container with the suitable development options.
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
options=("MySQL" "PostgreSQL" "Redis" "MongoDB" "MariaDB" "MSSQL")
|
||||||
|
|
||||||
|
if (( $# == 0 )); then
|
||||||
|
choices=$(printf "%s\n" "${options[@]}" | gum choose --header "Select database (return to install, esc to cancel)") || main_menu
|
||||||
|
else
|
||||||
|
choices="$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $choices ]]; then
|
||||||
|
for db in $choices; do
|
||||||
|
echo "Installing $db..."
|
||||||
|
case $db in
|
||||||
|
MySQL) sudo docker run -d --restart unless-stopped -p "127.0.0.1:3306:3306" --name=mysql8 -e MYSQL_ROOT_PASSWORD= -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql:8.4 ;;
|
||||||
|
PostgreSQL) sudo docker run -d --restart unless-stopped -p "127.0.0.1:5432:5432" --name=postgres18 -e POSTGRES_HOST_AUTH_METHOD=trust postgres:18 ;;
|
||||||
|
MariaDB) sudo docker run -d --restart unless-stopped -p "127.0.0.1:3306:3306" --name=mariadb11 -e MARIADB_ROOT_PASSWORD= -e MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=true mariadb:11.8 ;;
|
||||||
|
Redis) sudo docker run -d --restart unless-stopped -p "127.0.0.1:6379:6379" --name=redis redis:7 ;;
|
||||||
|
MongoDB) sudo docker run -d --restart unless-stopped -p "127.0.0.1:27017:27017" --name mongodb -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=admin123 mongo:noble ;;
|
||||||
|
MSSQL) sudo docker run -d --restart unless-stopped -p "127.0.0.1:1433:1433" --name mssql -e MSSQL_PID=Developer -e ACCEPT_EULA=Y -e "MSSQL_SA_PASSWORD=@dmin123" mcr.microsoft.com/mssql/server:2022-CU12-ubuntu-22.04 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "No databases selected for installation."
|
||||||
|
fi
|
||||||
Executable
+88
@@ -0,0 +1,88 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Install Battle.net standalone via umu-launcher + GE-Proton (no Steam, no Lutris, no Heroic).
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
PREFIX="$HOME/Games/battlenet"
|
||||||
|
LAUNCHER="$PREFIX/drive_c/Program Files (x86)/Battle.net/Battle.net Launcher.exe"
|
||||||
|
INSTALLER_URL="https://downloader.battle.net/download/getInstallerForGame?os=win&gameProgram=BATTLENET_APP&version=Live"
|
||||||
|
|
||||||
|
echo "Installing Battle.net..."
|
||||||
|
|
||||||
|
blob-pkg-add umu-launcher
|
||||||
|
blob-install-gaming-gpu-lib32
|
||||||
|
|
||||||
|
# Detect a half-finished prefix from a closed/crashed previous run and offer
|
||||||
|
# to wipe it before trying again. Battle.net's installer isn't idempotent.
|
||||||
|
if [[ -d $PREFIX && ! -f $LAUNCHER ]]; then
|
||||||
|
echo
|
||||||
|
echo "Found a partial Battle.net install at $PREFIX (no Launcher.exe)."
|
||||||
|
echo "Battle.net's installer can't resume from this state."
|
||||||
|
if gum confirm "Wipe the partial prefix and start fresh?"; then
|
||||||
|
pkill -f "$PREFIX" 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
rm -rf "$PREFIX"
|
||||||
|
else
|
||||||
|
echo "Aborting. Re-run when ready to wipe."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$PREFIX"
|
||||||
|
|
||||||
|
export WINEPREFIX="$PREFIX"
|
||||||
|
export PROTONPATH=GE-Proton
|
||||||
|
export GAMEID=umu-battlenet
|
||||||
|
export PROTON_VERB=run
|
||||||
|
|
||||||
|
if [[ -f $LAUNCHER ]]; then
|
||||||
|
echo "Battle.net is already installed at $PREFIX."
|
||||||
|
launched_installer=0
|
||||||
|
else
|
||||||
|
cache_dir="$HOME/.cache/blob"
|
||||||
|
mkdir -p "$cache_dir"
|
||||||
|
installer="$cache_dir/Battle.net-Setup.exe"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Downloading Battle.net installer..."
|
||||||
|
curl --fail --location --retry 3 "$INSTALLER_URL" --output "$installer"
|
||||||
|
|
||||||
|
cat <<'EOF'
|
||||||
|
|
||||||
|
Launching the Battle.net setup wizard. Click through it normally — the
|
||||||
|
default install path is fine. When it finishes, Battle.net will be in your
|
||||||
|
app launcher.
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
log="/tmp/blob-battlenet-installer.log"
|
||||||
|
setsid -f sh -c "umu-run '$installer' >'$log' 2>&1" </dev/null >/dev/null 2>&1
|
||||||
|
echo "Installer log: $log"
|
||||||
|
launched_installer=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$HOME/.local/share/applications"
|
||||||
|
install -m 644 "$BLOB_PATH/default/applications/battlenet.desktop" \
|
||||||
|
"$HOME/.local/share/applications/battlenet.desktop"
|
||||||
|
update-desktop-database "$HOME/.local/share/applications" 2>/dev/null || true
|
||||||
|
|
||||||
|
if (( launched_installer )); then
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
|
The Battle.net installer is running in the background. After it finishes,
|
||||||
|
find Battle.net in your app launcher, or run:
|
||||||
|
|
||||||
|
blob-launch-battlenet
|
||||||
|
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
|
Battle.net is installed. Find it in your app launcher, or run:
|
||||||
|
|
||||||
|
blob-launch-battlenet
|
||||||
|
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
Executable
+20
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Install and launch Geforce Now.
|
||||||
|
# blob:group=install
|
||||||
|
# blob:name=gaming geforce-now
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Installing GeForce NOW..."
|
||||||
|
blob-pkg-add flatpak
|
||||||
|
cd /tmp
|
||||||
|
|
||||||
|
# Download and run GeForce NOW
|
||||||
|
curl -LO https://international.download.nvidia.com/GFNLinux/GeForceNOWSetup.bin
|
||||||
|
chmod +x GeForceNOWSetup.bin
|
||||||
|
./GeForceNOWSetup.bin
|
||||||
|
|
||||||
|
# Ensure a separate browser process not started by GFN is available.
|
||||||
|
# If not, it seems like GFN has a tendency to hang on login.
|
||||||
|
setsid blob-launch-browser
|
||||||
Executable
+30
@@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Install lib32 graphics drivers (Vulkan + NVIDIA) for any detected GPUs.
|
||||||
|
# blob:group=install
|
||||||
|
# blob:name=gaming gpu-lib32
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Installing lib32 graphics drivers..."
|
||||||
|
|
||||||
|
PACKAGES=()
|
||||||
|
|
||||||
|
declare -A VULKAN_DRIVERS=(
|
||||||
|
[Intel]=lib32-vulkan-intel
|
||||||
|
[AMD]=lib32-vulkan-radeon
|
||||||
|
)
|
||||||
|
for vendor in "${!VULKAN_DRIVERS[@]}"; do
|
||||||
|
if lspci | grep -iE "(VGA|Display).*$vendor" >/dev/null; then
|
||||||
|
PACKAGES+=("${VULKAN_DRIVERS[$vendor]}")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if blob-hw-nvidia-gsp; then
|
||||||
|
PACKAGES+=(lib32-nvidia-utils)
|
||||||
|
elif blob-hw-nvidia-without-gsp; then
|
||||||
|
PACKAGES+=(lib32-nvidia-580xx-utils)
|
||||||
|
fi
|
||||||
|
|
||||||
|
(( ${#PACKAGES[@]} > 0 )) && blob-pkg-add "${PACKAGES[@]}"
|
||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Install Heroic Games Launcher (Epic, GOG, Amazon Prime Gaming) with graphics drivers.
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Installing Heroic Games Launcher..."
|
||||||
|
blob-pkg-add heroic-games-launcher-bin
|
||||||
|
blob-install-gaming-gpu-lib32
|
||||||
|
|
||||||
|
setsid uwsm-app -- gtk-launch heroic >/dev/null 2>&1 &
|
||||||
Executable
+23
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Install Lutris with Wine + DXVK for running Windows games (Battle.net, EA, Ubisoft Connect, etc.)
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Installing Lutris..."
|
||||||
|
blob-pkg-add lutris umu-launcher wine-staging wine-mono wine-gecko winetricks python-protobuf
|
||||||
|
blob-install-gaming-gpu-lib32
|
||||||
|
|
||||||
|
# Lutris ships with `#!/usr/bin/env python3`, which resolves to mise's Python and
|
||||||
|
# fails to import the lutris module. Pin the shebang to the system Python.
|
||||||
|
sudo sed -i '/env python3/ c\#!/bin/python3' /usr/bin/lutris
|
||||||
|
|
||||||
|
cat <<'EOF'
|
||||||
|
|
||||||
|
Lutris will open and auto-fetch its DXVK and VKD3D runtimes in the background
|
||||||
|
(watch the bottom status bar). Once that finishes, click the + to add or install games.
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
setsid lutris >/dev/null 2>&1 &
|
||||||
Executable
+85
@@ -0,0 +1,85 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Install RetroArch with the full libretro core set plus FBNeo and a ~/Games ROM directory.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Installing RetroArch..."
|
||||||
|
blob-pkg-add \
|
||||||
|
retroarch \
|
||||||
|
retroarch-assets-glui retroarch-assets-ozone retroarch-assets-xmb \
|
||||||
|
libretro-beetle-pce libretro-beetle-pce-fast libretro-beetle-psx libretro-beetle-psx-hw libretro-beetle-supergrafx \
|
||||||
|
libretro-blastem \
|
||||||
|
libretro-bsnes libretro-bsnes-hd \
|
||||||
|
libretro-core-info \
|
||||||
|
libretro-desmume libretro-dolphin libretro-flycast \
|
||||||
|
libretro-gambatte libretro-genesis-plus-gx \
|
||||||
|
libretro-kronos \
|
||||||
|
libretro-mame libretro-melonds libretro-mesen libretro-mesen-s libretro-mgba libretro-mupen64plus-next \
|
||||||
|
libretro-nestopia \
|
||||||
|
libretro-overlays \
|
||||||
|
libretro-parallel-n64 libretro-picodrive libretro-play libretro-ppsspp \
|
||||||
|
libretro-sameboy libretro-scummvm libretro-shaders-slang libretro-snes9x \
|
||||||
|
libretro-yabause \
|
||||||
|
libretro-cap32-git libretro-fbneo-git libretro-uae-git \
|
||||||
|
libretro-vice-x128-git libretro-vice-x64-git libretro-vice-x64dtv-git libretro-vice-x64sc-git \
|
||||||
|
libretro-vice-xcbm2-git libretro-vice-xcbm5x0-git libretro-vice-xpet-git \
|
||||||
|
libretro-vice-xplus4-git libretro-vice-xscpu64-git libretro-vice-xvic-git \
|
||||||
|
libretro-database-git \
|
||||||
|
retroarch-joypad-autoconfig-git
|
||||||
|
|
||||||
|
# Set up ~/Games for BIOS files and ROMs
|
||||||
|
mkdir -p "$HOME/Games/bios" "$HOME/Games/roms"
|
||||||
|
|
||||||
|
CFG="$HOME/.config/retroarch/retroarch.cfg"
|
||||||
|
mkdir -p "$(dirname "$CFG")"
|
||||||
|
touch "$CFG"
|
||||||
|
|
||||||
|
set_cfg() {
|
||||||
|
local key=$1 value=$2
|
||||||
|
if grep -q "^$key = " "$CFG"; then
|
||||||
|
sed -i "s|^$key = .*|$key = \"$value\"|" "$CFG"
|
||||||
|
else
|
||||||
|
echo "$key = \"$value\"" >>"$CFG"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
set_cfg rgui_browser_directory "$HOME/Games/roms"
|
||||||
|
set_cfg system_directory "$HOME/Games/bios"
|
||||||
|
|
||||||
|
# Point at the cores and assets installed by pacman
|
||||||
|
set_cfg libretro_directory "/usr/lib/libretro"
|
||||||
|
set_cfg libretro_info_path "/usr/share/libretro/info"
|
||||||
|
set_cfg overlay_directory "/usr/share/libretro/overlays"
|
||||||
|
set_cfg osk_overlay_directory "/usr/share/libretro/overlays/keyboards"
|
||||||
|
set_cfg video_shader_dir "/usr/share/libretro/shaders/shaders_slang"
|
||||||
|
set_cfg joypad_autoconfig_dir "/usr/share/libretro/autoconfig"
|
||||||
|
|
||||||
|
# Point at the database, cheats, and cursors from libretro-database-git
|
||||||
|
set_cfg content_database_path "/usr/share/libretro/database/rdb"
|
||||||
|
set_cfg cheat_database_path "/usr/share/libretro/database/cht"
|
||||||
|
set_cfg cursor_directory "/usr/share/libretro/database/cursors"
|
||||||
|
|
||||||
|
# Vulkan is required for slang shaders and unlocks hardware renderers in beetle-psx-hw, parallel-n64, dolphin
|
||||||
|
set_cfg video_driver "vulkan"
|
||||||
|
|
||||||
|
# XMB is the classic PS3-style menu (vs. ozone/rgui/glui)
|
||||||
|
set_cfg menu_driver "xmb"
|
||||||
|
|
||||||
|
# Default to crt-royale shader for that classic CRT look. The global preset is
|
||||||
|
# auto-loaded by RetroArch when auto_shaders_enable is true and no per-core/per-game
|
||||||
|
# preset takes precedence — setting video_shader alone in retroarch.cfg is not enough.
|
||||||
|
set_cfg video_shader_enable "true"
|
||||||
|
set_cfg auto_shaders_enable "true"
|
||||||
|
mkdir -p ~/.config/retroarch/config
|
||||||
|
echo '#reference "/usr/share/libretro/shaders/shaders_slang/crt/crt-royale.slangp"' \
|
||||||
|
> ~/.config/retroarch/config/global.slangp
|
||||||
|
|
||||||
|
# Hide Images and Video tabs in the main menu sidebar
|
||||||
|
set_cfg content_show_images "false"
|
||||||
|
set_cfg content_show_video "false"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Put your roms and bios files in ~/Games. Then start RetroArch from the app launcher (Super + Space)."
|
||||||
|
|
||||||
|
setsid nautilus "$HOME/Games" >/dev/null 2>&1 &
|
||||||
Executable
+15
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Install Steam and graphics drivers selected for this system
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Installing Steam..."
|
||||||
|
blob-pkg-add steam
|
||||||
|
blob-install-gaming-gpu-lib32
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Steam will start automatically now. This might take a while..."
|
||||||
|
|
||||||
|
setsid uwsm-app -- gtk-launch steam >/dev/null 2>&1 &
|
||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Install Xbox Cloud Gaming as a web app and launch it.
|
||||||
|
# blob:group=install
|
||||||
|
# blob:name=gaming xbox-cloud
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Installing Xbox Cloud Gaming..."
|
||||||
|
blob-webapp-install "Xbox Cloud Gaming" "https://www.xbox.com/en-US/play" "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/xbox.png"
|
||||||
|
|
||||||
|
setsid blob-launch-webapp "https://www.xbox.com/en-US/play" >/dev/null 2>&1 &
|
||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Install support for using Xbox controllers with Steam/RetroArch/etc.
|
||||||
|
# blob:group=install
|
||||||
|
# blob:name=gaming xbox-controllers
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Installing Xbox controller Bluetooth support..."
|
||||||
|
|
||||||
|
# Install xpadneo to ensure controllers work out of the box
|
||||||
|
blob-pkg-add xpadneo-dkms
|
||||||
|
|
||||||
|
# Prevent xpad/xpadneo driver conflict
|
||||||
|
echo blacklist xpad | sudo tee /etc/modprobe.d/blacklist-xpad.conf >/dev/null
|
||||||
|
echo hid_xpadneo | sudo tee /etc/modules-load.d/xpadneo.conf >/dev/null
|
||||||
|
|
||||||
|
# Ensure user is in the input group (controllers need it)
|
||||||
|
needs_reboot=false
|
||||||
|
if ! id -nG "$USER" | grep -qw input; then
|
||||||
|
sudo usermod -aG input "$USER"
|
||||||
|
needs_reboot=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Swap drivers in the running kernel so a reboot isn't needed otherwise
|
||||||
|
if lsmod | grep -q '^xpad '; then
|
||||||
|
sudo modprobe -r xpad 2>/dev/null || needs_reboot=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $needs_reboot; then
|
||||||
|
gum confirm "Reboot needed to finish setup. Reboot now?" && sudo reboot now
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo modprobe hid_xpadneo
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Now you can pair your Xbox controller with Bluetooth using Super + Ctrl + B."
|
||||||
Executable
+48
@@ -0,0 +1,48 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Launch the installed Battle.net client via umu-launcher + GE-Proton.
|
||||||
|
# blob:args=[--with-mangohud]
|
||||||
|
# blob:examples=blob launch battlenet | blob launch battlenet --with-mangohud
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
PREFIX="$HOME/Games/battlenet"
|
||||||
|
LAUNCHER="$PREFIX/drive_c/Program Files (x86)/Battle.net/Battle.net Launcher.exe"
|
||||||
|
|
||||||
|
with_mangohud=0
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--with-mangohud) with_mangohud=1 ;;
|
||||||
|
-h|--help)
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: blob-launch-battlenet [--with-mangohud]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--with-mangohud Enable the MangoHud FPS overlay for games launched from
|
||||||
|
Battle.net. Toggle perf logging in-game with Shift_L+F2;
|
||||||
|
CSV logs land in ~/mangohud/.
|
||||||
|
EOF
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown argument: $arg" >&2
|
||||||
|
echo "Try: blob-launch-battlenet --help" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ! -f $LAUNCHER ]]; then
|
||||||
|
echo "Battle.net is not installed. Run blob-install-gaming-battlenet first." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
env_args=(
|
||||||
|
WINEPREFIX="$PREFIX"
|
||||||
|
PROTONPATH=GE-Proton
|
||||||
|
GAMEID=umu-battlenet
|
||||||
|
PROTON_VERB=run
|
||||||
|
)
|
||||||
|
(( with_mangohud )) && env_args+=(MANGOHUD=1)
|
||||||
|
|
||||||
|
env "${env_args[@]}" umu-run "$LAUNCHER"
|
||||||
Executable
+15
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Launch or focus on a given web app identified by the window-pattern.
|
||||||
|
# blob:args=<window-pattern> <url-and-flags...>
|
||||||
|
|
||||||
|
if (($# == 0)); then
|
||||||
|
echo "Usage: blob-launch-or-focus-webapp [window-pattern] [url-and-flags...]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
WINDOW_PATTERN="$1"
|
||||||
|
shift
|
||||||
|
LAUNCH_COMMAND="blob-launch-webapp $@"
|
||||||
|
|
||||||
|
exec blob-launch-or-focus "$WINDOW_PATTERN" "$LAUNCH_COMMAND"
|
||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Launch a URL as a web app in the default supported browser
|
||||||
|
# blob:args=<url>
|
||||||
|
|
||||||
|
browser=$(xdg-settings get default-web-browser)
|
||||||
|
|
||||||
|
case $browser in
|
||||||
|
google-chrome* | brave* | microsoft-edge* | opera* | vivaldi* | helium*) ;;
|
||||||
|
*) browser="chromium.desktop" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exec setsid uwsm-app -- $(sed -n 's/^Exec=\([^ ]*\).*/\1/p' {~/.local,~/.nix-profile,/usr}/share/applications/$browser 2>/dev/null | head -1) --app="$1" "${@:2}"
|
||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Add the named packages to the system from the AUR if they're missing. Returns false if it couldn't be done.
|
||||||
|
# blob:args=<packages...>
|
||||||
|
|
||||||
|
if blob-pkg-missing "$@"; then
|
||||||
|
yay -S --noconfirm --needed "$@" || exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for pkg in "$@"; do
|
||||||
|
# Secondary check to handle states where pacman doesn't actually register an error
|
||||||
|
if ! pacman -Q "$pkg" &>/dev/null; then
|
||||||
|
echo -e "\033[31mError: Package '$pkg' did not install\033[0m" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
||||||
Executable
+70
@@ -0,0 +1,70 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Remove a supported browser and clean up Blob browser defaults
|
||||||
|
# blob:args=<chrome|brave|brave-origin|edge|firefox|zen>
|
||||||
|
# blob:examples=blob remove browser firefox | blob remove browser brave
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
set_fallback_default_browser() {
|
||||||
|
local current_browser
|
||||||
|
current_browser=$(env -u BROWSER xdg-settings get default-web-browser)
|
||||||
|
|
||||||
|
if [[ $current_browser != $1 ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if blob-cmd-present chromium; then
|
||||||
|
env -u BROWSER xdg-settings set default-web-browser chromium.desktop || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
chrome)
|
||||||
|
echo "Removing Chrome..."
|
||||||
|
set_fallback_default_browser google-chrome.desktop
|
||||||
|
blob-pkg-drop google-chrome
|
||||||
|
rm -f ~/.config/chrome-flags.conf
|
||||||
|
sudo rm -f /etc/opt/chrome/policies/managed/color.json
|
||||||
|
;;
|
||||||
|
edge)
|
||||||
|
echo "Removing Edge..."
|
||||||
|
set_fallback_default_browser microsoft-edge.desktop
|
||||||
|
blob-pkg-drop microsoft-edge-stable-bin
|
||||||
|
rm -f ~/.config/microsoft-edge-stable-flags.conf
|
||||||
|
sudo rm -f /etc/opt/edge/policies/managed/color.json
|
||||||
|
;;
|
||||||
|
brave)
|
||||||
|
echo "Removing Brave..."
|
||||||
|
set_fallback_default_browser brave-browser.desktop
|
||||||
|
blob-pkg-drop brave-bin
|
||||||
|
rm -f ~/.config/brave-flags.conf
|
||||||
|
|
||||||
|
if blob-pkg-missing brave-origin-bin; then
|
||||||
|
sudo rm -rf /etc/brave
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
brave-origin)
|
||||||
|
echo "Removing Brave Origin..."
|
||||||
|
set_fallback_default_browser brave-origin.desktop
|
||||||
|
blob-pkg-drop brave-origin-bin
|
||||||
|
rm -f ~/.config/brave-origin-flags.conf
|
||||||
|
|
||||||
|
if blob-pkg-missing brave-bin; then
|
||||||
|
sudo rm -rf /etc/brave
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
firefox)
|
||||||
|
echo "Removing Firefox..."
|
||||||
|
set_fallback_default_browser firefox.desktop
|
||||||
|
blob-pkg-drop firefox
|
||||||
|
;;
|
||||||
|
zen)
|
||||||
|
echo "Removing Zen..."
|
||||||
|
set_fallback_default_browser zen.desktop
|
||||||
|
blob-pkg-drop zen-browser-bin
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: blob-remove-browser <chrome|brave|brave-origin|edge|firefox|zen>"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Executable
+113
@@ -0,0 +1,113 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Remove a development environment that was previously installed via blob-install-dev-env.
|
||||||
|
# blob:args=<ruby|node|bun|deno|go|php|laravel|symfony|python|elixir|phoenix|zig|rust|java|dotnet|ocaml|clojure|scala>
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
if [[ -z $1 ]]; then
|
||||||
|
echo "Usage: blob-remove-dev-env <ruby|node|bun|deno|go|php|laravel|symfony|python|elixir|phoenix|zig|rust|java|dotnet|ocaml|clojure|scala>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
remove_php() {
|
||||||
|
sudo pacman -Rns --noconfirm php composer php-sqlite xdebug 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
ruby)
|
||||||
|
echo -e "Removing Ruby...\n"
|
||||||
|
mise uninstall ruby --all
|
||||||
|
mise rm -g ruby
|
||||||
|
rm -f ~/.gemrc
|
||||||
|
;;
|
||||||
|
node)
|
||||||
|
echo -e "Removing Node.js...\n"
|
||||||
|
mise uninstall node --all
|
||||||
|
mise rm -g node
|
||||||
|
;;
|
||||||
|
bun)
|
||||||
|
echo -e "Removing Bun...\n"
|
||||||
|
mise uninstall bun --all
|
||||||
|
mise rm -g bun
|
||||||
|
;;
|
||||||
|
deno)
|
||||||
|
echo -e "Removing Deno...\n"
|
||||||
|
mise uninstall deno --all
|
||||||
|
mise rm -g deno
|
||||||
|
;;
|
||||||
|
go)
|
||||||
|
echo -e "Removing Go...\n"
|
||||||
|
mise uninstall go --all
|
||||||
|
mise rm -g go
|
||||||
|
;;
|
||||||
|
php)
|
||||||
|
echo -e "Removing PHP...\n"
|
||||||
|
remove_php
|
||||||
|
;;
|
||||||
|
laravel)
|
||||||
|
echo -e "Removing Laravel...\n"
|
||||||
|
composer global remove laravel/installer 2>/dev/null || true
|
||||||
|
;;
|
||||||
|
symfony)
|
||||||
|
echo -e "Removing Symfony CLI...\n"
|
||||||
|
sudo pacman -Rns --noconfirm symfony-cli 2>/dev/null || true
|
||||||
|
;;
|
||||||
|
python)
|
||||||
|
echo -e "Removing Python...\n"
|
||||||
|
mise uninstall python --all
|
||||||
|
mise rm -g python
|
||||||
|
rm -rf ~/.local/bin/uv ~/.local/bin/uvx ~/.cargo/bin/uv 2>/dev/null || true
|
||||||
|
;;
|
||||||
|
elixir|phoenix)
|
||||||
|
echo -e "Removing Elixir/Erlang...\n"
|
||||||
|
mise uninstall elixir --all
|
||||||
|
mise uninstall erlang --all
|
||||||
|
mise rm -g elixir
|
||||||
|
mise rm -g erlang
|
||||||
|
;;
|
||||||
|
zig)
|
||||||
|
echo -e "Removing Zig...\n"
|
||||||
|
mise uninstall zig --all
|
||||||
|
mise uninstall zls --all
|
||||||
|
mise rm -g zig
|
||||||
|
mise rm -g zls
|
||||||
|
;;
|
||||||
|
rust)
|
||||||
|
echo -e "Removing Rust...\n"
|
||||||
|
rustup self uninstall -y 2>/dev/null || true
|
||||||
|
;;
|
||||||
|
java)
|
||||||
|
echo -e "Removing Java...\n"
|
||||||
|
mise uninstall java --all
|
||||||
|
mise rm -g java
|
||||||
|
;;
|
||||||
|
dotnet)
|
||||||
|
echo -e "Removing .NET...\n"
|
||||||
|
mise uninstall dotnet --all
|
||||||
|
mise rm -g dotnet
|
||||||
|
;;
|
||||||
|
ocaml)
|
||||||
|
echo -e "Removing OCaml...\n"
|
||||||
|
opam switch remove default -y 2>/dev/null || true
|
||||||
|
rm -rf ~/.opam 2>/dev/null || true
|
||||||
|
sudo rm -f /usr/local/bin/opam 2>/dev/null || true
|
||||||
|
;;
|
||||||
|
clojure)
|
||||||
|
echo -e "Removing Clojure...\n"
|
||||||
|
mise uninstall clojure --all
|
||||||
|
mise rm -g clojure
|
||||||
|
;;
|
||||||
|
scala)
|
||||||
|
echo -e "Removing Scala...\n"
|
||||||
|
mise uninstall scala --all
|
||||||
|
mise uninstall scala-cli --all
|
||||||
|
mise rm -g scala
|
||||||
|
mise rm -g scala-cli
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown environment: $1"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo -e "\nDone!"
|
||||||
Executable
+35
@@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Remove Battle.net, its Proton prefix, installed games, and desktop entry.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
PREFIX="$HOME/Games/battlenet"
|
||||||
|
|
||||||
|
# Stop any running Battle.net / wine processes tied to this prefix.
|
||||||
|
pkill -f "$PREFIX" 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
rm -rf "$PREFIX"
|
||||||
|
rm -f "$HOME/.local/share/applications/battlenet.desktop"
|
||||||
|
rm -f "$HOME/.cache/blob/Battle.net-Setup.exe"
|
||||||
|
update-desktop-database "$HOME/.local/share/applications" 2>/dev/null || true
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Battle.net and its Proton prefix at $PREFIX have been removed."
|
||||||
|
|
||||||
|
if blob-pkg-present umu-launcher; then
|
||||||
|
echo
|
||||||
|
if gum confirm "Also remove umu-launcher? It's only used by this command."; then
|
||||||
|
blob-pkg-drop umu-launcher
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
PROTON_DIR="$HOME/.local/share/Steam/compatibilitytools.d"
|
||||||
|
if compgen -G "$PROTON_DIR/GE-Proton*" >/dev/null; then
|
||||||
|
echo
|
||||||
|
if gum confirm "Also remove GE-Proton runtimes downloaded by umu?"; then
|
||||||
|
rm -rf "$PROTON_DIR"/GE-Proton*
|
||||||
|
rm -rf "$HOME/.local/share/umu"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
Executable
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Remove the GeForce NOW Flatpak app and its data.
|
||||||
|
# blob:group=remove
|
||||||
|
# blob:name=gaming geforce-now
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if blob-cmd-present flatpak && flatpak info com.nvidia.geforcenow &>/dev/null; then
|
||||||
|
flatpak uninstall -y --delete-data com.nvidia.geforcenow
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "GeForce NOW removed."
|
||||||
Executable
+17
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Remove Heroic Games Launcher and its game libraries, configs, and caches.
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
blob-pkg-drop heroic-games-launcher-bin
|
||||||
|
|
||||||
|
rm -rf \
|
||||||
|
"$HOME/.config/heroic" \
|
||||||
|
"$HOME/.local/share/heroic" \
|
||||||
|
"$HOME/.cache/heroic" \
|
||||||
|
"$HOME/Games/Heroic"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Heroic and its data have been removed."
|
||||||
Executable
+21
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Remove Lutris, Wine, umu-launcher, and all their configs and caches.
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
blob-pkg-drop lutris wine-staging wine-mono wine-gecko winetricks python-protobuf umu-launcher
|
||||||
|
|
||||||
|
rm -rf \
|
||||||
|
"$HOME/.config/lutris" \
|
||||||
|
"$HOME/.local/share/lutris" \
|
||||||
|
"$HOME/.cache/lutris" \
|
||||||
|
"$HOME/.local/share/umu" \
|
||||||
|
"$HOME/.cache/umu" \
|
||||||
|
"$HOME/.wine" \
|
||||||
|
"$HOME/.cache/wine" \
|
||||||
|
"$HOME/.cache/winetricks"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Lutris, Wine, umu-launcher, and their configs have been removed."
|
||||||
Executable
+17
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Remove the Minecraft launcher along with its worlds, mods, and caches.
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
blob-pkg-drop minecraft-launcher
|
||||||
|
|
||||||
|
rm -rf \
|
||||||
|
"$HOME/.minecraft" \
|
||||||
|
"$HOME/.config/Minecraft Launcher" \
|
||||||
|
"$HOME/.local/share/minecraft-launcher" \
|
||||||
|
"$HOME/.cache/minecraft"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Minecraft and its data have been removed."
|
||||||
Executable
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Remove RetroArch, all libretro cores, and its config/saves. Leaves ~/Games/roms and ~/Games/bios alone.
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
blob-pkg-drop \
|
||||||
|
retroarch \
|
||||||
|
retroarch-assets-glui retroarch-assets-ozone retroarch-assets-xmb \
|
||||||
|
libretro-beetle-pce libretro-beetle-pce-fast libretro-beetle-psx libretro-beetle-psx-hw libretro-beetle-supergrafx \
|
||||||
|
libretro-blastem \
|
||||||
|
libretro-bsnes libretro-bsnes-hd \
|
||||||
|
libretro-core-info \
|
||||||
|
libretro-desmume libretro-dolphin libretro-flycast \
|
||||||
|
libretro-gambatte libretro-genesis-plus-gx \
|
||||||
|
libretro-kronos \
|
||||||
|
libretro-mame libretro-melonds libretro-mesen libretro-mesen-s libretro-mgba libretro-mupen64plus-next \
|
||||||
|
libretro-nestopia \
|
||||||
|
libretro-overlays \
|
||||||
|
libretro-parallel-n64 libretro-picodrive libretro-play libretro-ppsspp \
|
||||||
|
libretro-sameboy libretro-scummvm libretro-shaders-slang libretro-snes9x \
|
||||||
|
libretro-yabause \
|
||||||
|
libretro-cap32-git libretro-fbneo-git libretro-uae-git \
|
||||||
|
libretro-vice-x128-git libretro-vice-x64-git libretro-vice-x64dtv-git libretro-vice-x64sc-git \
|
||||||
|
libretro-vice-xcbm2-git libretro-vice-xcbm5x0-git libretro-vice-xpet-git \
|
||||||
|
libretro-vice-xplus4-git libretro-vice-xscpu64-git libretro-vice-xvic-git \
|
||||||
|
retroarch-joypad-autoconfig-git
|
||||||
|
|
||||||
|
rm -rf \
|
||||||
|
"$HOME/.config/retroarch" \
|
||||||
|
"$HOME/.local/share/retroarch" \
|
||||||
|
"$HOME/.cache/retroarch"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "RetroArch and its cores have been removed."
|
||||||
|
echo "ROMs and BIOS files at ~/Games/roms and ~/Games/bios were left in place."
|
||||||
Executable
+17
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Remove Steam and all of its game libraries, configs, and caches.
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
blob-pkg-drop steam
|
||||||
|
|
||||||
|
rm -rf \
|
||||||
|
"$HOME/.steam" \
|
||||||
|
"$HOME/.local/share/Steam" \
|
||||||
|
"$HOME/.config/steam" \
|
||||||
|
"$HOME/.cache/steam"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Steam and its data have been removed."
|
||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Remove the Xbox Cloud Gaming web app.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
blob-webapp-remove "Xbox Cloud Gaming"
|
||||||
Executable
+15
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Remove the xpadneo Xbox controller driver and undo its module/blacklist config.
|
||||||
|
# blob:group=remove
|
||||||
|
# blob:name=gaming xbox-controllers
|
||||||
|
# blob:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
blob-pkg-drop xpadneo-dkms
|
||||||
|
|
||||||
|
sudo rm -f /etc/modprobe.d/blacklist-xpad.conf /etc/modules-load.d/xpadneo.conf
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Xbox controller support removed. Reboot to fully unload xpadneo and restore xpad."
|
||||||
Executable
+15
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Open HEY webmail and translate mailto links
|
||||||
|
# blob:args=[url]
|
||||||
|
|
||||||
|
url="$1"
|
||||||
|
web_url="https://app.hey.com"
|
||||||
|
|
||||||
|
# Handle mailto: URLs
|
||||||
|
if [[ $url =~ ^mailto: ]]; then
|
||||||
|
email=$(echo "$url" | sed 's/mailto://')
|
||||||
|
web_url="https://app.hey.com/messages/new?to=$email"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec blob-launch-webapp "$web_url"
|
||||||
Executable
+23
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Open Zoom web meetings from browser protocol links
|
||||||
|
# blob:args=[url]
|
||||||
|
|
||||||
|
url="$1"
|
||||||
|
web_url="https://app.zoom.us/wc/home"
|
||||||
|
|
||||||
|
if [[ $url =~ ^zoom(mtg|us):// ]]; then
|
||||||
|
confno=$(echo "$url" | sed -n 's/.*[?&]confno=\([^&]*\).*/\1/p')
|
||||||
|
|
||||||
|
if [[ -n $confno ]]; then
|
||||||
|
pwd=$(echo "$url" | sed -n 's/.*[?&]pwd=\([^&]*\).*/\1/p')
|
||||||
|
|
||||||
|
if [[ -n $pwd ]]; then
|
||||||
|
web_url="https://app.zoom.us/wc/join/$confno?pwd=$pwd"
|
||||||
|
else
|
||||||
|
web_url="https://app.zoom.us/wc/join/$confno"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec blob-launch-webapp "$web_url"
|
||||||
Executable
+241
@@ -0,0 +1,241 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Create a desktop launcher for a web app
|
||||||
|
# blob:args=[name url icon-url-or-name [custom-exec] [mime-types]]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
ICON_DIR="$HOME/.local/share/icons/hicolor/256x256/apps"
|
||||||
|
|
||||||
|
safe_icon_name() {
|
||||||
|
printf '%s\n' "$1" \
|
||||||
|
| tr '[:upper:]' '[:lower:]' \
|
||||||
|
| sed 's/[^[:alnum:]]\+/-/g; s/^-//; s/-$//'
|
||||||
|
}
|
||||||
|
|
||||||
|
require_plain_name() {
|
||||||
|
# The name becomes a filename. A slash would turn it into directory levels, so
|
||||||
|
# the launcher lands somewhere blob-webapp-remove cannot address and the app
|
||||||
|
# is stuck in the launcher; a leading ../ leaves the applications directory
|
||||||
|
# altogether. Refuse rather than silently renaming what the user typed -- most
|
||||||
|
# often it is a URL entered in the name field.
|
||||||
|
if [[ $1 == */* ]]; then
|
||||||
|
echo "App name cannot contain '/': $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
icon_name_from_ref() {
|
||||||
|
local ref="$1"
|
||||||
|
local name
|
||||||
|
name=$(basename "$ref")
|
||||||
|
|
||||||
|
if [[ $name == *.* ]]; then
|
||||||
|
safe_icon_name "${name%.*}"
|
||||||
|
else
|
||||||
|
printf '%s\n' "$name"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
install_user_icon() {
|
||||||
|
local source="$1"
|
||||||
|
local name="$2"
|
||||||
|
local ext="${source##*.}"
|
||||||
|
|
||||||
|
[[ $ext == "$source" ]] && ext="png"
|
||||||
|
mkdir -p "$ICON_DIR"
|
||||||
|
cp "$source" "$ICON_DIR/$name.$ext"
|
||||||
|
gtk-update-icon-cache "$HOME/.local/share/icons/hicolor" &>/dev/null || true
|
||||||
|
printf '%s\n' "$name"
|
||||||
|
}
|
||||||
|
|
||||||
|
download_icon() {
|
||||||
|
curl -fsSL --max-time 10 -o "$2" "$1" 2>/dev/null &&
|
||||||
|
[[ -s $2 && $(file -b --mime-type "$2") == image/* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Chromium --app= treats javascript:, file:, and data: as a document to
|
||||||
|
# run. Prefix schemeless input with https as before, then refuse anything
|
||||||
|
# that is not http(s).
|
||||||
|
normalize_webapp_url() {
|
||||||
|
local url=$1
|
||||||
|
if [[ ! $url =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then
|
||||||
|
url="https://$url"
|
||||||
|
fi
|
||||||
|
printf '%s' "$url"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Raw whitespace must be percent-encoded in a URL. Refuse it before serializing
|
||||||
|
# the desktop entry; before Exec argument quoting, it also split browser flags
|
||||||
|
# and additional URLs into separate arguments. Schemes are case-insensitive.
|
||||||
|
require_http_url() {
|
||||||
|
local url=$1
|
||||||
|
|
||||||
|
if [[ $url =~ [[:space:]] ]]; then
|
||||||
|
echo "Error: web app URL must not contain whitespace." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! ${url,,} =~ ^https?:// ]]; then
|
||||||
|
echo "Error: web app URL must be http or https." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch_site_icon() {
|
||||||
|
local site_url="$1" dest="$2"
|
||||||
|
local origin page icon_url
|
||||||
|
origin=$(sed -E 's|^(https?://[^/]+).*|\1|' <<<"$site_url")
|
||||||
|
|
||||||
|
# Prefer the site's own high-res icon (apple-touch-icon is typically 180px+),
|
||||||
|
# then the well-known path, then Google's favicon service as a last resort.
|
||||||
|
page=$(curl -fsSL --max-time 5 "$site_url" 2>/dev/null | head -c 100000 | tr '\n' ' ')
|
||||||
|
icon_url=$(grep -oiE "<link[^>]*rel=[\"'][^\"']*apple-touch-icon[^\"']*[\"'][^>]*>" <<<"$page" |
|
||||||
|
grep -oiE "href=[\"'][^\"']+" | head -1 | sed -E "s/^href=[\"']//")
|
||||||
|
|
||||||
|
case $icon_url in
|
||||||
|
http://* | https://*) ;;
|
||||||
|
//*) icon_url="https:$icon_url" ;;
|
||||||
|
/*) icon_url="$origin$icon_url" ;;
|
||||||
|
?*) icon_url="$origin/$icon_url" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
{ [[ -n $icon_url ]] && download_icon "$icon_url" "$dest"; } ||
|
||||||
|
download_icon "$origin/apple-touch-icon.png" "$dest" ||
|
||||||
|
download_icon "https://www.google.com/s2/favicons?domain=${site_url}&sz=256" "$dest"
|
||||||
|
}
|
||||||
|
|
||||||
|
desktop_string_escape() {
|
||||||
|
# Desktop Entry "string" value (freedesktop Desktop Entry Spec, "Value types"):
|
||||||
|
# a raw newline would start a new key line and let a value inject a second
|
||||||
|
# Exec=. Escape backslash first, then tab/CR/LF and a leading space. Every value
|
||||||
|
# written into the .desktop file passes through here.
|
||||||
|
#
|
||||||
|
# Parameter expansion rather than sed: GNU sed's N auto-prints the pattern space
|
||||||
|
# and exits at end of input, so a `:a;N;$!ba` slurp skips every following s///
|
||||||
|
# for a value with no newline in it - which is every value except the injection
|
||||||
|
# attempt this exists to stop.
|
||||||
|
local value="$1"
|
||||||
|
|
||||||
|
value=${value//\\/\\\\}
|
||||||
|
value=${value//$'\t'/\\t}
|
||||||
|
value=${value//$'\r'/\\r}
|
||||||
|
value=${value//$'\n'/\\n}
|
||||||
|
[[ $value == " "* ]] && value="\\s${value# }"
|
||||||
|
|
||||||
|
printf '%s' "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
desktop_exec_arg() {
|
||||||
|
# One Exec argument, double-quoted per the freedesktop Exec spec: inside quotes
|
||||||
|
# " ` $ \ take a backslash and a literal % becomes %%. Only the default Exec's
|
||||||
|
# URL needs this; $CUSTOM_EXEC stays a whole command line (file-syntax only).
|
||||||
|
local escaped
|
||||||
|
escaped=$(printf '%s' "$1" \
|
||||||
|
| sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/`/\\`/g' -e 's/\$/\\$/g' -e 's/%/%%/g')
|
||||||
|
printf '"%s"' "$escaped"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (( $# < 3 )); then
|
||||||
|
echo -e "\e[32mLet's create a new web app you can start with the app launcher.\n\e[0m"
|
||||||
|
APP_NAME=$(gum input --prompt "Name> " --placeholder "My favorite web app")
|
||||||
|
require_plain_name "$APP_NAME"
|
||||||
|
APP_URL=$(gum input --prompt "URL> " --placeholder "https://example.com")
|
||||||
|
APP_URL=$(normalize_webapp_url "$APP_URL")
|
||||||
|
require_http_url "$APP_URL"
|
||||||
|
|
||||||
|
# Try to fetch the site's icon automatically first.
|
||||||
|
mkdir -p "$ICON_DIR"
|
||||||
|
ICON_VALUE=$(safe_icon_name "$APP_NAME")
|
||||||
|
if fetch_site_icon "$APP_URL" "$ICON_DIR/$ICON_VALUE.png"; then
|
||||||
|
gtk-update-icon-cache "$HOME/.local/share/icons/hicolor" &>/dev/null || true
|
||||||
|
ICON_REF="$ICON_VALUE"
|
||||||
|
else
|
||||||
|
ICON_REF=$(gum input --prompt "Icon URL/name> " --placeholder "Could not fetch favicon automatically. Enter PNG icon URL or icon name")
|
||||||
|
fi
|
||||||
|
|
||||||
|
CUSTOM_EXEC=""
|
||||||
|
MIME_TYPES=""
|
||||||
|
INTERACTIVE_MODE=true
|
||||||
|
else
|
||||||
|
APP_NAME="$1"
|
||||||
|
APP_URL=$(normalize_webapp_url "$2")
|
||||||
|
require_http_url "$APP_URL"
|
||||||
|
ICON_REF="$3"
|
||||||
|
CUSTOM_EXEC="$4" # Optional custom exec command
|
||||||
|
MIME_TYPES="$5" # Optional mime types
|
||||||
|
INTERACTIVE_MODE=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure valid execution
|
||||||
|
if [[ -z $APP_NAME || -z $APP_URL ]]; then
|
||||||
|
echo "You must set app name and app URL!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
require_plain_name "$APP_NAME"
|
||||||
|
|
||||||
|
if [[ -z $ICON_REF ]]; then
|
||||||
|
ICON_VALUE=$(safe_icon_name "$APP_NAME")
|
||||||
|
mkdir -p "$ICON_DIR"
|
||||||
|
if ! fetch_site_icon "$APP_URL" "$ICON_DIR/$ICON_VALUE.png"; then
|
||||||
|
echo "Error: Failed to download icon."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
gtk-update-icon-cache "$HOME/.local/share/icons/hicolor" &>/dev/null || true
|
||||||
|
elif [[ $ICON_REF =~ ^https?:// ]]; then
|
||||||
|
ICON_VALUE=$(safe_icon_name "$APP_NAME")
|
||||||
|
mkdir -p "$ICON_DIR"
|
||||||
|
if ! download_icon "$ICON_REF" "$ICON_DIR/$ICON_VALUE.png"; then
|
||||||
|
echo "Error: Failed to download icon."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
gtk-update-icon-cache "$HOME/.local/share/icons/hicolor" &>/dev/null || true
|
||||||
|
elif [[ -f $ICON_REF ]]; then
|
||||||
|
ICON_VALUE=$(install_user_icon "$ICON_REF" "$(safe_icon_name "$APP_NAME")")
|
||||||
|
else
|
||||||
|
# Bundled Blob icons are package-owned under /usr/share/icons/hicolor.
|
||||||
|
# Accept either "HEY" or the historical "HEY.png" argument form.
|
||||||
|
ICON_VALUE=$(icon_name_from_ref "$ICON_REF")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Default Exec quotes the URL as one Exec-spec argument; the whole line then gets
|
||||||
|
# the file-syntax escaping below (unescaped first at read time per spec, so the
|
||||||
|
# layers compose). $CUSTOM_EXEC is a full command line, so it gets file-syntax only.
|
||||||
|
if [[ -n $CUSTOM_EXEC ]]; then
|
||||||
|
EXEC_COMMAND=$CUSTOM_EXEC
|
||||||
|
else
|
||||||
|
EXEC_COMMAND="blob-launch-webapp $(desktop_exec_arg "$APP_URL")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create application .desktop file
|
||||||
|
DESKTOP_DIR="$HOME/.local/share/applications"
|
||||||
|
DESKTOP_FILE="$DESKTOP_DIR/$APP_NAME.desktop"
|
||||||
|
mkdir -p "$DESKTOP_DIR"
|
||||||
|
|
||||||
|
name_field=$(desktop_string_escape "$APP_NAME")
|
||||||
|
exec_field=$(desktop_string_escape "$EXEC_COMMAND")
|
||||||
|
icon_field=$(desktop_string_escape "$ICON_VALUE")
|
||||||
|
|
||||||
|
cat >"$DESKTOP_FILE" <<EOF
|
||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Name=$name_field
|
||||||
|
Comment=$name_field
|
||||||
|
Exec=$exec_field
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Icon=$icon_field
|
||||||
|
StartupNotify=true
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Add mime types if provided
|
||||||
|
if [[ -n $MIME_TYPES ]]; then
|
||||||
|
printf 'MimeType=%s\n' "$(desktop_string_escape "$MIME_TYPES")" >>"$DESKTOP_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x "$DESKTOP_FILE"
|
||||||
|
|
||||||
|
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||||
|
echo -e "You can now find $APP_NAME using the app launcher (SUPER + SPACE)\n"
|
||||||
|
fi
|
||||||
Executable
+61
@@ -0,0 +1,61 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# blob:summary=Remove a web app desktop launcher
|
||||||
|
# blob:args=[name]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
ICON_DIR="$HOME/.local/share/icons/hicolor/256x256/apps"
|
||||||
|
OLD_ICON_DIR="$HOME/.local/share/applications/icons"
|
||||||
|
DESKTOP_DIR="$HOME/.local/share/applications/"
|
||||||
|
|
||||||
|
# Always index the launchers, so removal deletes the file that was found rather
|
||||||
|
# than a path rebuilt from the displayed name. Installs predating the name
|
||||||
|
# validation could nest the launcher inside directories, and those are exactly
|
||||||
|
# the ones a reconstructed path cannot reach.
|
||||||
|
WEB_APP_PATHS=()
|
||||||
|
while IFS= read -r -d '' file; do
|
||||||
|
if grep -q '^Exec=.*\(blob-launch-webapp\|blob-webapp-handler\).*' "$file"; then
|
||||||
|
WEB_APPS+=("$(basename "${file%.desktop}")")
|
||||||
|
WEB_APP_PATHS+=("$file")
|
||||||
|
fi
|
||||||
|
done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0 2>/dev/null)
|
||||||
|
|
||||||
|
# The launcher matching a chosen name, or empty when nothing was indexed under
|
||||||
|
# it (an app removed between the scan and the pick, say).
|
||||||
|
path_for_web_app() {
|
||||||
|
local wanted="$1" i
|
||||||
|
for i in "${!WEB_APPS[@]}"; do
|
||||||
|
if [[ ${WEB_APPS[$i]} == "$wanted" ]]; then
|
||||||
|
printf '%s\n' "${WEB_APP_PATHS[$i]}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
if (( $# == 0 )); then
|
||||||
|
if ((${#WEB_APPS[@]})); then
|
||||||
|
mapfile -t SORTED_WEB_APPS < <(printf '%s\n' "${WEB_APPS[@]}" | sort)
|
||||||
|
APP_NAME=$(blob-menu-select "Select web app to remove" "${SORTED_WEB_APPS[@]}" -- --width 520 --maxheight 520)
|
||||||
|
else
|
||||||
|
echo "No web apps to remove."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
APP_NAME="$*"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $APP_NAME ]]; then
|
||||||
|
echo "You must select a web app to remove."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
icon_name=$(printf '%s\n' "$APP_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^[:alnum:]]\+/-/g; s/^-//; s/-$//')
|
||||||
|
desktop_file=$(path_for_web_app "$APP_NAME")
|
||||||
|
rm -f "${desktop_file:-$DESKTOP_DIR/$APP_NAME.desktop}"
|
||||||
|
rm -f "$ICON_DIR/$icon_name.png" "$ICON_DIR/$APP_NAME.png" "$OLD_ICON_DIR/$APP_NAME.png"
|
||||||
|
|
||||||
|
if [[ ${BLOB_REMOVE_NOTIFY:-true} != "false" ]]; then
|
||||||
|
blob-notify-send -g "Web app removed" "$APP_NAME"
|
||||||
|
fi
|
||||||
|
update-desktop-database "$DESKTOP_DIR" &>/dev/null
|
||||||
Executable
+1591
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,88 @@
|
|||||||
"setup": {"icon":"","label":"Setup","aliases":["settings"]},
|
"setup": {"icon":"","label":"Setup","aliases":["settings"]},
|
||||||
"install": {"icon":"","label":"Install"},
|
"install": {"icon":"","label":"Install"},
|
||||||
"remove": {"icon":"","label":"Remove","aliases":["uninstall"]},
|
"remove": {"icon":"","label":"Remove","aliases":["uninstall"]},
|
||||||
|
"install.development": {"icon":"","label":"Development"},
|
||||||
|
"install.browser": {"icon":"","label":"Browser"},
|
||||||
|
"install.gaming": {"icon":"","label":"Gaming"},
|
||||||
|
"install.windows": {"icon":"","label":"Windows","when":"[[ ! -f $HOME/.local/share/applications/windows-vm.desktop ]]","action":"blob-launch-floating 'blob-windows-vm install'"},
|
||||||
|
"install.browser.chrome": {"icon":"","label":"Chrome","when":"! blob-pkg-present google-chrome","action":"blob-launch-floating 'blob-install-browser chrome'"},
|
||||||
|
"install.browser.edge": {"icon":"","label":"Edge","when":"! blob-pkg-present microsoft-edge-stable-bin","action":"blob-launch-floating 'blob-install-browser edge'"},
|
||||||
|
"install.browser.brave": {"icon":"","label":"Brave","when":"! blob-pkg-present brave-bin","action":"blob-launch-floating 'blob-install-browser brave'"},
|
||||||
|
"install.browser.brave-origin": {"icon":"","label":"Brave Origin","when":"! blob-pkg-present brave-origin-bin","action":"blob-launch-floating 'blob-install-browser brave-origin'"},
|
||||||
|
"install.browser.firefox": {"icon":"","label":"Firefox","when":"! blob-pkg-present firefox","action":"blob-launch-floating 'blob-install-browser firefox'"},
|
||||||
|
"install.browser.zen": {"icon":"","label":"Zen","when":"! blob-pkg-present zen-browser-bin","action":"blob-launch-floating 'blob-install-browser zen'"},
|
||||||
|
"install.gaming.steam": {"icon":"","label":"Steam","when":"! blob-pkg-present steam","action":"blob-launch-floating blob-install-gaming-steam"},
|
||||||
|
"install.gaming.retroarch": {"icon":"","label":"RetroArch","when":"! blob-pkg-present retroarch","action":"blob-launch-floating blob-install-gaming-retroarch"},
|
||||||
|
"install.gaming.minecraft": {"icon":"","label":"Minecraft","when":"! blob-pkg-present minecraft-launcher","action":"blob-install-launch Minecraft minecraft-launcher minecraft-launcher"},
|
||||||
|
"install.gaming.geforce-now": {"icon":"","label":"NVIDIA GeForce NOW","when":"! flatpak info com.nvidia.geforcenow","action":"blob-launch-floating blob-install-gaming-geforce-now"},
|
||||||
|
"install.gaming.xbox-cloud": {"icon":"","label":"Xbox Cloud Gaming","when":"[[ ! -f \"$HOME/.local/share/applications/Xbox Cloud Gaming.desktop\" ]]","action":"blob-launch-floating blob-install-gaming-xbox-cloud"},
|
||||||
|
"install.gaming.xbox-controllers": {"icon":"","label":"Xbox Controllers","when":"! blob-pkg-present xpadneo-dkms","action":"blob-launch-floating blob-install-gaming-xbox-controllers"},
|
||||||
|
"install.gaming.battlenet": {"icon":"","label":"Battle.net","when":"[[ ! -d $HOME/Games/battlenet ]]","action":"blob-launch-floating blob-install-gaming-battlenet"},
|
||||||
|
"install.gaming.lutris": {"icon":"","label":"Lutris","when":"! blob-pkg-present lutris","action":"blob-launch-floating blob-install-gaming-lutris"},
|
||||||
|
"install.gaming.heroic": {"icon":"","label":"Heroic (Epic Games)","when":"! blob-pkg-present heroic-games-launcher-bin","action":"blob-launch-floating blob-install-gaming-heroic"},
|
||||||
|
"install.gaming.retro-launcher": {"icon":"","label":"RetroArch Game Launcher","action":"blob-games-retro-install"},
|
||||||
|
"install.development.rails": {"icon":"","label":"Ruby on Rails","when":"[[ ! -d $HOME/.local/share/mise/installs/ruby ]]","action":"blob-launch-floating 'blob-install-dev-env ruby'"},
|
||||||
|
"install.development.docker-dbs": {"icon":"","label":"Docker DB","action":"blob-launch-floating blob-install-docker-dbs"},
|
||||||
|
"install.development.javascript": {"icon":"","label":"JavaScript"},
|
||||||
|
"install.development.go": {"icon":"","label":"Go","when":"[[ ! -d $HOME/.local/share/mise/installs/go ]]","action":"blob-launch-floating 'blob-install-dev-env go'"},
|
||||||
|
"install.development.php": {"icon":"","label":"PHP"},
|
||||||
|
"install.development.python": {"icon":"","label":"Python","when":"[[ ! -d $HOME/.local/share/mise/installs/python ]]","action":"blob-launch-floating 'blob-install-dev-env python'"},
|
||||||
|
"install.development.elixir": {"icon":"","label":"Elixir"},
|
||||||
|
"install.development.zig": {"icon":"","label":"Zig","when":"[[ ! -d $HOME/.local/share/mise/installs/zig ]]","action":"blob-launch-floating 'blob-install-dev-env zig'"},
|
||||||
|
"install.development.rust": {"icon":"","label":"Rust","when":"[[ ! -d $HOME/.rustup ]]","action":"blob-launch-floating 'blob-install-dev-env rust'"},
|
||||||
|
"install.development.java": {"icon":"","label":"Java","when":"[[ ! -d $HOME/.local/share/mise/installs/java ]]","action":"blob-launch-floating 'blob-install-dev-env java'"},
|
||||||
|
"install.development.dotnet": {"icon":"","label":".NET","when":"[[ ! -d $HOME/.local/share/mise/installs/dotnet ]]","action":"blob-launch-floating 'blob-install-dev-env dotnet'"},
|
||||||
|
"install.development.ocaml": {"icon":"","label":"OCaml","when":"[[ ! -d $HOME/.opam ]]","action":"blob-launch-floating 'blob-install-dev-env ocaml'"},
|
||||||
|
"install.development.clojure": {"icon":"","label":"Clojure","when":"[[ ! -d $HOME/.local/share/mise/installs/clojure ]]","action":"blob-launch-floating 'blob-install-dev-env clojure'"},
|
||||||
|
"install.development.scala": {"icon":"","label":"Scala","when":"[[ ! -d $HOME/.local/share/mise/installs/scala ]]","action":"blob-launch-floating 'blob-install-dev-env scala'"},
|
||||||
|
"install.development.javascript.node": {"icon":"","label":"Node.js","when":"[[ ! -d $HOME/.local/share/mise/installs/node ]]","action":"blob-launch-floating 'blob-install-dev-env node'"},
|
||||||
|
"install.development.javascript.bun": {"icon":"","label":"Bun","when":"[[ ! -d $HOME/.local/share/mise/installs/bun ]]","action":"blob-launch-floating 'blob-install-dev-env bun'"},
|
||||||
|
"install.development.javascript.deno": {"icon":"","label":"Deno","when":"[[ ! -d $HOME/.local/share/mise/installs/deno ]]","action":"blob-launch-floating 'blob-install-dev-env deno'"},
|
||||||
|
"install.development.php.php": {"icon":"","label":"PHP","when":"! blob-pkg-present php","action":"blob-launch-floating 'blob-install-dev-env php'"},
|
||||||
|
"install.development.php.laravel": {"icon":"","label":"Laravel","when":"[[ ! -x $HOME/.config/composer/vendor/bin/laravel ]]","action":"blob-launch-floating 'blob-install-dev-env laravel'"},
|
||||||
|
"install.development.php.symfony": {"icon":"","label":"Symfony","when":"! blob-pkg-present symfony-cli","action":"blob-launch-floating 'blob-install-dev-env symfony'"},
|
||||||
|
"install.development.elixir.elixir": {"icon":"","label":"Elixir","when":"[[ ! -d $HOME/.local/share/mise/installs/elixir ]]","action":"blob-launch-floating 'blob-install-dev-env elixir'"},
|
||||||
|
"install.development.elixir.phoenix": {"icon":"","label":"Phoenix","when":"! compgen -G \"$HOME/.mix/archives/phx_new*\"","action":"blob-launch-floating 'blob-install-dev-env phoenix'"},
|
||||||
|
"remove.development": {"icon":"","label":"Development","title":"Remove"},
|
||||||
|
"remove.browser": {"icon":"","label":"Browser","title":"Remove"},
|
||||||
|
"remove.gaming": {"icon":"","label":"Gaming","title":"Remove"},
|
||||||
|
"remove.windows": {"icon":"","label":"Windows","when":"[[ -f $HOME/.local/share/applications/windows-vm.desktop ]]","action":"blob-launch-floating 'blob-windows-vm remove'"},
|
||||||
|
"remove.browser.chrome": {"icon":"","label":"Chrome","when":"blob-pkg-present google-chrome","action":"blob-launch-floating 'blob-remove-browser chrome'"},
|
||||||
|
"remove.browser.edge": {"icon":"","label":"Edge","when":"blob-pkg-present microsoft-edge-stable-bin","action":"blob-launch-floating 'blob-remove-browser edge'"},
|
||||||
|
"remove.browser.brave": {"icon":"","label":"Brave","when":"blob-pkg-present brave-bin","action":"blob-launch-floating 'blob-remove-browser brave'"},
|
||||||
|
"remove.browser.brave-origin": {"icon":"","label":"Brave Origin","when":"blob-pkg-present brave-origin-bin","action":"blob-launch-floating 'blob-remove-browser brave-origin'"},
|
||||||
|
"remove.browser.firefox": {"icon":"","label":"Firefox","when":"blob-pkg-present firefox","action":"blob-launch-floating 'blob-remove-browser firefox'"},
|
||||||
|
"remove.browser.zen": {"icon":"","label":"Zen","when":"blob-pkg-present zen-browser-bin","action":"blob-launch-floating 'blob-remove-browser zen'"},
|
||||||
|
"remove.gaming.steam": {"icon":"","label":"Steam","when":"blob-pkg-present steam","action":"blob-launch-floating blob-remove-gaming-steam"},
|
||||||
|
"remove.gaming.retroarch": {"icon":"","label":"RetroArch","when":"blob-pkg-present retroarch","action":"blob-launch-floating blob-remove-gaming-retroarch"},
|
||||||
|
"remove.gaming.minecraft": {"icon":"","label":"Minecraft","when":"blob-pkg-present minecraft-launcher","action":"blob-launch-floating blob-remove-gaming-minecraft"},
|
||||||
|
"remove.gaming.geforce-now": {"icon":"","label":"NVIDIA GeForce NOW","when":"flatpak info com.nvidia.geforcenow","action":"blob-launch-floating blob-remove-gaming-geforce-now"},
|
||||||
|
"remove.gaming.xbox-cloud": {"icon":"","label":"Xbox Cloud Gaming","when":"[[ -f \"$HOME/.local/share/applications/Xbox Cloud Gaming.desktop\" ]]","action":"blob-launch-floating blob-remove-gaming-xbox-cloud"},
|
||||||
|
"remove.gaming.xbox-controllers": {"icon":"","label":"Xbox Controllers ()","when":"blob-pkg-present xpadneo-dkms","action":"blob-launch-floating blob-remove-gaming-xbox-controllers"},
|
||||||
|
"remove.gaming.battlenet": {"icon":"","label":"Battle.net","when":"[[ -d $HOME/Games/battlenet ]]","action":"blob-launch-floating blob-remove-gaming-battlenet"},
|
||||||
|
"remove.gaming.lutris": {"icon":"","label":"Lutris","when":"blob-pkg-present lutris","action":"blob-launch-floating blob-remove-gaming-lutris"},
|
||||||
|
"remove.gaming.heroic": {"icon":"","label":"Heroic (Epic Games)","when":"blob-pkg-present heroic-games-launcher-bin","action":"blob-launch-floating blob-remove-gaming-heroic"},
|
||||||
|
"remove.development.rails": {"icon":"","label":"Ruby on Rails","when":"[[ -d $HOME/.local/share/mise/installs/ruby ]]","action":"blob-launch-floating 'blob-remove-dev-env ruby'"},
|
||||||
|
"remove.development.javascript": {"icon":"","label":"JavaScript","title":"Remove"},
|
||||||
|
"remove.development.go": {"icon":"","label":"Go","when":"[[ -d $HOME/.local/share/mise/installs/go ]]","action":"blob-launch-floating 'blob-remove-dev-env go'"},
|
||||||
|
"remove.development.php": {"icon":"","label":"PHP","title":"Remove"},
|
||||||
|
"remove.development.python": {"icon":"","label":"Python","when":"[[ -d $HOME/.local/share/mise/installs/python ]]","action":"blob-launch-floating 'blob-remove-dev-env python'"},
|
||||||
|
"remove.development.elixir": {"icon":"","label":"Elixir","title":"Remove"},
|
||||||
|
"remove.development.zig": {"icon":"","label":"Zig","when":"[[ -d $HOME/.local/share/mise/installs/zig ]]","action":"blob-launch-floating 'blob-remove-dev-env zig'"},
|
||||||
|
"remove.development.rust": {"icon":"","label":"Rust","when":"[[ -d $HOME/.rustup ]]","action":"blob-launch-floating 'blob-remove-dev-env rust'"},
|
||||||
|
"remove.development.java": {"icon":"","label":"Java","when":"[[ -d $HOME/.local/share/mise/installs/java ]]","action":"blob-launch-floating 'blob-remove-dev-env java'"},
|
||||||
|
"remove.development.dotnet": {"icon":"","label":".NET","when":"[[ -d $HOME/.local/share/mise/installs/dotnet ]]","action":"blob-launch-floating 'blob-remove-dev-env dotnet'"},
|
||||||
|
"remove.development.ocaml": {"icon":"","label":"OCaml","when":"[[ -d $HOME/.opam ]]","action":"blob-launch-floating 'blob-remove-dev-env ocaml'"},
|
||||||
|
"remove.development.clojure": {"icon":"","label":"Clojure","when":"[[ -d $HOME/.local/share/mise/installs/clojure ]]","action":"blob-launch-floating 'blob-remove-dev-env clojure'"},
|
||||||
|
"remove.development.scala": {"icon":"","label":"Scala","when":"[[ -d $HOME/.local/share/mise/installs/scala ]]","action":"blob-launch-floating 'blob-remove-dev-env scala'"},
|
||||||
|
"remove.development.javascript.node": {"icon":"","label":"Node.js","when":"[[ -d $HOME/.local/share/mise/installs/node ]]","action":"blob-launch-floating 'blob-remove-dev-env node'"},
|
||||||
|
"remove.development.javascript.bun": {"icon":"","label":"Bun","when":"[[ -d $HOME/.local/share/mise/installs/bun ]]","action":"blob-launch-floating 'blob-remove-dev-env bun'"},
|
||||||
|
"remove.development.javascript.deno": {"icon":"","label":"Deno","when":"[[ -d $HOME/.local/share/mise/installs/deno ]]","action":"blob-launch-floating 'blob-remove-dev-env deno'"},
|
||||||
|
"remove.development.php.php": {"icon":"","label":"PHP","when":"blob-pkg-present php","action":"blob-launch-floating 'blob-remove-dev-env php'"},
|
||||||
|
"remove.development.php.laravel": {"icon":"","label":"Laravel","when":"[[ -x $HOME/.config/composer/vendor/bin/laravel ]]","action":"blob-launch-floating 'blob-remove-dev-env laravel'"},
|
||||||
|
"remove.development.php.symfony": {"icon":"","label":"Symfony","when":"blob-pkg-present symfony-cli","action":"blob-launch-floating 'blob-remove-dev-env symfony'"},
|
||||||
|
"remove.development.elixir.elixir": {"icon":"","label":"Elixir","when":"[[ -d $HOME/.local/share/mise/installs/elixir ]]","action":"blob-launch-floating 'blob-remove-dev-env elixir'"},
|
||||||
|
"remove.development.elixir.phoenix": {"icon":"","label":"Phoenix","when":"[[ -d $HOME/.local/share/mise/installs/elixir ]]","action":"blob-launch-floating 'blob-remove-dev-env phoenix'"},
|
||||||
"update": {"icon":"","label":"Update","aliases":["restart","refresh"]},
|
"update": {"icon":"","label":"Update","aliases":["restart","refresh"]},
|
||||||
"about": {"icon":"","label":"About","action":"blob-launch-about"},
|
"about": {"icon":"","label":"About","action":"blob-launch-about"},
|
||||||
"system": {"icon":"","label":"System","aliases":["power-menu"]},
|
"system": {"icon":"","label":"System","aliases":["power-menu"]},
|
||||||
|
|||||||
+59
-1
@@ -91,6 +91,13 @@ of the `blob` listing.
|
|||||||
| `blob-capture-webcam-list` | List webcam devices that support video capture (hidden) | - |
|
| `blob-capture-webcam-list` | List webcam devices that support video capture (hidden) | - |
|
||||||
| `blob-capture-webcam-resize` | Resize the active webcam recording overlay | <smaller\|larger\|reset\|small\|medium\|large> |
|
| `blob-capture-webcam-resize` | Resize the active webcam recording overlay | <smaller\|larger\|reset\|small\|medium\|large> |
|
||||||
|
|
||||||
|
## chromium
|
||||||
|
|
||||||
|
| Command | Does | Arguments |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `blob-chromium-copy-url-host` | Native messaging host: copy a Chromium tab URL to the clipboard (hidden) | - |
|
||||||
|
| `blob-chromium-ytdlp-host` | Native messaging host: download the URL sent by the yt-dlp Chromium extension (hidden) | - |
|
||||||
|
|
||||||
## clipboard
|
## clipboard
|
||||||
|
|
||||||
| Command | Does | Arguments |
|
| Command | Does | Arguments |
|
||||||
@@ -163,6 +170,13 @@ of the `blob` listing.
|
|||||||
| `blob-font-list` | List available monospace fonts | - |
|
| `blob-font-list` | List available monospace fonts | - |
|
||||||
| `blob-font-set` | Set the system monospace font | <font-name> |
|
| `blob-font-set` | Set the system monospace font | <font-name> |
|
||||||
|
|
||||||
|
## games
|
||||||
|
|
||||||
|
| Command | Does | Arguments |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `blob-games-retro-cores` | List installed RetroArch core names | - |
|
||||||
|
| `blob-games-retro-install` | Create a desktop launcher for a RetroArch game | [core path-to-game] |
|
||||||
|
|
||||||
## git
|
## git
|
||||||
|
|
||||||
| Command | Does | Arguments |
|
| Command | Does | Arguments |
|
||||||
@@ -235,7 +249,21 @@ of the `blob` listing.
|
|||||||
|
|
||||||
| Command | Does | Arguments |
|
| Command | Does | Arguments |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
| `blob-install-browser` | Install a supported browser | <chrome\|brave\|brave-origin\|edge\|firefox\|zen> |
|
||||||
|
| `blob-install-chromium-copy-url` | Install the native messaging host for the Copy URL Chromium extension | - |
|
||||||
|
| `blob-install-chromium-ytdlp` | Install the native messaging host for the yt-dlp Chromium extension | - |
|
||||||
|
| `blob-install-dev-env` | Install a supported development environment | <ruby\|node\|bun\|deno\|go\|laravel\|symfony\|php\|python\|elixir\|phoenix\|rust\|java\|zig\|ocaml\|dotnet\|clojure\|scala> |
|
||||||
|
| `blob-install-docker-dbs` | Install one of the supported databases in a Docker container with the suitable development options. | - |
|
||||||
| `blob-install-font` | Install a Nerd Font package and switch the system to it | <display-name> <package> <family> |
|
| `blob-install-font` | Install a Nerd Font package and switch the system to it | <display-name> <package> <family> |
|
||||||
|
| `blob-install-gaming-battlenet` | Install Battle.net standalone via umu-launcher + GE-Proton (no Steam, no Lutris, no Heroic). | - |
|
||||||
|
| `blob-install-gaming-geforce-now` | Install and launch Geforce Now. | - |
|
||||||
|
| `blob-install-gaming-gpu-lib32` | Install lib32 graphics drivers (Vulkan + NVIDIA) for any detected GPUs. | - |
|
||||||
|
| `blob-install-gaming-heroic` | Install Heroic Games Launcher (Epic, GOG, Amazon Prime Gaming) with graphics drivers. | - |
|
||||||
|
| `blob-install-gaming-lutris` | Install Lutris with Wine + DXVK for running Windows games (Battle.net, EA, Ubisoft Connect, etc.) | - |
|
||||||
|
| `blob-install-gaming-retroarch` | Install RetroArch with the full libretro core set plus FBNeo and a ~/Games ROM directory. | - |
|
||||||
|
| `blob-install-gaming-steam` | Install Steam and graphics drivers selected for this system | - |
|
||||||
|
| `blob-install-gaming-xbox-cloud` | Install Xbox Cloud Gaming as a web app and launch it. | - |
|
||||||
|
| `blob-install-gaming-xbox-controllers` | Install support for using Xbox controllers with Steam/RetroArch/etc. | - |
|
||||||
| `blob-install-launch` | Install a packaged app and launch it once it finishes | <display-name> <packages> <desktop-id> |
|
| `blob-install-launch` | Install a packaged app and launch it once it finishes | <display-name> <packages> <desktop-id> |
|
||||||
|
|
||||||
## launch
|
## launch
|
||||||
@@ -243,6 +271,7 @@ of the `blob` listing.
|
|||||||
| Command | Does | Arguments |
|
| Command | Does | Arguments |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `blob-launch-about` | Launch the fastfetch TUI that gives information about the current system. | - |
|
| `blob-launch-about` | Launch the fastfetch TUI that gives information about the current system. | - |
|
||||||
|
| `blob-launch-battlenet` | Launch the installed Battle.net client via umu-launcher + GE-Proton. | [--with-mangohud] |
|
||||||
| `blob-launch-browser` | Launch the default browser as determined by xdg-settings. | [url] |
|
| `blob-launch-browser` | Launch the default browser as determined by xdg-settings. | [url] |
|
||||||
| `blob-launch-config-editor` | Open a config file in the user's editor and surface a toast | <path> |
|
| `blob-launch-config-editor` | Open a config file in the user's editor and surface a toast | <path> |
|
||||||
| `blob-launch-docker-tui` | Open the Docker TUI (lazydocker) with access to the Docker daemon (hidden) | - |
|
| `blob-launch-docker-tui` | Open the Docker TUI (lazydocker) with access to the Docker daemon (hidden) | - |
|
||||||
@@ -261,9 +290,11 @@ of the `blob` listing.
|
|||||||
| `blob-launch-floating` | Launch a floating terminal with the Blob presentation wrapper | <command> |
|
| `blob-launch-floating` | Launch a floating terminal with the Blob presentation wrapper | <command> |
|
||||||
| `blob-launch-or-focus` | Launch an app or focus an existing window matching a pattern | <window-pattern> <launch-command> |
|
| `blob-launch-or-focus` | Launch an app or focus an existing window matching a pattern | <window-pattern> <launch-command> |
|
||||||
| `blob-launch-or-focus-tui` | Launch a TUI or focus an existing terminal window for it | [--app-id=<app-id>] <command> [args...] |
|
| `blob-launch-or-focus-tui` | Launch a TUI or focus an existing terminal window for it | [--app-id=<app-id>] <command> [args...] |
|
||||||
|
| `blob-launch-or-focus-webapp` | Launch or focus on a given web app identified by the window-pattern. | <window-pattern> <url-and-flags...> |
|
||||||
| `blob-launch-screensaver` | Launch the Blob screensaver in the default terminal on the system with the correct font configuration. | - |
|
| `blob-launch-screensaver` | Launch the Blob screensaver in the default terminal on the system with the correct font configuration. | - |
|
||||||
| `blob-launch-shell` | Launch the Blob shell with its log kept in the journal (hidden) | - |
|
| `blob-launch-shell` | Launch the Blob shell with its log kept in the journal (hidden) | - |
|
||||||
| `blob-launch-tui` | Launch a TUI command in the default terminal with Blob styling | [--app-id=<app-id>] <command> [args...] |
|
| `blob-launch-tui` | Launch a TUI command in the default terminal with Blob styling | [--app-id=<app-id>] <command> [args...] |
|
||||||
|
| `blob-launch-webapp` | Launch a URL as a web app in the default supported browser | <url> |
|
||||||
|
|
||||||
## menu
|
## menu
|
||||||
|
|
||||||
@@ -321,6 +352,7 @@ of the `blob` listing.
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `blob-pkg-add` | Install Arch packages if they are missing | <packages...> |
|
| `blob-pkg-add` | Install Arch packages if they are missing | <packages...> |
|
||||||
| `blob-pkg-aur` | Returns true if the AUR is up and available. | - |
|
| `blob-pkg-aur` | Returns true if the AUR is up and available. | - |
|
||||||
|
| `blob-pkg-aur-add` | Add the named packages to the system from the AUR if they're missing. Returns false if it couldn't be done. | <packages...> |
|
||||||
| `blob-pkg-aur-install` | Show a fuzzy-finder TUI for picking new AUR packages to install. | - |
|
| `blob-pkg-aur-install` | Show a fuzzy-finder TUI for picking new AUR packages to install. | - |
|
||||||
| `blob-pkg-drop` | Remove all the named packages from the system if they're installed (otherwise ignore). | <packages...> |
|
| `blob-pkg-drop` | Remove all the named packages from the system if they're installed (otherwise ignore). | <packages...> |
|
||||||
| `blob-pkg-install` | Show a fuzzy-finder TUI for picking new Arch and OPR packages to install. | - |
|
| `blob-pkg-install` | Show a fuzzy-finder TUI for picking new Arch and OPR packages to install. | - |
|
||||||
@@ -384,6 +416,17 @@ of the `blob` listing.
|
|||||||
|
|
||||||
| Command | Does | Arguments |
|
| Command | Does | Arguments |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
| `blob-remove-browser` | Remove a supported browser and clean up Blob browser defaults | <chrome\|brave\|brave-origin\|edge\|firefox\|zen> |
|
||||||
|
| `blob-remove-dev-env` | Remove a development environment that was previously installed via blob-install-dev-env. | <ruby\|node\|bun\|deno\|go\|php\|laravel\|symfony\|python\|elixir\|phoenix\|zig\|rust\|java\|dotnet\|ocaml\|clojure\|scala> |
|
||||||
|
| `blob-remove-gaming-battlenet` | Remove Battle.net, its Proton prefix, installed games, and desktop entry. | - |
|
||||||
|
| `blob-remove-gaming-geforce-now` | Remove the GeForce NOW Flatpak app and its data. | - |
|
||||||
|
| `blob-remove-gaming-heroic` | Remove Heroic Games Launcher and its game libraries, configs, and caches. | - |
|
||||||
|
| `blob-remove-gaming-lutris` | Remove Lutris, Wine, umu-launcher, and all their configs and caches. | - |
|
||||||
|
| `blob-remove-gaming-minecraft` | Remove the Minecraft launcher along with its worlds, mods, and caches. | - |
|
||||||
|
| `blob-remove-gaming-retroarch` | Remove RetroArch, all libretro cores, and its config/saves. Leaves ~/Games/roms and ~/Games/bios alone. | - |
|
||||||
|
| `blob-remove-gaming-steam` | Remove Steam and all of its game libraries, configs, and caches. | - |
|
||||||
|
| `blob-remove-gaming-xbox-cloud` | Remove the Xbox Cloud Gaming web app. | - |
|
||||||
|
| `blob-remove-gaming-xbox-controllers` | Remove the xpadneo Xbox controller driver and undo its module/blacklist config. | - |
|
||||||
| `blob-remove-security-fido2` | Remove FIDO2 authentication from sudo and polkit | - |
|
| `blob-remove-security-fido2` | Remove FIDO2 authentication from sudo and polkit | - |
|
||||||
| `blob-remove-security-sudoless-docker` | Disable sudoless Docker by removing your user from the docker group | - |
|
| `blob-remove-security-sudoless-docker` | Disable sudoless Docker by removing your user from the docker group | - |
|
||||||
|
|
||||||
@@ -554,6 +597,21 @@ of the `blob` listing.
|
|||||||
| `blob-weather-location` | Show or set the location used for weather reports | - |
|
| `blob-weather-location` | Show or set the location used for weather reports | - |
|
||||||
| `blob-weather-status` | Returns a formatted weather status string with temperature and wind speed. | - |
|
| `blob-weather-status` | Returns a formatted weather status string with temperature and wind speed. | - |
|
||||||
|
|
||||||
|
## webapp
|
||||||
|
|
||||||
|
| Command | Does | Arguments |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `blob-webapp-handler-hey` | Open HEY webmail and translate mailto links | [url] |
|
||||||
|
| `blob-webapp-handler-zoom` | Open Zoom web meetings from browser protocol links | [url] |
|
||||||
|
| `blob-webapp-install` | Create a desktop launcher for a web app | [name url icon-url-or-name [custom-exec] [mime-types]] |
|
||||||
|
| `blob-webapp-remove` | Remove a web app desktop launcher | [name] |
|
||||||
|
|
||||||
|
## windows
|
||||||
|
|
||||||
|
| Command | Does | Arguments |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `blob-windows-vm` | Install, launch, stop, inspect, or remove the Windows VM | <install\|remove\|launch\|stop\|status> [options] |
|
||||||
|
|
||||||
## Totals
|
## Totals
|
||||||
|
|
||||||
252 commands.
|
290 commands.
|
||||||
|
|||||||
+37
-5
@@ -8,12 +8,12 @@ menu fork carried is gone.
|
|||||||
|
|
||||||
## Trimmed from upstream
|
## Trimmed from upstream
|
||||||
|
|
||||||
Upstream ships 338 rows. This tree has 178. What came out:
|
Upstream ships 338 rows. This tree has 260. What came out:
|
||||||
|
|
||||||
| Section | Dropped | Why |
|
| Section | Dropped | Why |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `install.*` | 84 of 90 | the software catalogue: browsers, gaming, dev languages, AI tools, services. Kept: Package, AUR, TUI, and the Style subtree (theme, background, font) |
|
| `install.*` | 35 of 91 | AI tools, editors, terminals, services, web apps, preinstalls. Browsers, gaming, dev languages and the Windows VM were restored on request |
|
||||||
| `remove.*` | 56 of 59 | same catalogue in reverse. Kept: Package, TUI, Theme |
|
| `remove.*` | 16 of 60 | the same set in reverse, plus security and dictation removal |
|
||||||
| `setup.default.agent.*` | 14 | AI agent defaults |
|
| `setup.default.agent.*` | 14 | AI agent defaults |
|
||||||
| `update.channel.*` | 5 | release channels for a package that will not exist |
|
| `update.channel.*` | 5 | release channels for a package that will not exist |
|
||||||
| `learn.*` | 4 | Omarchy's manual and Discord, plus tmux and herdr keybindings |
|
| `learn.*` | 4 | Omarchy's manual and Discord, plus tmux and herdr keybindings |
|
||||||
@@ -27,8 +27,8 @@ which also keeps them working whichever way the web app question lands.
|
|||||||
|
|
||||||
## Commands the menu still needs
|
## Commands the menu still needs
|
||||||
|
|
||||||
The tree references 95 commands. All of them are now in `bin/`. What follows is
|
Every command the tree references is in `bin/`. What follows is the record of
|
||||||
kept as the record of what Phase 6 had to fill:
|
what Phase 6 had to fill:
|
||||||
|
|
||||||
- **audio**: `blob-audio-restart`
|
- **audio**: `blob-audio-restart`
|
||||||
- **bar**: `blob-bar`
|
- **bar**: `blob-bar`
|
||||||
@@ -78,3 +78,35 @@ Every row in this tree resolves to a command in `bin/`. The only unbacked
|
|||||||
`hypr/default/helpers.lua` and reachable only from a binding that uses
|
`hypr/default/helpers.lua` and reachable only from a binding that uses
|
||||||
`{ webapp = ... }`. No such binding ships. They stay as the hook for whichever
|
`{ webapp = ... }`. No such binding ships. They stay as the hook for whichever
|
||||||
way the web app question lands.
|
way the web app question lands.
|
||||||
|
|
||||||
|
## Restored install and remove subtrees
|
||||||
|
|
||||||
|
Browsers, gaming, dev languages and the Windows VM came back after the first
|
||||||
|
pass cut them, which took 82 rows and 38 commands with them.
|
||||||
|
|
||||||
|
| Subtree | Rows | Covers |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Browsers | 14 | Chrome, Edge, Brave, Brave Origin, Firefox, Zen, install and remove |
|
||||||
|
| Gaming | 20 | Steam, RetroArch, Minecraft, Lutris, Heroic, GeForce NOW, Xbox Cloud, Xbox controllers, Battle.net |
|
||||||
|
| Development | 44 | Ruby on Rails, JavaScript (Node/Bun/Deno), Go, PHP (+ Laravel, Symfony), Python, Elixir (+ Phoenix), Zig, Rust, Java, .NET, OCaml, Clojure, Scala |
|
||||||
|
| Windows VM | 2 | install and remove |
|
||||||
|
|
||||||
|
### This settled the web app question
|
||||||
|
|
||||||
|
Xbox Cloud Gaming and GeForce NOW are web apps, not packages. Restoring gaming
|
||||||
|
therefore pulled in `blob-webapp-install`, `blob-webapp-remove`,
|
||||||
|
`blob-launch-webapp` and `blob-launch-or-focus-webapp`, and those fall back to
|
||||||
|
`chromium.desktop` for any browser that is not Chrome, Brave, Edge, Opera,
|
||||||
|
Vivaldi or Helium. Zen is Firefox-based.
|
||||||
|
|
||||||
|
So chromium stays, as the web app runtime. The `webapp` helper in
|
||||||
|
`hypr/default/helpers.lua` is live again rather than a dangling hook, though no
|
||||||
|
binding ships that uses it. The `install.webapp` and `remove.webapp` menu rows
|
||||||
|
are still out; the machinery behind them now works, so adding them back is a
|
||||||
|
two-line change if you want to create web app launchers from the menu.
|
||||||
|
|
||||||
|
### Still out
|
||||||
|
|
||||||
|
AI tools, editors, terminals, services (1Password, Dropbox, Spotify, Signal,
|
||||||
|
Tailscale, NordVPN, ONCE, Bitwarden), preinstalls, dictation, and the security
|
||||||
|
removal rows. Say the word on any of them.
|
||||||
|
|||||||
Reference in New Issue
Block a user