Add the installer, uninstaller, and generated docs
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
<div align="center">
|
||||
<h1>Blob</h1>
|
||||
<p>A self-contained Wayland desktop for Arch Linux.</p>
|
||||
|
||||
<img src="https://img.shields.io/badge/Arch_Linux-1793D1?style=for-the-badge&logo=arch-linux&logoColor=white" alt="Arch Linux" />
|
||||
<img src="https://img.shields.io/badge/Hyprland-00A86B?style=for-the-badge&logo=hyprland&logoColor=white" alt="Hyprland" />
|
||||
<img src="https://img.shields.io/badge/Quickshell-2A2A2A?style=for-the-badge&logo=qt&logoColor=white" alt="Quickshell" />
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
This started as a layer of overrides on top of Omarchy and is now its own
|
||||
desktop. It keeps the look, layout, and base functionality that were worth
|
||||
keeping, and owns every line that runs. There is no upstream package to track
|
||||
and nothing to re-apply after somebody else's release.
|
||||
|
||||
## What it is
|
||||
|
||||
- **[Hyprland](https://hyprland.org/)** as the compositor, configured in Lua.
|
||||
- **[Quickshell](https://quickshell.org/)** as the desktop: `blob-shell` is one
|
||||
process hosting the bar, both menus, the panels, notifications, the lock
|
||||
screen, and the OSD as plugins.
|
||||
- **A `blob-*` CLI** of 251 commands, dispatched by `blob`.
|
||||
- **24 themes** with a palette pipeline that retints the terminal, editor,
|
||||
browser, shell, and lock screen from one `colors.toml`.
|
||||
|
||||
## Layout
|
||||
|
||||
| Path | Contents |
|
||||
| --- | --- |
|
||||
| `bin/` | the `blob-<area>-<verb>` commands |
|
||||
| `shell/` | the Quickshell desktop |
|
||||
| `hypr/` | personal Hyprland config, deployed to `~/.config/hypr` |
|
||||
| `default/` | the shipped defaults the commands and shell resolve: `hypr/` Lua layer, `themed/` templates, `blob/` menu tree |
|
||||
| `themes/` | colour themes |
|
||||
| `config/` | shipped app configs |
|
||||
| `shell.json` | bar layout and idle timings |
|
||||
| `hooks/` | event hooks, such as retinting on theme change |
|
||||
| `branding/` | icon, ASCII art, boot splash |
|
||||
| `wallpapers/` | 103 wallpapers, previewed in [the gallery](wallpaper-gallery/index.md) |
|
||||
| `session/` | wayland session entry and uwsm environment |
|
||||
| `docs/` | everything below |
|
||||
|
||||
## Docs
|
||||
|
||||
| Doc | Covers |
|
||||
| --- | --- |
|
||||
| [commands.md](docs/commands.md) | every command, generated from its own metadata |
|
||||
| [keybinds.md](docs/keybinds.md) | every binding, generated from the Lua |
|
||||
| [shell.md](docs/shell.md) | shell layout, bar config, plugin model |
|
||||
| [themes.md](docs/themes.md) | the palette pipeline and how to add a theme |
|
||||
| [widgets.md](docs/widgets.md) | the ported GTK widgets and where they went |
|
||||
| [menu.md](docs/menu.md) | the menu tree and what was trimmed |
|
||||
| [upstream.md](docs/upstream.md) | the fork base, for diffing later |
|
||||
|
||||
## Install
|
||||
|
||||
`BLOB_PATH` is a symlink to this checkout, so `bin/`, `shell/`, `themes/` and
|
||||
`default/` are always the working tree and edits are live. Only the handful of
|
||||
files that must sit under `~/.config` get copied.
|
||||
|
||||
```bash
|
||||
./install.sh # link and deploy
|
||||
./install.sh --check # report what would change, write nothing
|
||||
./install.sh --force # overwrite files with local changes
|
||||
```
|
||||
|
||||
Then log out and pick the Blob session.
|
||||
|
||||
To back out, `./uninstall.sh` removes the session entry, the config, and the
|
||||
symlink, restoring any `.bak` the installer made. The checkout, the wallpapers,
|
||||
and the themes stay where they are.
|
||||
|
||||
## Regenerating docs
|
||||
|
||||
```bash
|
||||
blob-docs-commands
|
||||
blob-docs-keybinds
|
||||
```
|
||||
|
||||
## Keys worth knowing
|
||||
|
||||
| Keys | Action |
|
||||
| --- | --- |
|
||||
| `Super + Space` | Apps menu |
|
||||
| `Super + Alt + Space` | Root menu |
|
||||
| `Super + Ctrl + Q` | Quick settings |
|
||||
| `Super + Ctrl + M` | System monitor |
|
||||
| `Super + Alt + W` | Wallpaper picker |
|
||||
| `Super + Ctrl + L` | Lock |
|
||||
|
||||
The rest are in [keybinds.md](docs/keybinds.md), or run `blob menu keybindings`.
|
||||
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
|
||||
# blob:summary=Regenerate docs/commands.md from each command's own metadata
|
||||
# blob:hidden=true
|
||||
|
||||
set -e
|
||||
|
||||
bin_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_dir="$(dirname "$bin_dir")"
|
||||
output="$repo_dir/docs/commands.md"
|
||||
|
||||
metadata() {
|
||||
sed -n "s/^# blob:$2=//p" "$1" | head -1
|
||||
}
|
||||
|
||||
area_of() {
|
||||
local name="${1#blob-}"
|
||||
printf '%s\n' "${name%%-*}"
|
||||
}
|
||||
|
||||
{
|
||||
echo "# Commands"
|
||||
echo
|
||||
echo "Generated by \`blob-docs-commands\` from the \`# blob:summary=\` line in each"
|
||||
echo "file. Do not edit by hand."
|
||||
echo
|
||||
echo "Commands marked hidden are plumbing other commands call, and are left out"
|
||||
echo "of the \`blob\` listing."
|
||||
echo
|
||||
|
||||
current_area=""
|
||||
for file in "$bin_dir"/blob-*; do
|
||||
[[ -f $file ]] || continue
|
||||
name="$(basename "$file")"
|
||||
area="$(area_of "$name")"
|
||||
|
||||
if [[ $area != "$current_area" ]]; then
|
||||
echo
|
||||
echo "## $area"
|
||||
echo
|
||||
echo "| Command | Does | Arguments |"
|
||||
echo "| --- | --- | --- |"
|
||||
current_area="$area"
|
||||
fi
|
||||
|
||||
summary="$(metadata "$file" summary)"
|
||||
args="$(metadata "$file" args)"
|
||||
[[ -n $summary ]] || summary="-"
|
||||
[[ -n $args ]] || args="-"
|
||||
if grep -q '^# blob:hidden=true' "$file"; then
|
||||
summary="$summary (hidden)"
|
||||
fi
|
||||
printf '| `%s` | %s | %s |\n' "$name" "$summary" "${args//|/\\|}"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "## Totals"
|
||||
echo
|
||||
printf '%s commands.\n' "$(ls "$bin_dir"/blob-* | wc -l)"
|
||||
} > "$output"
|
||||
|
||||
echo "Wrote $output"
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
|
||||
# blob:summary=Regenerate docs/keybinds.md from the Hyprland Lua bindings
|
||||
# blob:hidden=true
|
||||
|
||||
set -e
|
||||
|
||||
bin_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_dir="$(dirname "$bin_dir")"
|
||||
output="$repo_dir/docs/keybinds.md"
|
||||
|
||||
# o.bind("KEYS", "Description", ...) and o.bind_toggle("KEYS", "Description", ...)
|
||||
# are the only two forms the Lua layer uses to declare a binding.
|
||||
collect() {
|
||||
local file="$1"
|
||||
grep -ohE 'o\.bind(_toggle)?\(\s*"[^"]+",\s*"[^"]+"' "$file" 2>/dev/null |
|
||||
sed -E 's/o\.bind(_toggle)?\(\s*"([^"]+)",\s*"([^"]+)"/\2\t\3/'
|
||||
}
|
||||
|
||||
section() {
|
||||
local title="$1"
|
||||
shift
|
||||
local rows
|
||||
rows="$(for file in "$@"; do collect "$file"; done | sort -u -t$'\t' -k1,1)"
|
||||
[[ -n $rows ]] || return 0
|
||||
|
||||
echo
|
||||
echo "## $title"
|
||||
echo
|
||||
echo "| Keys | Action |"
|
||||
echo "| --- | --- |"
|
||||
printf '%s\n' "$rows" | while IFS=$'\t' read -r keys action; do
|
||||
printf '| `%s` | %s |\n' "$keys" "$action"
|
||||
done
|
||||
}
|
||||
|
||||
{
|
||||
echo "# Keybindings"
|
||||
echo
|
||||
echo "Generated by \`blob-docs-keybinds\` from the \`o.bind\` calls in the Hyprland"
|
||||
echo "Lua files. Do not edit by hand."
|
||||
echo
|
||||
echo "Personal overrides in \`hypr/bindings.lua\` win over the defaults, and"
|
||||
echo "\`hl.unbind\` in that file removes a default outright."
|
||||
|
||||
section "Personal" "$repo_dir/hypr/bindings.lua"
|
||||
section "Applications" "$repo_dir/default/hypr/bindings/applications.lua"
|
||||
section "Tiling" "$repo_dir/default/hypr/bindings/tiling.lua"
|
||||
section "Media" "$repo_dir/default/hypr/bindings/media.lua"
|
||||
section "Clipboard" "$repo_dir/default/hypr/bindings/clipboard.lua"
|
||||
section "Utilities" "$repo_dir/default/hypr/bindings/utilities.lua"
|
||||
|
||||
echo
|
||||
echo "## Unbound defaults"
|
||||
echo
|
||||
grep -ohE 'hl\.unbind\("[^"]+"\)' "$repo_dir/hypr/bindings.lua" 2>/dev/null |
|
||||
sed -E 's/hl\.unbind\("([^"]+)"\)/- `\1`/' || echo "None."
|
||||
} > "$output"
|
||||
|
||||
echo "Wrote $output"
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
|
||||
# blob:summary=Monitor sleep preparation and lock before suspend
|
||||
# blob:group=system
|
||||
# blob:hidden=true
|
||||
|
||||
consume_sleep_events() {
|
||||
local line sleep_lock
|
||||
sleep_lock="$BLOB_PATH/bin/blob-system-sleep"
|
||||
|
||||
while IFS= read -r line; do
|
||||
if [[ $line == *"boolean true"* ]]; then
|
||||
"$sleep_lock"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
monitor_sleep_events() {
|
||||
local monitor_fd monitor_pid status
|
||||
|
||||
coproc SLEEP_EVENTS {
|
||||
exec dbus-monitor --system \
|
||||
"type='signal',sender='org.freedesktop.login1',interface='org.freedesktop.login1.Manager',member='PrepareForSleep'"
|
||||
}
|
||||
monitor_fd=${SLEEP_EVENTS[0]}
|
||||
monitor_pid=$SLEEP_EVENTS_PID
|
||||
|
||||
cleanup_monitor() {
|
||||
kill "$monitor_pid" 2>/dev/null || true
|
||||
wait "$monitor_pid" 2>/dev/null || true
|
||||
}
|
||||
trap cleanup_monitor EXIT
|
||||
|
||||
consume_sleep_events <&"$monitor_fd"
|
||||
status=$?
|
||||
cleanup_monitor
|
||||
trap - EXIT
|
||||
|
||||
return "$status"
|
||||
}
|
||||
|
||||
case ${1:-} in
|
||||
--consume)
|
||||
consume_sleep_events
|
||||
exit 0
|
||||
;;
|
||||
--inhibited)
|
||||
monitor_sleep_events
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
sleep_monitor="$BLOB_PATH/bin/blob-system-sleep-monitor"
|
||||
|
||||
exec systemd-inhibit \
|
||||
--what=sleep \
|
||||
--mode=delay \
|
||||
--who=Blob \
|
||||
--why="Lock screen before suspend" \
|
||||
"$sleep_monitor" --inhibited
|
||||
@@ -0,0 +1,272 @@
|
||||
#? Config file for btop v.1.4.6
|
||||
|
||||
#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes.
|
||||
#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes"
|
||||
color_theme = "current"
|
||||
|
||||
#* If the theme set background should be shown, set to False if you want terminal background transparency.
|
||||
theme_background = true
|
||||
|
||||
#* Sets if 24-bit truecolor should be used, will convert 24-bit colors to 256 color (6x6x6 color cube) if false.
|
||||
truecolor = true
|
||||
|
||||
#* Set to true to force tty mode regardless if a real tty has been detected or not.
|
||||
#* Will force 16-color mode and TTY theme, set all graph symbols to "tty" and swap out other non tty friendly symbols.
|
||||
force_tty = false
|
||||
|
||||
#* Define presets for the layout of the boxes. Preset 0 is always all boxes shown with default settings. Max 9 presets.
|
||||
#* Format: "box_name:P:G,box_name:P:G" P=(0 or 1) for alternate positions, G=graph symbol to use for box.
|
||||
#* Use whitespace " " as separator between different presets.
|
||||
#* Example: "cpu:0:default,mem:0:tty,proc:1:default cpu:0:braille,proc:0:tty"
|
||||
presets = "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty"
|
||||
|
||||
#* Set to True to enable "h,j,k,l,g,G" keys for directional control in lists.
|
||||
#* Conflicting keys for h:"help" and k:"kill" is accessible while holding shift.
|
||||
vim_keys = true
|
||||
|
||||
#* Rounded corners on boxes, is ignored if TTY mode is ON.
|
||||
rounded_corners = true
|
||||
|
||||
#* Use terminal synchronized output sequences to reduce flickering on supported terminals.
|
||||
terminal_sync = true
|
||||
|
||||
#* Default symbols to use for graph creation, "braille", "block" or "tty".
|
||||
#* "braille" offers the highest resolution but might not be included in all fonts.
|
||||
#* "block" has half the resolution of braille but uses more common characters.
|
||||
#* "tty" uses only 3 different symbols but will work with most fonts and should work in a real TTY.
|
||||
#* Note that "tty" only has half the horizontal resolution of the other two, so will show a shorter historical view.
|
||||
graph_symbol = "braille"
|
||||
|
||||
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
|
||||
graph_symbol_cpu = "default"
|
||||
|
||||
# Graph symbol to use for graphs in gpu box, "default", "braille", "block" or "tty".
|
||||
graph_symbol_gpu = "default"
|
||||
|
||||
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
|
||||
graph_symbol_mem = "default"
|
||||
|
||||
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
|
||||
graph_symbol_net = "default"
|
||||
|
||||
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
|
||||
graph_symbol_proc = "default"
|
||||
|
||||
#* Manually set which boxes to show. Available values are "cpu mem net proc" and "gpu0" through "gpu5", separate values with whitespace.
|
||||
shown_boxes = "cpu mem net proc"
|
||||
|
||||
#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs.
|
||||
update_ms = 2000
|
||||
|
||||
#* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu direct",
|
||||
#* "cpu lazy" sorts top process over time (easier to follow), "cpu direct" updates top process directly.
|
||||
proc_sorting = "cpu lazy"
|
||||
|
||||
#* Reverse sorting order, True or False.
|
||||
proc_reversed = false
|
||||
|
||||
#* Show processes as a tree.
|
||||
proc_tree = false
|
||||
|
||||
#* Use the cpu graph colors in the process list.
|
||||
proc_colors = true
|
||||
|
||||
#* Use a darkening gradient in the process list.
|
||||
proc_gradient = true
|
||||
|
||||
#* If process cpu usage should be of the core it's running on or usage of the total available cpu power.
|
||||
proc_per_core = false
|
||||
|
||||
#* Show process memory as bytes instead of percent.
|
||||
proc_mem_bytes = true
|
||||
|
||||
#* Show cpu graph for each process.
|
||||
proc_cpu_graphs = true
|
||||
|
||||
#* Use /proc/[pid]/smaps for memory information in the process info box (very slow but more accurate)
|
||||
proc_info_smaps = false
|
||||
|
||||
#* Show proc box on left side of screen instead of right.
|
||||
proc_left = false
|
||||
|
||||
#* (Linux) Filter processes tied to the Linux kernel(similar behavior to htop).
|
||||
proc_filter_kernel = false
|
||||
|
||||
#* In tree-view, always accumulate child process resources in the parent process.
|
||||
proc_aggregate = false
|
||||
|
||||
#* Should cpu and memory usage display be preserved for dead processes when paused.
|
||||
keep_dead_proc_usage = false
|
||||
|
||||
#* Sets the CPU stat shown in upper half of the CPU graph, "total" is always available.
|
||||
#* Select from a list of detected attributes from the options menu.
|
||||
cpu_graph_upper = "Auto"
|
||||
|
||||
#* Sets the CPU stat shown in lower half of the CPU graph, "total" is always available.
|
||||
#* Select from a list of detected attributes from the options menu.
|
||||
cpu_graph_lower = "Auto"
|
||||
|
||||
#* If gpu info should be shown in the cpu box. Available values = "Auto", "On" and "Off".
|
||||
show_gpu_info = "Auto"
|
||||
|
||||
#* Toggles if the lower CPU graph should be inverted.
|
||||
cpu_invert_lower = true
|
||||
|
||||
#* Set to True to completely disable the lower CPU graph.
|
||||
cpu_single_graph = false
|
||||
|
||||
#* Show cpu box at bottom of screen instead of top.
|
||||
cpu_bottom = false
|
||||
|
||||
#* Shows the system uptime in the CPU box.
|
||||
show_uptime = true
|
||||
|
||||
#* Shows the CPU package current power consumption in watts. Requires running `make setcap` or `make setuid` or running with sudo.
|
||||
show_cpu_watts = true
|
||||
|
||||
#* Show cpu temperature.
|
||||
check_temp = true
|
||||
|
||||
#* Which sensor to use for cpu temperature, use options menu to select from list of available sensors.
|
||||
cpu_sensor = "Auto"
|
||||
|
||||
#* Show temperatures for cpu cores also if check_temp is True and sensors has been found.
|
||||
show_coretemp = true
|
||||
|
||||
#* Set a custom mapping between core and coretemp, can be needed on certain cpus to get correct temperature for correct core.
|
||||
#* Use lm-sensors or similar to see which cores are reporting temperatures on your machine.
|
||||
#* Format "x:y" x=core with wrong temp, y=core with correct temp, use space as separator between multiple entries.
|
||||
#* Example: "4:0 5:1 6:3"
|
||||
cpu_core_map = ""
|
||||
|
||||
#* Which temperature scale to use, available values: "celsius", "fahrenheit", "kelvin" and "rankine".
|
||||
temp_scale = "celsius"
|
||||
|
||||
#* Use base 10 for bits/bytes sizes, KB = 1000 instead of KiB = 1024.
|
||||
base_10_sizes = false
|
||||
|
||||
#* Show CPU frequency.
|
||||
show_cpu_freq = true
|
||||
|
||||
#* How to calculate CPU frequency, available values: "first", "range", "lowest", "highest" and "average".
|
||||
freq_mode = "first"
|
||||
|
||||
#* Draw a clock at top of screen, formatting according to strftime, empty string to disable.
|
||||
#* Special formatting: /host = hostname | /user = username | /uptime = system uptime
|
||||
clock_format = "%X"
|
||||
|
||||
#* Update main ui in background when menus are showing, set this to false if the menus is flickering too much for comfort.
|
||||
background_update = true
|
||||
|
||||
#* Custom cpu model name, empty string to disable.
|
||||
custom_cpu_name = ""
|
||||
|
||||
#* Optional filter for shown disks, should be full path of a mountpoint, separate multiple values with whitespace " ".
|
||||
#* Only disks matching the filter will be shown. Prepend exclude= to only show disks not matching the filter. Examples: disk_filter="/boot /home/user", disks_filter="exclude=/boot /home/user"
|
||||
disks_filter = ""
|
||||
|
||||
#* Show graphs instead of meters for memory values.
|
||||
mem_graphs = true
|
||||
|
||||
#* Show mem box below net box instead of above.
|
||||
mem_below_net = false
|
||||
|
||||
#* Count ZFS ARC in cached and available memory.
|
||||
zfs_arc_cached = true
|
||||
|
||||
#* If swap memory should be shown in memory box.
|
||||
show_swap = true
|
||||
|
||||
#* Show swap as a disk, ignores show_swap value above, inserts itself after first disk.
|
||||
swap_disk = true
|
||||
|
||||
#* If mem box should be split to also show disks info.
|
||||
show_disks = true
|
||||
|
||||
#* Filter out non physical disks. Set this to False to include network disks, RAM disks and similar.
|
||||
only_physical = true
|
||||
|
||||
#* Read disks list from /etc/fstab. This also disables only_physical.
|
||||
use_fstab = true
|
||||
|
||||
#* Setting this to True will hide all datasets, and only show ZFS pools. (IO stats will be calculated per-pool)
|
||||
zfs_hide_datasets = false
|
||||
|
||||
#* Set to true to show available disk space for privileged users.
|
||||
disk_free_priv = false
|
||||
|
||||
#* Toggles if io activity % (disk busy time) should be shown in regular disk usage view.
|
||||
show_io_stat = true
|
||||
|
||||
#* Toggles io mode for disks, showing big graphs for disk read/write speeds.
|
||||
io_mode = false
|
||||
|
||||
#* Set to True to show combined read/write io graphs in io mode.
|
||||
io_graph_combined = false
|
||||
|
||||
#* Set the top speed for the io graphs in MiB/s (100 by default), use format "mountpoint:speed" separate disks with whitespace " ".
|
||||
#* Example: "/mnt/media:100 /:20 /boot:1".
|
||||
io_graph_speeds = ""
|
||||
|
||||
#* Set fixed values for network graphs in Mebibits. Is only used if net_auto is also set to False.
|
||||
net_download = 100
|
||||
|
||||
net_upload = 100
|
||||
|
||||
#* Use network graphs auto rescaling mode, ignores any values set above and rescales down to 10 Kibibytes at the lowest.
|
||||
net_auto = true
|
||||
|
||||
#* Sync the auto scaling for download and upload to whichever currently has the highest scale.
|
||||
net_sync = true
|
||||
|
||||
#* Starts with the Network Interface specified here.
|
||||
net_iface = ""
|
||||
|
||||
#* "True" shows bitrates in base 10 (Kbps, Mbps). "False" shows bitrates in binary sizes (Kibps, Mibps, etc.). "Auto" uses base_10_sizes.
|
||||
base_10_bitrate = "Auto"
|
||||
|
||||
#* Show battery stats in top right if battery is present.
|
||||
show_battery = true
|
||||
|
||||
#* Which battery to use if multiple are present. "Auto" for auto detection.
|
||||
selected_battery = "Auto"
|
||||
|
||||
#* Show power stats of battery next to charge indicator.
|
||||
show_battery_watts = true
|
||||
|
||||
#* Set loglevel for "~/.local/state/btop.log" levels are: "ERROR" "WARNING" "INFO" "DEBUG".
|
||||
#* The level set includes all lower levels, i.e. "DEBUG" will show all logging info.
|
||||
log_level = "WARNING"
|
||||
|
||||
#* Automatically save current settings to config file on exit.
|
||||
save_config_on_exit = true
|
||||
|
||||
#* Measure PCIe throughput on NVIDIA cards, may impact performance on certain cards.
|
||||
nvml_measure_pcie_speeds = true
|
||||
|
||||
#* Measure PCIe throughput on AMD cards, may impact performance on certain cards.
|
||||
rsmi_measure_pcie_speeds = true
|
||||
|
||||
#* Horizontally mirror the GPU graph.
|
||||
gpu_mirror_graph = true
|
||||
|
||||
#* Set which GPU vendors to show. Available values are "nvidia amd intel"
|
||||
shown_gpus = "nvidia amd intel"
|
||||
|
||||
#* Custom gpu0 model name, empty string to disable.
|
||||
custom_gpu_name0 = ""
|
||||
|
||||
#* Custom gpu1 model name, empty string to disable.
|
||||
custom_gpu_name1 = ""
|
||||
|
||||
#* Custom gpu2 model name, empty string to disable.
|
||||
custom_gpu_name2 = ""
|
||||
|
||||
#* Custom gpu3 model name, empty string to disable.
|
||||
custom_gpu_name3 = ""
|
||||
|
||||
#* Custom gpu4 model name, empty string to disable.
|
||||
custom_gpu_name4 = ""
|
||||
|
||||
#* Custom gpu5 model name, empty string to disable.
|
||||
custom_gpu_name5 = ""
|
||||
@@ -0,0 +1,26 @@
|
||||
[main]
|
||||
include=~/.local/state/blob/current/theme/foot.ini
|
||||
term=xterm-256color
|
||||
font=JetBrainsMono Nerd Font:size=9
|
||||
pad=14x14
|
||||
initial-window-mode=windowed
|
||||
workers=0
|
||||
|
||||
[scrollback]
|
||||
lines=10000
|
||||
multiplier=7.0
|
||||
|
||||
[cursor]
|
||||
style=block
|
||||
blink=no
|
||||
|
||||
[key-bindings]
|
||||
clipboard-copy=Control+Insert Control+Shift+c XF86Copy
|
||||
primary-paste=none
|
||||
clipboard-paste=Shift+Insert Control+Shift+v XF86Paste
|
||||
|
||||
[text-bindings]
|
||||
# Send Shift+Return as CSI-u so TUIs can distinguish it from Return.
|
||||
\x1b[13;2u=Shift+Return
|
||||
# Send Alt+Shift+Return as CSI-u so tmux can match M-S-Enter.
|
||||
\x1b[13;4u=Mod1+Shift+Return
|
||||
@@ -0,0 +1,28 @@
|
||||
# See https://git-scm.com/docs/git-config
|
||||
|
||||
[alias]
|
||||
co = checkout
|
||||
br = branch
|
||||
ci = commit
|
||||
st = status
|
||||
[init]
|
||||
defaultBranch = master
|
||||
[pull]
|
||||
rebase = true # Rebase (instead of merge) on pull
|
||||
[push]
|
||||
autoSetupRemote = true # Automatically set upstream branch on push
|
||||
[diff]
|
||||
algorithm = histogram # Clearer diffs on moved/edited lines
|
||||
colorMoved = plain # Highlight moved blocks in diffs
|
||||
mnemonicPrefix = true # More intuitive refs in diff output
|
||||
[commit]
|
||||
verbose = true # Include diff comment in commit message template
|
||||
[column]
|
||||
ui = auto # Output in columns when possible
|
||||
[branch]
|
||||
sort = -committerdate # Sort branches by most recent commit first
|
||||
[tag]
|
||||
sort = -version:refname # Sort version numbers as you would expect
|
||||
[rerere]
|
||||
enabled = true # Record and reuse conflict resolutions
|
||||
autoupdate = true # Apply stored conflict resolutions automatically
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Extra autostart processes.
|
||||
-- o.launch_on_start("my-service")
|
||||
@@ -0,0 +1,29 @@
|
||||
-- Keep only your personal keybinding overrides here. Add new bindings or
|
||||
-- unbind defaults before replacing them.
|
||||
|
||||
-- See current bindings and descriptions:
|
||||
-- blob menu keybindings --print
|
||||
|
||||
-- To disable every Blob default binding, set this in
|
||||
-- ~/.config/hypr/hyprland.lua before require("default.hypr.blob"), then add
|
||||
-- only the bindings you want below:
|
||||
-- blob_default_bindings = false
|
||||
|
||||
-- To disable all preinstalled app/webapp bindings, set:
|
||||
-- blob_preinstalled_bindings = false
|
||||
|
||||
-- Add a new binding.
|
||||
-- o.bind("SUPER + SHIFT + R", "SSH", "alacritty -e ssh your-server")
|
||||
|
||||
-- Change an existing binding by unbinding it first, then binding the key again.
|
||||
-- This example changes SUPER+SPACE from the launcher to the Blob root menu.
|
||||
-- hl.unbind("SUPER + SPACE")
|
||||
-- o.bind("SUPER + SPACE", "Blob menu", "blob-menu toggle root")
|
||||
|
||||
-- Disable a default binding without replacing it.
|
||||
-- hl.unbind("SUPER + SHIFT + B")
|
||||
|
||||
-- Logitech MX Keys examples:
|
||||
-- o.bind("SUPER + SHIFT + S", nil, "blob-capture-screenshot")
|
||||
-- o.bind("SUPER + H", nil, "voxtype record toggle")
|
||||
-- o.bind("SUPER + PERIOD", nil, "blob-shell shell toggle blob.emojis")
|
||||
@@ -0,0 +1,29 @@
|
||||
-- Learn how to configure Hyprland: https://wiki.hypr.land/Configuring/Start/
|
||||
|
||||
-- Blob's bootstrap keeps path setup out of this user config.
|
||||
dofile((os.getenv("BLOB_PATH") or "$HOME/.local/share/blob") .. "/default/hypr/bootstrap.lua")
|
||||
|
||||
-- Disable all Blob default bindings. Add your own in hypr/bindings.lua.
|
||||
-- blob_default_bindings = false
|
||||
--
|
||||
-- Or disable only bindings for Blob's preinstalled apps/web apps while
|
||||
-- keeping core window-manager bindings:
|
||||
-- blob_preinstalled_bindings = false
|
||||
|
||||
-- Load Blob defaults.
|
||||
require("default.hypr.blob")
|
||||
|
||||
-- Put your personal overrides in these files. They're loaded after Blob's
|
||||
-- defaults so package updates can improve the defaults without rewriting your
|
||||
-- ~/.config/hypr files.
|
||||
require("hypr.monitors")
|
||||
require("hypr.input")
|
||||
require("hypr.bindings")
|
||||
require("hypr.looknfeel")
|
||||
require("hypr.autostart")
|
||||
|
||||
-- Toggle config flags dynamically.
|
||||
require("default.hypr.toggles")
|
||||
|
||||
-- Add any other personal Hyprland configuration below.
|
||||
-- o.window("qemu", { workspace = "5" })
|
||||
@@ -0,0 +1,14 @@
|
||||
# Makes hyprsunset do nothing to the screen by default
|
||||
# Without this, the default applies some tint to the monitor
|
||||
profile {
|
||||
time = 07:00
|
||||
identity = true
|
||||
}
|
||||
|
||||
# To enable auto switch to nightlight, add to your .config/hypr/autostart.lua:
|
||||
# o.launch_on_start("hyprsunset")
|
||||
# and use the following:
|
||||
# profile {
|
||||
# time = 20:00
|
||||
# temperature = 4000
|
||||
# }
|
||||
@@ -0,0 +1,57 @@
|
||||
-- Keep only your personal input overrides here. Uncommented settings below
|
||||
-- replace Blob's defaults.
|
||||
|
||||
-- Keyboard layout and options.
|
||||
-- See https://wiki.hypr.land/Configuring/Basics/Variables/#input
|
||||
-- hl.config({
|
||||
-- input = {
|
||||
-- -- Use multiple keyboard layouts and switch between them with Left Alt + Right Alt.
|
||||
-- kb_layout = "us,dk,eu",
|
||||
-- kb_options = "compose:caps,shift:both_capslock_cancel,grp:alts_toggle",
|
||||
--
|
||||
-- -- Use a specific keyboard variant if needed (e.g. intl for international keyboards).
|
||||
-- kb_variant = "intl",
|
||||
--
|
||||
-- -- Change speed of keyboard repeat.
|
||||
-- repeat_rate = 40,
|
||||
-- repeat_delay = 250,
|
||||
--
|
||||
-- -- Start with numlock on by default.
|
||||
-- numlock_by_default = true,
|
||||
--
|
||||
-- -- Increase sensitivity for mouse/trackpad (default: 0).
|
||||
-- sensitivity = 0.35,
|
||||
--
|
||||
-- -- Turn off mouse acceleration (default: adaptive).
|
||||
-- accel_profile = "flat",
|
||||
--
|
||||
-- touchpad = {
|
||||
-- -- Use natural (inverse) scrolling.
|
||||
-- natural_scroll = true,
|
||||
--
|
||||
-- -- Use two-finger clicks for right-click instead of lower-right corner.
|
||||
-- clickfinger_behavior = true,
|
||||
--
|
||||
-- -- Control the speed of your scrolling.
|
||||
-- scroll_factor = 0.4,
|
||||
--
|
||||
-- -- Enable the touchpad while typing.
|
||||
-- disable_while_typing = false,
|
||||
--
|
||||
-- -- Left-click-and-drag with three fingers.
|
||||
-- drag_3fg = 1,
|
||||
-- },
|
||||
-- },
|
||||
-- })
|
||||
|
||||
-- App-specific touchpad scroll speeds.
|
||||
-- o.window("(Alacritty|kitty|foot)", { scroll_touchpad = 1.5 })
|
||||
-- o.window("com.mitchellh.ghostty", { scroll_touchpad = 0.2 })
|
||||
|
||||
-- Enable touchpad gestures for changing workspaces.
|
||||
-- See https://wiki.hypr.land/Configuring/Advanced-and-Cool/Gestures/
|
||||
-- hl.gesture({ fingers = 3, direction = "horizontal", action = "workspace" })
|
||||
|
||||
-- Enable touchpad gestures for moving focus (helpful on scrolling layout).
|
||||
-- hl.gesture({ fingers = 3, direction = "left", action = function() hl.dispatch(hl.dsp.focus({ direction = "l" })) end })
|
||||
-- hl.gesture({ fingers = 3, direction = "right", action = function() hl.dispatch(hl.dsp.focus({ direction = "r" })) end })
|
||||
@@ -0,0 +1,50 @@
|
||||
-- Change the default Blob look'n'feel.
|
||||
|
||||
-- https://wiki.hypr.land/Configuring/Basics/Variables/#general
|
||||
-- hl.config({
|
||||
-- general = {
|
||||
-- -- No gaps between windows or borders.
|
||||
-- gaps_in = 0,
|
||||
-- gaps_out = 0,
|
||||
-- border_size = 0,
|
||||
--
|
||||
-- -- Change to niri-like side-scrolling layout.
|
||||
-- layout = "scrolling",
|
||||
-- },
|
||||
-- })
|
||||
|
||||
-- https://wiki.hypr.land/Configuring/Basics/Variables/#decoration
|
||||
-- hl.config({
|
||||
-- decoration = {
|
||||
-- -- Use round window corners.
|
||||
-- rounding = 8,
|
||||
--
|
||||
-- -- Dim unfocused windows (0.0 = no dim, 1.0 = fully dimmed).
|
||||
-- dim_inactive = true,
|
||||
-- dim_strength = 0.15,
|
||||
-- },
|
||||
-- })
|
||||
|
||||
-- https://wiki.hypr.land/Configuring/Basics/Variables/#animations
|
||||
-- hl.config({
|
||||
-- animations = {
|
||||
-- -- Disable all animations.
|
||||
-- enabled = false,
|
||||
-- },
|
||||
-- })
|
||||
|
||||
-- https://wiki.hypr.land/Configuring/Basics/Variables/#layout
|
||||
-- hl.config({
|
||||
-- layout = {
|
||||
-- -- Avoid overly wide single-window layouts on wide screens.
|
||||
-- single_window_aspect_ratio = { 1, 1 },
|
||||
-- },
|
||||
-- })
|
||||
|
||||
-- https://wiki.hypr.land/Configuring/Layouts/Scrolling-Layout/
|
||||
-- hl.config({
|
||||
-- scrolling = {
|
||||
-- -- See only one column per screen instead of two.
|
||||
-- column_width = 0.97,
|
||||
-- },
|
||||
-- })
|
||||
@@ -0,0 +1,14 @@
|
||||
-- See https://wiki.hypr.land/Configuring/Basics/Monitors/
|
||||
-- List current monitors and supported resolutions with: hyprctl monitors all
|
||||
|
||||
local blob_gdk_scale = 2
|
||||
local blob_monitor_scale = "auto"
|
||||
|
||||
hl.env("GDK_SCALE", tostring(blob_gdk_scale))
|
||||
hl.monitor({ output = "", mode = "preferred", position = "auto", scale = blob_monitor_scale })
|
||||
|
||||
-- Configure a specific monitor.
|
||||
-- hl.monitor({ output = "DP-2", mode = "2560x1440@144", position = "0x0", scale = 1 })
|
||||
|
||||
-- Portrait/rotated secondary monitor (transform: 1 = 90°, 3 = 270°).
|
||||
-- hl.monitor({ output = "DP-2", mode = "preferred", position = "auto", scale = 1, transform = 1 })
|
||||
@@ -0,0 +1,4 @@
|
||||
screencopy {
|
||||
allow_token_by_default = true
|
||||
custom_picker_binary = hyprland-preview-share-picker
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
[binds]
|
||||
|
||||
# Print the current image file
|
||||
<Ctrl+p> = exec lp "$imv_current_file"
|
||||
|
||||
# Trash the current image and quit the viewer (recoverable from Trash)
|
||||
<Ctrl+x> = exec gio trash -- "$imv_current_file"; quit
|
||||
|
||||
# Trash the current image and move to the next one
|
||||
<Ctrl+Shift+X> = exec gio trash -- "$imv_current_file"; close
|
||||
|
||||
# Rotate the currently open image by 90 degrees
|
||||
<Ctrl+r> = exec mogrify -rotate 90 "$imv_current_file"
|
||||
|
||||
# Edit the current image in Tensaku and quit the viewer
|
||||
<Ctrl+e> = exec tensaku-edit "$imv_current_file" & ; quit
|
||||
@@ -0,0 +1,32 @@
|
||||
add_newline = true
|
||||
command_timeout = 200
|
||||
format = "[$directory$git_branch$git_status]($style)$character"
|
||||
|
||||
[character]
|
||||
error_symbol = "[✗](bold cyan)"
|
||||
success_symbol = "[❯](bold cyan)"
|
||||
|
||||
[directory]
|
||||
truncation_length = 2
|
||||
truncation_symbol = "…/"
|
||||
repo_root_style = "bold cyan"
|
||||
repo_root_format = "[$repo_root]($repo_root_style)[$path]($style)[$read_only]($read_only_style) "
|
||||
|
||||
[git_branch]
|
||||
format = "[$branch]($style) "
|
||||
style = "italic cyan"
|
||||
|
||||
[git_status]
|
||||
format = '[$all_status]($style)'
|
||||
style = "cyan"
|
||||
ahead = "⇡${count} "
|
||||
diverged = "⇕⇡${ahead_count}⇣${behind_count} "
|
||||
behind = "⇣${count} "
|
||||
conflicted = " "
|
||||
up_to_date = " "
|
||||
untracked = "? "
|
||||
modified = " "
|
||||
stashed = ""
|
||||
staged = ""
|
||||
renamed = ""
|
||||
deleted = ""
|
||||
@@ -0,0 +1,40 @@
|
||||
# Host config for the Blob speaker tuning.
|
||||
#
|
||||
# This exists so the tuning gets its own PipeWire client rather than sharing
|
||||
# PipeWire's stock filter-chain.conf. That config merges every fragment in
|
||||
# ~/.config/pipewire/filter-chain.conf.d/, so hosting the tuning there would load
|
||||
# any unrelated filter a user keeps in that directory -- duplicating filters
|
||||
# already hosted elsewhere, and stopping them all when the tuning is switched off.
|
||||
#
|
||||
# Installed as ~/.config/pipewire/blob-speaker-tuning.conf with the tuning
|
||||
# graph merged from blob-speaker-tuning.conf.d/, and run with
|
||||
# pipewire -c blob-speaker-tuning.conf
|
||||
#
|
||||
# The contents are the minimum a filter-hosting client needs, taken from
|
||||
# /usr/share/pipewire/filter-chain.conf.
|
||||
|
||||
context.properties = {
|
||||
log.level = 0
|
||||
}
|
||||
|
||||
context.spa-libs = {
|
||||
audio.convert.* = audioconvert/libspa-audioconvert
|
||||
support.* = support/libspa-support
|
||||
}
|
||||
|
||||
context.modules = [
|
||||
# Boost the audio thread priority.
|
||||
{ name = libpipewire-module-rt
|
||||
args = { }
|
||||
flags = [ ifexists nofail ]
|
||||
}
|
||||
|
||||
# The native communication protocol.
|
||||
{ name = libpipewire-module-protocol-native }
|
||||
|
||||
# Lets this process provide nodes to PipeWire.
|
||||
{ name = libpipewire-module-client-node }
|
||||
|
||||
# Wraps nodes in an adapter with a converter and resampler.
|
||||
{ name = libpipewire-module-adapter }
|
||||
]
|
||||
@@ -0,0 +1,140 @@
|
||||
# Dell XPS 14 / XPS 16 (2026) speaker tuning.
|
||||
#
|
||||
# Biquad chain fitted to the measured response of the xps-audio-linux EasyEffects
|
||||
# profile under a dense pink-weighted multitone of 104 bin-aligned tones,
|
||||
# followed by a lookahead limiter. Measures 1.24 dB RMS against that reference
|
||||
# (0.97 dB weighted over the fit's own error metric).
|
||||
#
|
||||
# Fitted and measured on the XPS 14 (SKU 0DB9); the XPS 16 (0DBA) is covered on
|
||||
# report that the same profile suits it. See tuning.conf.
|
||||
#
|
||||
# Q below 200 Hz is capped at 1.8 on purpose. A closer magnitude fit is possible
|
||||
# with high-Q sections, but the reference produces its narrow bass features by
|
||||
# convolution, and reproducing them with high-Q biquads swung group delay 31 ms
|
||||
# across 63-80 Hz, which smears bass transients. The cap costs 0.33 dB and
|
||||
# halves the swing.
|
||||
#
|
||||
# This is a plain filter-chain sink rather than a WirePlumber smart filter. A
|
||||
# smart filter is the better shape -- it would leave the real device as the
|
||||
# default output instead of adding a second one -- but on PipeWire 1.6.8 /
|
||||
# WirePlumber 0.5.15 this graph loads and links correctly as a smart filter and
|
||||
# then passes audio through unprocessed: its controls are present and
|
||||
# mpv -> filter -> sink links are made, yet the filter's input monitor and the
|
||||
# speaker sink's monitor measure identically. Revisit when that is understood.
|
||||
#
|
||||
# Channels are wired explicitly because the limiter is a stereo plugin; a mono
|
||||
# graph is duplicated per channel and would limit each side independently,
|
||||
# shifting the stereo image on bass transients.
|
||||
|
||||
context.modules = [
|
||||
{ name = libpipewire-module-filter-chain
|
||||
args = {
|
||||
node.description = "Laptop Speakers"
|
||||
media.name = "Laptop Speakers"
|
||||
|
||||
filter.graph = {
|
||||
nodes = [
|
||||
{ type = builtin name = s0_l label = bq_highpass control = { "Freq" = 60.9 "Q" = 1.0 } }
|
||||
{ type = builtin name = s1_l label = bq_highpass control = { "Freq" = 60.9 "Q" = 1.0 } }
|
||||
{ type = builtin name = s2_l label = bq_peaking control = { "Freq" = 83.4 "Q" = 1.8 "Gain" = -8.0 } }
|
||||
{ type = builtin name = s3_l label = bq_peaking control = { "Freq" = 100.4 "Q" = 1.59 "Gain" = 7.47 } }
|
||||
{ type = builtin name = s4_l label = bq_peaking control = { "Freq" = 250.5 "Q" = 2.966 "Gain" = -4.7 } }
|
||||
{ type = builtin name = s5_l label = bq_peaking control = { "Freq" = 419.8 "Q" = 3.0 "Gain" = -5.83 } }
|
||||
{ type = builtin name = s6_l label = bq_peaking control = { "Freq" = 631.3 "Q" = 2.515 "Gain" = -10.33 } }
|
||||
{ type = builtin name = s7_l label = bq_peaking control = { "Freq" = 894.4 "Q" = 4.0 "Gain" = -2.42 } }
|
||||
{ type = builtin name = s8_l label = bq_peaking control = { "Freq" = 1355.7 "Q" = 2.884 "Gain" = 6.92 } }
|
||||
{ type = builtin name = s9_l label = bq_peaking control = { "Freq" = 1707.2 "Q" = 1.311 "Gain" = -6.54 } }
|
||||
{ type = builtin name = s10_l label = bq_peaking control = { "Freq" = 3100.0 "Q" = 0.5 "Gain" = -10.09 } }
|
||||
{ type = builtin name = s11_l label = bq_peaking control = { "Freq" = 3200.0 "Q" = 1.048 "Gain" = 3.09 } }
|
||||
{ type = builtin name = s12_l label = bq_highshelf control = { "Freq" = 6015.2 "Q" = 1.5 "Gain" = -1.34 } }
|
||||
|
||||
{ type = builtin name = s0_r label = bq_highpass control = { "Freq" = 60.9 "Q" = 1.0 } }
|
||||
{ type = builtin name = s1_r label = bq_highpass control = { "Freq" = 60.9 "Q" = 1.0 } }
|
||||
{ type = builtin name = s2_r label = bq_peaking control = { "Freq" = 83.4 "Q" = 1.8 "Gain" = -8.0 } }
|
||||
{ type = builtin name = s3_r label = bq_peaking control = { "Freq" = 100.4 "Q" = 1.59 "Gain" = 7.47 } }
|
||||
{ type = builtin name = s4_r label = bq_peaking control = { "Freq" = 250.5 "Q" = 2.966 "Gain" = -4.7 } }
|
||||
{ type = builtin name = s5_r label = bq_peaking control = { "Freq" = 419.8 "Q" = 3.0 "Gain" = -5.83 } }
|
||||
{ type = builtin name = s6_r label = bq_peaking control = { "Freq" = 631.3 "Q" = 2.515 "Gain" = -10.33 } }
|
||||
{ type = builtin name = s7_r label = bq_peaking control = { "Freq" = 894.4 "Q" = 4.0 "Gain" = -2.42 } }
|
||||
{ type = builtin name = s8_r label = bq_peaking control = { "Freq" = 1355.7 "Q" = 2.884 "Gain" = 6.92 } }
|
||||
{ type = builtin name = s9_r label = bq_peaking control = { "Freq" = 1707.2 "Q" = 1.311 "Gain" = -6.54 } }
|
||||
{ type = builtin name = s10_r label = bq_peaking control = { "Freq" = 3100.0 "Q" = 0.5 "Gain" = -10.09 } }
|
||||
{ type = builtin name = s11_r label = bq_peaking control = { "Freq" = 3200.0 "Q" = 1.048 "Gain" = 3.09 } }
|
||||
{ type = builtin name = s12_r label = bq_highshelf control = { "Freq" = 6015.2 "Q" = 1.5 "Gain" = -1.34 } }
|
||||
{ type = lv2
|
||||
name = limiter
|
||||
plugin = "http://lsp-plug.in/plugins/lv2/limiter_stereo"
|
||||
control = {
|
||||
# Both default to enabled: "alr" regulates level toward the
|
||||
# threshold and "boost" normalises the threshold up to full
|
||||
# scale. A fixed tuning must switch them off or its tone drifts
|
||||
# with programme level.
|
||||
"alr" = 0
|
||||
"boost" = 0
|
||||
"g_in" = 0.5456
|
||||
"th" = 0.891
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
links = [
|
||||
{ output = "s0_l:Out" input = "s1_l:In" }
|
||||
{ output = "s1_l:Out" input = "s2_l:In" }
|
||||
{ output = "s2_l:Out" input = "s3_l:In" }
|
||||
{ output = "s3_l:Out" input = "s4_l:In" }
|
||||
{ output = "s4_l:Out" input = "s5_l:In" }
|
||||
{ output = "s5_l:Out" input = "s6_l:In" }
|
||||
{ output = "s6_l:Out" input = "s7_l:In" }
|
||||
{ output = "s7_l:Out" input = "s8_l:In" }
|
||||
{ output = "s8_l:Out" input = "s9_l:In" }
|
||||
{ output = "s9_l:Out" input = "s10_l:In" }
|
||||
{ output = "s10_l:Out" input = "s11_l:In" }
|
||||
{ output = "s11_l:Out" input = "s12_l:In" }
|
||||
{ output = "s12_l:Out" input = "limiter:in_l" }
|
||||
{ output = "s0_r:Out" input = "s1_r:In" }
|
||||
{ output = "s1_r:Out" input = "s2_r:In" }
|
||||
{ output = "s2_r:Out" input = "s3_r:In" }
|
||||
{ output = "s3_r:Out" input = "s4_r:In" }
|
||||
{ output = "s4_r:Out" input = "s5_r:In" }
|
||||
{ output = "s5_r:Out" input = "s6_r:In" }
|
||||
{ output = "s6_r:Out" input = "s7_r:In" }
|
||||
{ output = "s7_r:Out" input = "s8_r:In" }
|
||||
{ output = "s8_r:Out" input = "s9_r:In" }
|
||||
{ output = "s9_r:Out" input = "s10_r:In" }
|
||||
{ output = "s10_r:Out" input = "s11_r:In" }
|
||||
{ output = "s11_r:Out" input = "s12_r:In" }
|
||||
{ output = "s12_r:Out" input = "limiter:in_r" }
|
||||
]
|
||||
|
||||
inputs = [ "s0_l:In" "s0_r:In" ]
|
||||
outputs = [ "limiter:out_l" "limiter:out_r" ]
|
||||
}
|
||||
|
||||
audio.channels = 2
|
||||
audio.position = [ FL FR ]
|
||||
|
||||
capture.props = {
|
||||
node.name = "blob_speaker_tuning"
|
||||
media.class = Audio/Sink
|
||||
}
|
||||
playback.props = {
|
||||
node.name = "blob_speaker_tuning_output"
|
||||
node.passive = true
|
||||
target.object = "@SPEAKER_SINK@"
|
||||
# This stream is the filter's output and is a movable sink input like any
|
||||
# other, so anything that reroutes "all streams" to a newly selected
|
||||
# output would drag the processing along with it -- onto headphones, or
|
||||
# into the tuning's own sink, which is a cycle. Pin it.
|
||||
node.dont-move = true
|
||||
# If the speaker sink is not present yet -- the tuning host can start
|
||||
# before the device is discovered -- WirePlumber would otherwise link this
|
||||
# output to whatever default exists, quietly tuning the wrong device while
|
||||
# the tuning sink still looks healthy. Wait for the named target instead.
|
||||
# Both are needed: without linger, WirePlumber destroys the node rather
|
||||
# than waiting (see its scripts/linking/find-defined-target.lua).
|
||||
node.dont-fallback = true
|
||||
node.linger = true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,39 @@
|
||||
## Dell XPS 14 / XPS 16 (2026) internal speakers.
|
||||
##
|
||||
## Thirteen biquads and a lookahead limiter, applied as a PipeWire filter-chain
|
||||
## in front of the internal speaker sink. The stock Linux path already loads
|
||||
## Dell's Cirrus smart-amplifier firmware; this adds the perceptual voicing the
|
||||
## Windows Waves layer provides and Linux does not.
|
||||
|
||||
description="Dell XPS 14/16 (2026) speakers"
|
||||
## Matched on the DMI product SKU, which is what Dell keys the Cirrus speaker
|
||||
## firmware on -- 10280db9 for the XPS 14 and 10280dba for the XPS 16 -- so it
|
||||
## identifies the speaker hardware itself rather than a marketing name. Compared as
|
||||
## whole values, so this cannot widen to the rest of the XPS line.
|
||||
##
|
||||
## 0DB9 XPS 14 -- measured here, see below
|
||||
## 0DBA XPS 16 -- included on report that this profile suits it, not measured
|
||||
match_sku=("0DB9" "0DBA")
|
||||
## Unescaped dots: this is passed to awk as a string, where a backslash escape
|
||||
## would be consumed before the regex sees it.
|
||||
sink_pattern='^alsa_output.*sof_sdw.*HiFi__Speaker__sink$'
|
||||
|
||||
## Provenance. Derived by measuring the response of the xps-clone EasyEffects
|
||||
## profile from https://github.com/spencerbull/xps-audio-linux (MIT) and fitting
|
||||
## a biquad chain to it. No upstream asset is redistributed: the convolution
|
||||
## impulse response is not carried, so this tuning has no binary blob and is
|
||||
## sample-rate agnostic.
|
||||
derived_from="xps-audio-linux xps-clone (MIT, spencerbull)"
|
||||
validated_by="dhh"
|
||||
validated_on="2026-07-24"
|
||||
## The measurements below were taken on the XPS 14 (0DB9). The XPS 16 (0DBA) is
|
||||
## covered on report rather than measurement; re-measure there before treating
|
||||
## these figures as describing it.
|
||||
validated_hardware="XPS 14 DA14260 (0DB9)"
|
||||
|
||||
## Measured against that reference under a dense pink-weighted multitone of 104
|
||||
## bin-aligned tones. See docs/AUDIO-TUNING.md for how to reproduce these.
|
||||
magnitude_rms_db="1.24"
|
||||
bass_group_delay_swing_ms="13.2"
|
||||
limiter_headroom_db="1.6" ## worst-case peak on a hot master vs -1 dBFS
|
||||
dynamic_range_delta_lu="0.1"
|
||||
@@ -0,0 +1,7 @@
|
||||
[main]
|
||||
font=JetBrainsMono Nerd Font:size=18
|
||||
pad=0x0
|
||||
|
||||
[colors-dark]
|
||||
background=000000
|
||||
foreground=ffffff
|
||||
@@ -0,0 +1,2 @@
|
||||
[Manager]
|
||||
DefaultTimeoutStopSec=5s
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Use the Vfio to Integrated trick to turn off NVIDIA dgpu when in integrated mode
|
||||
# without needing to restart the computer. This is needed because computers like the Asus G14
|
||||
# will wake after suspend in Hybrid mode, even if the system was in Integrated mode before
|
||||
# suspending.
|
||||
|
||||
restore_marker=/run/blob-force-igpu-integrated
|
||||
sleep_action=${SYSTEMD_SLEEP_ACTION:-$2}
|
||||
[[ -x /usr/bin/supergfxctl ]] || exit 0
|
||||
|
||||
switch_mode() {
|
||||
local expected="$1" current
|
||||
|
||||
if ! /usr/bin/timeout --kill-after=1s 3s /usr/bin/supergfxctl -m "$expected"; then
|
||||
echo "Could not request the GPU transition to $expected mode" >&2
|
||||
return 1
|
||||
fi
|
||||
for _ in {1..10}; do
|
||||
if current=$(/usr/bin/timeout --kill-after=1s 2s /usr/bin/supergfxctl -g 2>/dev/null) &&
|
||||
[[ $current == "$expected" ]]; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "Could not confirm the GPU transition to $expected mode" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
pre)
|
||||
# Remember the mode this sleep cycle started in. supergfxctl persists the
|
||||
# temporary hibernate switch to Vfio, so post must not consult that mutable
|
||||
# value when deciding whether to restore Integrated mode.
|
||||
if [[ -L $restore_marker ]]; then
|
||||
exit 1
|
||||
elif [[ ! -f $restore_marker ]]; then
|
||||
/usr/bin/grep -Eq '"mode"[[:space:]]*:[[:space:]]*"Integrated"' /etc/supergfxd.conf 2>/dev/null || exit 0
|
||||
/usr/bin/install -m 0600 -o root -g root -T /dev/null "$restore_marker"
|
||||
fi
|
||||
|
||||
# Before hibernating, switch to Vfio so the nvidia driver is detached from the dGPU.
|
||||
# Without this, hibernate resume fails because the nvidia driver can't freeze a
|
||||
# powered-off dGPU (returns -EIO), which aborts the entire resume.
|
||||
if [[ $sleep_action == "hibernate" ]]; then
|
||||
switch_mode Vfio
|
||||
fi
|
||||
;;
|
||||
post)
|
||||
[[ -f $restore_marker && ! -L $restore_marker ]] || exit 0
|
||||
|
||||
# small delay so the device is fully re-enumerated
|
||||
sleep 4
|
||||
|
||||
# force-bind dGPU to vfio (fully detached from nvidia)
|
||||
switch_mode Vfio
|
||||
|
||||
# then go back to Integrated, which powers it off again
|
||||
switch_mode Integrated
|
||||
/usr/bin/rm -f -- "$restore_marker"
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Turn off keyboard backlight before hibernate to prevent hang on power-off.
|
||||
# The ASUS keyboard controller can block S4 shutdown if LEDs are active.
|
||||
|
||||
sleep_action=${SYSTEMD_SLEEP_ACTION:-$2}
|
||||
|
||||
if [[ $1 == "pre" && $sleep_action == "hibernate" ]]; then
|
||||
device=""
|
||||
for candidate in /sys/class/leds/*kbd_backlight*; do
|
||||
if [[ -e "$candidate" ]]; then
|
||||
device="$(basename "$candidate")"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -n "$device" ]]; then
|
||||
brightnessctl -d "$device" set 0 >/dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Lazy-unmount gvfsd-fuse filesystems before suspend/hibernate to prevent the
|
||||
# kernel's process freeze from timing out. FUSE daemons (like gvfsd-fuse from
|
||||
# Nautilus) can block in uninterruptible sleep during freeze, causing suspend
|
||||
# to silently fail. After wake, restart gvfs so the FUSE mount is restored.
|
||||
|
||||
if [[ $1 == "pre" ]]; then
|
||||
while IFS=' ' read -r _ mountpoint fstype _; do
|
||||
if [[ $fstype == fuse.gvfsd-fuse ]]; then
|
||||
mountpoint=$(printf '%b' "$mountpoint")
|
||||
fusermount3 -uz "$mountpoint" 2>/dev/null || fusermount -uz "$mountpoint" 2>/dev/null || true
|
||||
fi
|
||||
done < /proc/mounts
|
||||
fi
|
||||
|
||||
if [[ $1 == "post" ]]; then
|
||||
# Run in background — user.slice is still frozen at this point, so a
|
||||
# synchronous restart would block the thaw for up to 90 seconds.
|
||||
(
|
||||
sleep 5
|
||||
for uid_dir in /run/user/*; do
|
||||
uid=$(basename "$uid_dir")
|
||||
if [[ -S $uid_dir/bus ]]; then
|
||||
sudo -u "#$uid" env \
|
||||
DBUS_SESSION_BUS_ADDRESS="unix:path=$uid_dir/bus" \
|
||||
XDG_RUNTIME_DIR="$uid_dir" \
|
||||
systemctl --user restart gvfs-daemon.service 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
) &
|
||||
fi
|
||||
@@ -0,0 +1,3 @@
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=/usr/bin/updatedb --prune-bind-mounts=no --add-prunepaths=/.snapshots
|
||||
@@ -0,0 +1,6 @@
|
||||
[Service]
|
||||
# Delay startup to avoid race condition with display manager initialization
|
||||
# when booting in Integrated mode. Without this delay, the system can freeze
|
||||
# on boot because supergfxd tries to disable the dGPU while the display
|
||||
# subsystem is still initializing.
|
||||
ExecStartPre=/bin/sleep 5
|
||||
@@ -0,0 +1,16 @@
|
||||
# Make user apps the only thing systemd-oomd is allowed to kill.
|
||||
#
|
||||
# Hyprland runs in session.slice, as wayland-wm@hyprland.desktop.service, while
|
||||
# everything launched through uwsm-app lands in app.slice/app-*.scope. Marking
|
||||
# only app.slice as a kill candidate means the compositor is structurally
|
||||
# ineligible: oomd takes the browser or terminal that caused the pressure, and
|
||||
# the session survives to show the notification about it. Setting this on
|
||||
# user@.service instead would put the compositor back in the candidate pool.
|
||||
#
|
||||
# Swap kill is a backstop for the slower shape of the same problem, where swap
|
||||
# fills before pressure spikes. It uses the global SwapUsedLimit (90%) from
|
||||
# /etc/systemd/oomd.conf.d/10-blob.conf, which also carries the pressure
|
||||
# thresholds.
|
||||
[Slice]
|
||||
ManagedOOMMemoryPressure=kill
|
||||
ManagedOOMSwap=kill
|
||||
@@ -0,0 +1,19 @@
|
||||
[Unit]
|
||||
Description=Announce process crashes and offer an AI diagnosis
|
||||
# Needs the session bus to notify, and uwsm-app to open the diagnosis terminal.
|
||||
# Both are up only after graphical-session.target.
|
||||
After=graphical-session.target
|
||||
PartOf=graphical-session.target
|
||||
ConditionEnvironment=WAYLAND_DISPLAY
|
||||
# Set by blob-toggle-crash-capture. Checked here so a disabled watcher stays
|
||||
# disabled across logins without the unit having to be disabled.
|
||||
ConditionPathExists=!%h/.local/state/blob/toggles/crash-capture-off
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/blob-crash-watch
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
@@ -0,0 +1,31 @@
|
||||
[Unit]
|
||||
Description=Fcitx5 input method (XCompose sequences)
|
||||
# fcitx5 turns the CapsLock compose sequences in ~/.XCompose into text for
|
||||
# Wayland clients.
|
||||
#
|
||||
# Wait for the compositor: fcitx5 needs WAYLAND_DISPLAY and DISPLAY, which uwsm
|
||||
# imports into the user manager before it reaches graphical-session.target.
|
||||
After=graphical-session.target
|
||||
# The wayland connection dies with the compositor, so follow the session rather
|
||||
# than linger against a socket that is gone.
|
||||
PartOf=graphical-session.target
|
||||
# After= is ordering only -- it does not stop anything from starting this unit
|
||||
# while the target is inactive. An `blob update` over SSH has a live user
|
||||
# manager (pam_systemd) and no graphical session, and a fcitx5 started there
|
||||
# comes up with no WAYLAND_DISPLAY and no way to reach any client. Worse, it
|
||||
# stays active, so the later graphical-session.target activation won't pull in a
|
||||
# working one -- Wants= does not restart what is already running. Skip the start
|
||||
# instead; the unit stays enabled and starts for real at graphical login.
|
||||
ConditionEnvironment=WAYLAND_DISPLAY
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# notificationitem duplicates the tray entry blob already renders itself.
|
||||
ExecStart=/usr/bin/fcitx5 --disable notificationitem
|
||||
# always, not on-failure: fcitx5 exits 0 when it detects another instance owning
|
||||
# its bus name, and a clean exit still leaves the user with no input method.
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
@@ -0,0 +1,19 @@
|
||||
[Unit]
|
||||
Description=Lock Blob before suspend
|
||||
# The monitor calls into the running Blob shell. Wait until UWSM has imported
|
||||
# BLOB_PATH and WAYLAND_DISPLAY, but keep the default target ordering so the
|
||||
# monitor starts before graphical-session.target is reached.
|
||||
After=dbus.socket wayland-session-waitenv.service
|
||||
Requires=dbus.socket
|
||||
PartOf=graphical-session.target
|
||||
ConditionEnvironment=BLOB_PATH
|
||||
ConditionEnvironment=WAYLAND_DISPLAY
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/blob-system-sleep-monitor
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
@@ -0,0 +1,32 @@
|
||||
[Unit]
|
||||
Description=Blob speaker tuning filter-chain
|
||||
Documentation=https://github.com/basecamp/omarchy/blob/master/docs/AUDIO-TUNING.md
|
||||
# WirePlumber does the linking, so starting before it is up risks the output being
|
||||
# linked before the speaker device has been discovered.
|
||||
After=pipewire.service wireplumber.service
|
||||
Requires=pipewire.service
|
||||
Wants=wireplumber.service
|
||||
# Restart with the audio daemon, since the filter-chain loses its connection when
|
||||
# PipeWire goes away.
|
||||
PartOf=pipewire.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# Hosts the tuning as a PipeWire *client* rather than loading it into the daemon
|
||||
# from pipewire.conf.d, which is only read at daemon startup. That is what lets
|
||||
# the tuning be switched on and off without restarting pipewire-pulse -- a
|
||||
# restart drops every PulseAudio client's connection, and applications that do
|
||||
# not reconnect (Spotify) have to be restarted by hand.
|
||||
#
|
||||
# It also contains failure: a malformed tuning breaks only this service, where a
|
||||
# bad drop-in in the daemon's own config stops PipeWire from starting at all.
|
||||
#
|
||||
# The config name is deliberately not PipeWire's stock filter-chain.conf, which
|
||||
# merges every fragment in ~/.config/pipewire/filter-chain.conf.d/ and would make
|
||||
# this service host unrelated user filters too.
|
||||
ExecStart=/usr/bin/pipewire -c blob-speaker-tuning.conf
|
||||
Restart=on-failure
|
||||
RestartSec=2
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
@@ -0,0 +1,23 @@
|
||||
[Unit]
|
||||
Description=Bluetooth pairing agent (auto-accept)
|
||||
Documentation=man:bt-agent(1)
|
||||
ConditionPathIsDirectory=/sys/class/bluetooth
|
||||
# bluez must be reachable on the system bus before we can register.
|
||||
After=dbus.socket
|
||||
Requires=dbus.socket
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# If bluetoothd is unavailable (for example in VMs or machines without a
|
||||
# usable adapter), skip cleanly instead of entering a restart loop.
|
||||
ExecCondition=/usr/bin/systemctl is-active --quiet bluetooth.service
|
||||
# NoInputNoOutput auto-accepts pair requests. Safe because the adapter
|
||||
# is only `pairable: true` when the user explicitly opens the blob
|
||||
# bluetoothPanel and starts scanning; outside that window bluez refuses
|
||||
# inbound pair attempts at a lower layer.
|
||||
ExecStart=/usr/bin/bt-agent -c NoInputNoOutput
|
||||
Restart=on-failure
|
||||
RestartSec=2
|
||||
|
||||
[Install]
|
||||
WantedBy=graphical-session.target
|
||||
@@ -0,0 +1,2 @@
|
||||
[Service]
|
||||
TimeoutStopSec=5s
|
||||
@@ -0,0 +1,559 @@
|
||||
# Commands
|
||||
|
||||
Generated by `blob-docs-commands` from the `# blob:summary=` line in each
|
||||
file. Do not edit by hand.
|
||||
|
||||
Commands marked hidden are plumbing other commands call, and are left out
|
||||
of the `blob` listing.
|
||||
|
||||
|
||||
## audio
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-audio-availability` | Print PulseAudio sink availability for the shell | - |
|
||||
| `blob-audio-input-mute` | Toggle microphone mute. Drives the hardware mic-mute LED on laptops that expose one. | - |
|
||||
| `blob-audio-input-set` | Set the default audio input and move active streams | <node-id> <source-name> |
|
||||
| `blob-audio-restart` | Restart audio services and recover stuck USB audio devices. | - |
|
||||
| `blob-audio-sink` | Print the sink whose volume and mute a given output really uses | [sink-name] |
|
||||
| `blob-audio-sink-set` | Set the default audio output and move active streams | <node-id> <sink-name> |
|
||||
| `blob-audio-sink-switch` | Switch between audio outputs while preserving the mute status | - |
|
||||
| `blob-audio-source-switch` | Cycle to the next media source and transfer playback when the current source is playing | [next\|previous] |
|
||||
| `blob-audio-tuning` | Manage the speaker tuning for this laptop | <on\|off\|status\|match\|fronted-sink> [--force] |
|
||||
| `blob-audio-volume` | Adjust output volume and show the Blob OSD | <raise\|lower\|mute-toggle\|+N\|-N> |
|
||||
|
||||
## bar
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-bar` | Configure the bar and its widget layout | use <id> \| reset \| defaults \| position <top\|bottom\|left\|right> \| transparent <true\|false\|toggle> \| put <id> [placement] \| move <id> [placement] \| set <id> <key> <value> [--json] [placement] |
|
||||
| `blob-bar-color` | Choose a legible transparent bar text color (hidden) | - |
|
||||
|
||||
## battery
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-battery-low` | Send the low battery warning notification and run battery-low hooks. (hidden) | <percentage> |
|
||||
| `blob-battery-status` | Returns a formatted battery status string with percentage and power draw/charge. | [--shell] |
|
||||
|
||||
## bg
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-bg-cache` | Cache background switcher thumbnails for the current theme | - |
|
||||
| `blob-bg-current` | Show current background | - |
|
||||
| `blob-bg-install` | Open the current theme's user background folder | - |
|
||||
| `blob-bg-next` | Cycle to the next background for the current theme | - |
|
||||
| `blob-bg-set` | Set the current background image | <path-to-image> |
|
||||
| `blob-bg-switcher` | Open the Blob background switcher | - |
|
||||
|
||||
## bluetooth
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-bluetooth-device` | Control a Bluetooth device | [pair\|connect\|disconnect\|forget] <address> |
|
||||
| `blob-bluetooth-power` | Turn Bluetooth on or off, remembered across reboots | <on\|off\|toggle\|is-on> |
|
||||
| `blob-bluetooth-restart` | Unblock and restart the bluetooth service. | - |
|
||||
|
||||
## boot
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-boot` | - | - |
|
||||
|
||||
## branding
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-branding-about` | Edit, set, or reset About branding | <image\|text\|reset> |
|
||||
| `blob-branding-screensaver` | Edit, set, or reset screensaver branding | <image\|text\|reset> |
|
||||
|
||||
## brightness
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-brightness-ddc` | Show or adjust DDC/CI display brightness for a Hyprland monitor. | <monitor> [+N%\|N%-\|N%] |
|
||||
| `blob-brightness-display` | Show or adjust brightness on the focused display. | [--no-osd] [--monitor name] [+N%\|N%-\|N%\|off\|on] |
|
||||
| `blob-brightness-display-apple` | Show or adjust Apple Studio Display and Apple XDR Display brightness using asdcontrol. | [--no-osd] [+N%\|N%-\|N%] |
|
||||
| `blob-brightness-keyboard` | Adjust keyboard backlight brightness using available steps. | [--no-osd] <up\|down\|cycle\|off\|restore> |
|
||||
| `blob-brightness-keyboard-mute` | Set the mic-mute indicator LED on laptops that expose a platform::micmute LED node. | <on\|off> |
|
||||
|
||||
## capture
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-capture-qr` | Decode a QR code from a screenshot region | - |
|
||||
| `blob-capture-record` | Start or stop screen recording | [--fullscreen] [--with-desktop-audio] [--with-microphone-audio] [--with-webcam] [--webcam-device=<device>] [--webcam-size=<small\|medium\|large>] [--resolution=<size>] [--stop-recording] |
|
||||
| `blob-capture-record-webcam` | Pick a webcam and start a screen recording with it | - |
|
||||
| `blob-capture-region` | Pick a screen region over frozen screen content (hidden) | [region\|windows\|smart\|fullscreen] [--keep-freeze] [--match-monitor] \| --take-fullscreen \| --take-window \| --select-window <next\|prev\|left\|right\|up\|down> |
|
||||
| `blob-capture-screenshot` | Take a screenshot | [smart\|region\|windows\|fullscreen] [slurp\|copy\|save] [--editor=<name>] |
|
||||
| `blob-capture-text` | Extract text from a screenshot region with OCR | - |
|
||||
| `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> |
|
||||
|
||||
## clipboard
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-clipboard-file` | Copy a file to the clipboard and paste it (hidden) | [--copy-only] <mime-type> <path> |
|
||||
| `blob-clipboard-open` | Open a clipboard history entry (hidden) | --history-index <index> |
|
||||
| `blob-clipboard-text` | Copy text to the clipboard and type or paste it (hidden) | [--shift-insert] [--copy-only] [--history-index <index>\|<text>] |
|
||||
|
||||
## cmd
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-cmd-cwd` | Print the current working directory of the active terminal window (hidden) | - |
|
||||
| `blob-cmd-missing` | Check whether any required commands are missing | - |
|
||||
| `blob-cmd-present` | Check whether all required commands are available | - |
|
||||
|
||||
## crash
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-crash-watch` | Watch for process crashes and offer an AI diagnosis (hidden) | - |
|
||||
|
||||
## default
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-default-browser` | Set the default browser for Blob and XDG handlers | [chromium\|chrome\|brave\|brave-origin\|edge\|firefox\|zen] |
|
||||
| `blob-default-editor` | Set the default editor used by blob-launch-editor | [code\|cursor\|zed\|sublime_text\|helix\|vim\|emacs\|nvim] |
|
||||
| `blob-default-terminal` | Set the default terminal used by xdg-terminal-exec | [alacritty\|foot\|ghostty\|kitty] |
|
||||
|
||||
## disk
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-disk-speedtest` | Measure live disk read and write speed | [target-dir] |
|
||||
|
||||
## display
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-display-size` | Scale text everywhere — blob shell, GTK apps, and terminals | [size\|reset] |
|
||||
| `blob-display-state` | Print monitor panel state for the shell | - |
|
||||
|
||||
## docs
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-docs-commands` | Regenerate docs/commands.md from each command's own metadata (hidden) | - |
|
||||
| `blob-docs-keybinds` | Regenerate docs/keybinds.md from the Hyprland Lua bindings (hidden) | - |
|
||||
|
||||
## drive
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-drive-info` | Print drive information such as size, model, and mount details | <drive> |
|
||||
| `blob-drive-password` | Set a new encryption password for a drive selected. | - |
|
||||
| `blob-drive-select` | Select a drive from a list with info that includes space and brand. Used by blob-drive-password. | - |
|
||||
|
||||
## file
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-file-select` | Pick files with the desktop file chooser | [--title <title>] [--multiple] [--directory] [--extensions "<ext ext...>"] |
|
||||
|
||||
## font
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-font-current` | Show current monospace font | - |
|
||||
| `blob-font-list` | List available monospace fonts | - |
|
||||
| `blob-font-set` | Set the system monospace font | <font-name> |
|
||||
|
||||
## git
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-git-url-check` | Check that a git URL names a repository, not a transport helper (hidden) | <git-url> |
|
||||
|
||||
## hibernation
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-hibernation-available` | Check if hibernation is supported | - |
|
||||
|
||||
## hook
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-hook` | Run a named hook from ~/.config/blob/hooks/<name> and ~/.config/blob/hooks/<name>.d/. | [name] [args...] |
|
||||
|
||||
## hw
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-hw-clamshell` | Returns true when clamshell mode is active (hidden) | - |
|
||||
| `blob-hw-dell-xps-haptic-touchpad` | Match Dell XPS systems with the Synaptics haptic touchpad. | - |
|
||||
| `blob-hw-display` | Print the most likely display backlight device. | - |
|
||||
| `blob-hw-external` | Returns true when an external monitor is physically connected. | - |
|
||||
| `blob-hw-fingerprint` | Returns true when a fingerprint reader is present (hidden) | - |
|
||||
| `blob-hw-hybrid-gpu` | Detect whether the system has an active hybrid GPU configuration | - |
|
||||
| `blob-hw-laptop` | Returns true when running on a laptop (has a lid or laptop chassis). | - |
|
||||
| `blob-hw-laptop-closed` | Returns true when the laptop lid is closed (hidden) | - |
|
||||
| `blob-hw-match` | Match against the computer's DMI product name or product family (case-insensitive). | <pattern> |
|
||||
| `blob-hw-nvidia` | Detect whether the computer has an NVIDIA GPU. | - |
|
||||
| `blob-hw-nvidia-gsp` | Detect whether the computer has an NVIDIA GPU with GSP firmware (Turing or newer). | - |
|
||||
| `blob-hw-nvidia-without-gsp` | Detect whether the computer has an NVIDIA GPU without GSP firmware (Maxwell/Pascal/Volta). | - |
|
||||
| `blob-hw-touchpad` | Print the detected Hyprland touchpad or trackpad device name | - |
|
||||
| `blob-hw-touchscreen` | Print the detected Hyprland touchscreen or tablet device name | - |
|
||||
| `blob-hw-webcam` | Check whether a webcam is available | - |
|
||||
|
||||
## hypr
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-hypr-focus` | Focus a Hyprland window by application identity | <app-name> |
|
||||
| `blob-hypr-monitor-clamshell` | Apply clamshell display state to Hyprland monitors (hidden) | - |
|
||||
| `blob-hypr-monitor-external` | Returns true when Hyprland has an active external monitor (hidden) | - |
|
||||
| `blob-hypr-monitor-focused` | Print the name of the currently focused Hyprland monitor. | - |
|
||||
| `blob-hypr-monitor-focused-apple` | Return success if the focused or named Hyprland monitor is an Apple display. | [monitor] |
|
||||
| `blob-hypr-monitor-internal` | Enable, disable, toggle, or recover the internal laptop display | <on\|off\|toggle\|recover> |
|
||||
| `blob-hypr-monitor-laptop` | Print the name of the built-in laptop display, including disabled outputs. | - |
|
||||
| `blob-hypr-monitor-mirror` | Enable, disable, toggle, or recover mirroring the internal display to an external monitor | <on\|off\|toggle\|recover> |
|
||||
| `blob-hypr-monitor-modeless` | Returns true when Hyprland has an enabled monitor with no mode (hidden) | - |
|
||||
| `blob-hypr-monitor-scaling` | Show, set, or adjust focused Hyprland monitor scaling | [up\|down\|SCALE] |
|
||||
| `blob-hypr-monitor-watch` | Watch Hyprland monitor events and recover monitor toggles when a monitor is removed | - |
|
||||
| `blob-hypr-reload-guard` | Pause or resume Hyprland config auto-reload around package transactions. (hidden) | - |
|
||||
| `blob-hypr-restart` | Reload hyprland configuration (used by the Blob theme switching). | - |
|
||||
| `blob-hypr-session-locked` | Returns true when the compositor holds a session lock (hidden) | - |
|
||||
| `blob-hypr-toggle` | Toggle permanent Hyprland flags by copying them into a directory that's sourced entirely. | <flag-name> [on\|off\|toggle] |
|
||||
| `blob-hypr-toggle-disabled` | Check if a Hyprland toggle is currently disabled (missing). | <flag-name> |
|
||||
| `blob-hypr-toggle-enabled` | Check if a Hyprland toggle is currently enabled. | <flag-name> |
|
||||
| `blob-hypr-window-close-all` | Close all open windows | - |
|
||||
| `blob-hypr-window-gaps-toggle` | Toggles the window gaps globally between no gaps and the default. | - |
|
||||
| `blob-hypr-window-pop` | Toggle to pop-out a tile to stay fixed on a display basis. | [width height x y] |
|
||||
| `blob-hypr-window-single-square-aspect-toggle` | Toggle single-window square aspect ratio. | - |
|
||||
| `blob-hypr-window-tiled-fullscreen-toggle` | Toggle tiled fullscreen for the focused Hyprland window | - |
|
||||
| `blob-hypr-window-transparency-toggle` | Toggles transparency for the currently focused window. | - |
|
||||
| `blob-hypr-window-width` | Save or restore the focused Hyprland window width | <save\|restore> |
|
||||
| `blob-hypr-workspace-layout-toggle` | Toggle the layout on the current active workspace between dwindle and scrolling | - |
|
||||
|
||||
## install
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-install-font` | Install a Nerd Font package and switch the system to it | <display-name> <package> <family> |
|
||||
| `blob-install-launch` | Install a packaged app and launch it once it finishes | <display-name> <packages> <desktop-id> |
|
||||
|
||||
## launch
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-launch-about` | Launch the fastfetch TUI that gives information about the current system. | - |
|
||||
| `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-docker-tui` | Open the Docker TUI (lazydocker) with access to the Docker daemon (hidden) | - |
|
||||
| `blob-launch-editor` | Launch the default editor selected via Blob defaults. | [--inline] <path> |
|
||||
|
||||
## launcher
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-launcher-remove` | Remove or uninstall the selected launcher entry (hidden) | <desktop-id> <name> |
|
||||
|
||||
## launch
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `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-tui` | Launch a TUI or focus an existing terminal window for it | [--app-id=<app-id>] <command> [args...] |
|
||||
| `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-tui` | Launch a TUI command in the default terminal with Blob styling | [--app-id=<app-id>] <command> [args...] |
|
||||
|
||||
## menu
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-menu` | Control the Blob menu (toggle / summon / close / refresh) | [toggle\|summon\|close\|refresh\|ping] [route] |
|
||||
| `blob-menu-emoji` | Launch emojis | - |
|
||||
| `blob-menu-emoji-insert` | Insert an emoji into the focused application (hidden) | <emoji> |
|
||||
| `blob-menu-file` | Pick a file from a menu | label paths formats [menu args...] |
|
||||
| `blob-menu-images` | Open a generic image selector menu | [--selected <image>] [--print-name] [--show-labels] [--filterable] [--lazy-thumbnails] [--preload] [--cache-only] <image-dir>... |
|
||||
| `blob-menu-keybindings` | Display Hyprland keybindings defined in your configuration using an interactive search menu. | - |
|
||||
| `blob-menu-plugin` | Pick a shell plugin to enable, disable, clone, or remove | <enable\|disable\|clone\|remove> |
|
||||
| `blob-menu-select` | Pick one option from a menu | prompt [option...] [-- menu args...] |
|
||||
| `blob-menu-share` | Share clipboard, files, or folders with LocalSend | <clipboard\|file\|folder> [path...] |
|
||||
| `blob-menu-timezone` | Select and set the system timezone | - |
|
||||
|
||||
## network
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-network-band` | Show or pin the Wi-Fi band for the active connection | [auto\|2.4\|5\|6] |
|
||||
| `blob-network-dns` | Show or configure the system DNS provider | [Cloudflare\|Google\|DHCP\|Custom] |
|
||||
| `blob-network-iwd` | - | - |
|
||||
| `blob-network-password` | Print the active Wi-Fi connection's password | <interface> |
|
||||
| `blob-network-qr` | Generate a Wi-Fi QR matrix for the shell | [--meta] [interface] |
|
||||
| `blob-network-restart` | Unblock and restart the Wi-Fi service. | - |
|
||||
| `blob-network-speedtest` | Measure live internet speed for one direction | [down\|up] |
|
||||
| `blob-network-status` | Print active network status for the shell | [--verbose] |
|
||||
|
||||
## notification
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-notification-battery` | Show the current battery status notification | - |
|
||||
| `blob-notification-time` | Show the current time and date notification | - |
|
||||
| `blob-notification-wait` | Wait for the desktop notification server to accept notifications (hidden) | [timeout-seconds] |
|
||||
| `blob-notification-weather` | Toggle the current weather panel | - |
|
||||
|
||||
## notify
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-notify-dismiss` | Dismiss a notification by summary substring. Used by the first-run notifications to dismiss them after clicking for action. | <summary> |
|
||||
| `blob-notify-send` | Send an Blob desktop notification | [--app-name <app-name>] [-g <glyph>] [-u <low\|normal\|critical>] [-i <icon>] [-t <ms>] [-r <id>] [-p] [--image <path-or-uri>] <headline> [description] [--exec <program> [args...]] |
|
||||
|
||||
## osd
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-osd` | Show the Blob Quickshell on-screen display | [-i\|--icon <icon>] [-m\|--message <text>] [-p\|--progress <0-100>] [-d\|--duration <ms>] |
|
||||
|
||||
## pkg
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `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-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-install` | Show a fuzzy-finder TUI for picking new Arch and OPR packages to install. | - |
|
||||
| `blob-pkg-missing` | Returns true if any of the named packages are missing from the system (or false if they're all there). | <packages...> |
|
||||
| `blob-pkg-present` | Returns true if all of the named packages are installed on the system (or false if any of them are missing). | <packages...> |
|
||||
| `blob-pkg-remove` | Show a fuzzy-finder TUI for picking packages installed on the system to be removed. | - |
|
||||
|
||||
## plugin
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-plugin-add` | Add a shell plugin from git | [git-url] [--enable] [--yes] |
|
||||
| `blob-plugin-catalog` | Emit every first-party and user plugin manifest as JSON (hidden) | - |
|
||||
| `blob-plugin-clone` | Clone a built-in Blob shell plugin into your own config | <source-id> [--edit] |
|
||||
| `blob-plugin-enable` | Enable a shell plugin | <id> [placement] |
|
||||
| `blob-plugin-list` | List discovered shell plugins | [--json] |
|
||||
| `blob-plugin-remove` | Remove an installed shell plugin | [id] [--yes] |
|
||||
| `blob-plugin-validate` | Validate a plugin folder against the Blob plugin manifest schema | <plugin-folder> |
|
||||
|
||||
## plymouth
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-plymouth-set` | Set the Plymouth boot theme colors and logo | <background-hex> <text-hex> <path-to-logo.png> |
|
||||
|
||||
## power
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-power-list` | Returns a list of all the available power profiles on the system. | [--active-state] |
|
||||
|
||||
## powerprofiles
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-powerprofiles-init` | Set the correct power profile on boot based on current AC/battery state. | - |
|
||||
|
||||
## power
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-power-set` | Set and remember the power profile for AC or battery use | [autodetect\|ac\|battery] [power-saver\|balanced\|performance] |
|
||||
|
||||
## refresh
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-refresh-config` | Copy a shipped user config from $BLOB_PATH/config into ~/.config (backs up your version). | <config-path> |
|
||||
| `blob-refresh-hyprland` | Overwrite all the user Hyprland Lua configs in ~/.config/hypr with the Blob defaults. | - |
|
||||
| `blob-refresh-hyprsunset` | Overwrite the user config for hyprsunset with the Blob default and restart the service. | - |
|
||||
| `blob-refresh-plymouth` | Overwrite the user config for the Plymouth drive decryption and boot sequence with the Blob default and rebuild it. | - |
|
||||
| `blob-refresh-shell` | Reset shell.json to Blob defaults | - |
|
||||
|
||||
## reminder
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-reminder` | Set and show lightweight desktop notification reminders | [-i\|--interactive] \| <minutes> [message] \| show [-j\|--json] \| clear |
|
||||
|
||||
## remove
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `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 | - |
|
||||
|
||||
## restart
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-restart-app` | Restart an application by killing it and relaunching via uwsm. | <application-name> [application-args...] |
|
||||
| `blob-restart-btop` | Reload btop configuration (used by the Blob theme switching). | - |
|
||||
| `blob-restart-gum` | Export the current theme's gum styling into the environment (hidden) | - |
|
||||
| `blob-restart-hyprsunset` | Restart the hyprsunset service (used for blue light filtering/night light). | - |
|
||||
| `blob-restart-terminal` | Reload supported terminal emulators after config changes | - |
|
||||
| `blob-restart-trackpad` | Reset the trackpad by unbinding and rebinding its driver. | - |
|
||||
| `blob-restart-xcompose` | Restart the XCompose input method service (fcitx5) to apply new compose key settings. | - |
|
||||
|
||||
## screensaver
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-screensaver` | Run the Blob screensaver using random effects from TTE. | - |
|
||||
|
||||
## secret
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-secret` | - | - |
|
||||
|
||||
## setup
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-setup-direct-boot` | Add or remove an EFI boot entry for the Blob UKI, allowing the system to boot directly | - |
|
||||
| `blob-setup-security-fido2` | Set up FIDO2 authentication for sudo and polkit | - |
|
||||
| `blob-setup-security-fingerprint` | Set up fingerprint authentication for sudo, polkit, and lock screen | - |
|
||||
| `blob-setup-security-sshd` | Set up the OpenSSH server, open the firewall, and authorize an SSH key | [--key=<public-key>] |
|
||||
| `blob-setup-security-sudoless-docker` | Enable sudoless Docker by adding your user to the docker group (root-equivalent!) | - |
|
||||
|
||||
## shell
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-shell` | Send an IPC call to the running Blob shell | [-q] <target> <method> [args...] |
|
||||
| `blob-shell-config` | Shared helpers for editing ~/.config/blob/shell.json (source this, don't run it). (hidden) | - |
|
||||
| `blob-shell-restart` | Restart the Blob shell | - |
|
||||
|
||||
## show
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-show-done` | Display a "Done!" message and wait for user to press any key. | - |
|
||||
| `blob-show-logo` | Display the Blob logo in the terminal using green color. | - |
|
||||
|
||||
## state
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-state` | Manage persistent state files for Blob toggles and settings. (hidden) | <set\|clear> <state-name-or-pattern> |
|
||||
|
||||
## sudo
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-sudo-docker` | Succeed when Docker needs sudo, fail when it can be used directly (hidden) | [--configured] |
|
||||
| `blob-sudo-keepalive` | Prompt for sudo once and keep the credential alive in the background. | - |
|
||||
| `blob-sudo-passwordless` | Toggle passwordless sudo for the current user. | [MINUTES] |
|
||||
|
||||
## system
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-system-lid` | Lock and reconcile displays when the laptop lid closes (hidden) | - |
|
||||
| `blob-system-lock` | Lock the computer and turn off the display | - |
|
||||
| `blob-system-logout` | Log out after closing application windows | - |
|
||||
| `blob-system-reboot` | Reboot after closing application windows | - |
|
||||
| `blob-system-shutdown` | Shut down after closing application windows | - |
|
||||
| `blob-system-sleep` | Lock before suspend and wait for the session lock to become secure (hidden) | - |
|
||||
| `blob-system-sleep-monitor` | Monitor sleep preparation and lock before suspend (hidden) | - |
|
||||
| `blob-system-stats` | Print CPU and memory stats for the shell | [--bar-widget] |
|
||||
| `blob-system-wake` | Wake displays and restore brightness after idle | - |
|
||||
|
||||
## tablet
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-tablet-follow` | - | - |
|
||||
|
||||
## theme
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-theme-browser` | Apply the current theme color to Chromium, Chrome, Edge, and Brave (hidden) | - |
|
||||
| `blob-theme-browser-policy` | Write the current theme color into the browser policy directories (hidden) | <rrggbb> |
|
||||
| `blob-theme-color` | Resolve semantic colors from an Blob theme colors.toml (hidden) | [--file <colors.toml>] (--all \| --raw \| <key> [fallback]) |
|
||||
| `blob-theme-contrast` | - | - |
|
||||
| `blob-theme-current` | Show current theme | - |
|
||||
| `blob-theme-dir` | Print the directory holding a theme, preferring a user-installed copy | <theme-name> |
|
||||
| `blob-theme-dynamic` | Apply the pywal-generated theme built from the current wallpaper | - |
|
||||
| `blob-theme-extras` | List the user-installed themes that came from a git clone | - |
|
||||
| `blob-theme-foot` | Apply current Blob theme colors to running Foot terminals (hidden) | - |
|
||||
| `blob-theme-import` | Generate a theme's colors.toml from its alacritty.toml palette (hidden) | <theme-dir> |
|
||||
| `blob-theme-install` | Install a theme from a git repository | [git-repo-url] |
|
||||
| `blob-theme-list` | List available themes | - |
|
||||
| `blob-theme-menu` | Pick a color theme from the local and bundled sets | [--mode\|--print] |
|
||||
| `blob-theme-osc` | Print OSC sequences for an Blob color theme (hidden) | - |
|
||||
| `blob-theme-refresh` | Refresh the current theme from its templates. | - |
|
||||
| `blob-theme-remove` | Remove a user-installed theme | [theme-name] |
|
||||
| `blob-theme-set` | Apply an Blob theme | <theme-name> |
|
||||
| `blob-theme-share` | Fetch a shared theme by link or id and apply it as blob-dynamic | <share-link-or-id> |
|
||||
| `blob-theme-switcher` | Open the Blob theme switcher | - |
|
||||
| `blob-theme-templates` | Generate themed config files from Blob templates (hidden) | - |
|
||||
| `blob-theme-update` | Update user-installed git themes | - |
|
||||
|
||||
## toggle
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-toggle` | Toggle Blob features between enabled and disabled | <flag-name> [toggle\|on\|off] |
|
||||
| `blob-toggle-bar` | Toggle bar visibility without killing the Blob shell | [toggle\|on\|off] |
|
||||
| `blob-toggle-crash-capture` | Toggle crash capture notifications | - |
|
||||
| `blob-toggle-enabled` | Check if a toggle is enabled (flag file exists) | <flag-name> |
|
||||
| `blob-toggle-glass` | - | - |
|
||||
| `blob-toggle-hybrid-gpu` | Toggle dedicated vs integrated GPU mode via supergfxd (for hybrid gpu laptops, like Asus G14). | - |
|
||||
| `blob-toggle-idle` | Toggle idle behavior so the system either idles normally or stays awake | [toggle\|stay-awake\|allow-idle\|status] |
|
||||
| `blob-toggle-input` | Enable, disable, or toggle a Hyprland input device (hidden) | <touchpad\|touchscreen> [on\|off\|toggle] |
|
||||
| `blob-toggle-nightlight` | Toggle nightlight screen temperature | [--status] |
|
||||
| `blob-toggle-screensaver` | Toggle screensaver availability | - |
|
||||
| `blob-toggle-silencing` | Toggle notification do-not-disturb mode | - |
|
||||
| `blob-toggle-tablet` | - | - |
|
||||
| `blob-toggle-touchpad` | Enable, disable, or toggle the touchpad | [on\|off\|toggle] |
|
||||
| `blob-toggle-touchscreen` | Enable, disable, or toggle the touch functionality of the screen | [on\|off\|toggle] |
|
||||
|
||||
## transcode
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-transcode` | Transcode pictures and videos for sharing | [--path path] [input] [format] [resolution] |
|
||||
| `blob-transcode-ascii` | Transcode an image into ASCII/Unicode art text | <input-image.svg\|png> <output-path> [--width <columns>] [--height <rows>] [--mode <braille\|block>] [--threshold <percent>] [--invert] |
|
||||
|
||||
## tui
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-tui-install` | Create a desktop launcher for a terminal UI app | [name command window-style icon-url-or-name] |
|
||||
| `blob-tui-remove` | Remove a terminal UI desktop launcher | [name] |
|
||||
| `blob-tui-remove-all` | Remove all TUIs installed via blob-tui-install. | - |
|
||||
|
||||
## update
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-update` | Update system and AUR packages | [--no-confirm] |
|
||||
| `blob-update-available` | Print the number of pending package updates (hidden) | - |
|
||||
| `blob-update-firmware` | Update system firmware using fwupd. Ensures the fwupd EFI binary is installed | - |
|
||||
| `blob-update-time` | Restart system time synchronization | - |
|
||||
|
||||
## wallpaper
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-wallpaper-set` | Set a wallpaper from ~/wallpapers and recolor the desktop from it | [--menu\|<file>] |
|
||||
|
||||
## weather
|
||||
|
||||
| Command | Does | Arguments |
|
||||
| --- | --- | --- |
|
||||
| `blob-weather-card` | Print a one-line weather summary for the quick settings card (hidden) | - |
|
||||
| `blob-weather-icon` | Returns a weather condition icon, adjusted for live sunrise and sunset. | - |
|
||||
| `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. | - |
|
||||
|
||||
## Totals
|
||||
|
||||
252 commands.
|
||||
@@ -0,0 +1,202 @@
|
||||
# Keybindings
|
||||
|
||||
Generated by `blob-docs-keybinds` from the `o.bind` calls in the Hyprland
|
||||
Lua files. Do not edit by hand.
|
||||
|
||||
Personal overrides in `hypr/bindings.lua` win over the defaults, and
|
||||
`hl.unbind` in that file removes a default outright.
|
||||
|
||||
## Personal
|
||||
|
||||
| Keys | Action |
|
||||
| --- | --- |
|
||||
| `SUPER + ALT + SPACE` | Blob menu |
|
||||
| `SUPER + ALT + W` | Wallpaper picker |
|
||||
| `SUPER + CTRL + M` | System monitor |
|
||||
| `SUPER + CTRL + Q` | Quick settings |
|
||||
| `SUPER + SPACE` | Apps menu |
|
||||
|
||||
## Applications
|
||||
|
||||
| Keys | Action |
|
||||
| --- | --- |
|
||||
| `SUPER + ALT + SHIFT + F` | File manager (cwd) |
|
||||
| `SUPER + RETURN` | Terminal |
|
||||
| `SUPER + SHIFT + ALT + B` | Browser (private) |
|
||||
| `SUPER + SHIFT + B` | Browser |
|
||||
| `SUPER + SHIFT + D` | Docker |
|
||||
| `SUPER + SHIFT + F` | File manager |
|
||||
| `SUPER + SHIFT + G` | Signal |
|
||||
| `SUPER + SHIFT + M` | Music |
|
||||
| `SUPER + SHIFT + N` | Editor |
|
||||
| `SUPER + SHIFT + O` | Obsidian |
|
||||
| `SUPER + SHIFT + RETURN` | Browser |
|
||||
|
||||
## Tiling
|
||||
|
||||
| Keys | Action |
|
||||
| --- | --- |
|
||||
| `ALT + SHIFT + TAB` | Focus on previous window |
|
||||
| `ALT + TAB` | Focus on next window |
|
||||
| `CTRL + ALT + DELETE` | Close all windows |
|
||||
| `CTRL + ALT + SHIFT + TAB` | Focus on previous monitor |
|
||||
| `CTRL + ALT + TAB` | Focus on next monitor |
|
||||
| `SUPER + ALT + code:20` | Expand window left a little |
|
||||
| `SUPER + ALT + code:21` | Shrink window left a little |
|
||||
| `SUPER + ALT + DOWN` | Move window to group on bottom |
|
||||
| `SUPER + ALT + F` | Full width |
|
||||
| `SUPER + ALT + G` | Move active window out of group |
|
||||
| `SUPER + ALT + Home` | Save window width |
|
||||
| `SUPER + ALT + LEFT` | Move window to group on left |
|
||||
| `SUPER + ALT + mouse_down` | Next window in group |
|
||||
| `SUPER + ALT + mouse_up` | Previous window in group |
|
||||
| `SUPER + ALT + RIGHT` | Move window to group on right |
|
||||
| `SUPER + ALT + S` | Move window to scratchpad |
|
||||
| `SUPER + ALT + SHIFT + TAB` | Previous window in group |
|
||||
| `SUPER + ALT + SLASH` | Monitor scaling down |
|
||||
| `SUPER + ALT + TAB` | Next window in group |
|
||||
| `SUPER + ALT + UP` | Move window to group on top |
|
||||
| `SUPER + code:20` | Expand window left |
|
||||
| `SUPER + code:21` | Shrink window left |
|
||||
| `SUPER + CTRL + code:20` | Expand window left a lot |
|
||||
| `SUPER + CTRL + code:21` | Shrink window left a lot |
|
||||
| `SUPER + CTRL + F` | Tiled full screen |
|
||||
| `SUPER + CTRL + LEFT` | Move grouped window focus left |
|
||||
| `SUPER + CTRL + RIGHT` | Move grouped window focus right |
|
||||
| `SUPER + CTRL + SHIFT + code:20` | Shrink window up a lot |
|
||||
| `SUPER + CTRL + SHIFT + code:21` | Expand window down a lot |
|
||||
| `SUPER + CTRL + TAB` | Former workspace |
|
||||
| `SUPER + DOWN` | Focus on below window |
|
||||
| `SUPER + F` | Full screen |
|
||||
| `SUPER + G` | Toggle window grouping |
|
||||
| `SUPER + Home` | Restore window width |
|
||||
| `SUPER + J` | Toggle window split |
|
||||
| `SUPER + L` | Toggle workspace layout |
|
||||
| `SUPER + LEFT` | Focus on left window |
|
||||
| `SUPER + mouse:272` | Move window |
|
||||
| `SUPER + mouse:273` | Resize window |
|
||||
| `SUPER + mouse_down` | Scroll active workspace forward |
|
||||
| `SUPER + mouse_up` | Scroll active workspace backward |
|
||||
| `SUPER + O` | Pop window out (float & pin) |
|
||||
| `SUPER + P` | Pseudo window |
|
||||
| `SUPER + RIGHT` | Focus on right window |
|
||||
| `SUPER + S` | Toggle scratchpad |
|
||||
| `SUPER + SHIFT + ALT + code:20` | Shrink window up a little |
|
||||
| `SUPER + SHIFT + ALT + code:21` | Expand window down a little |
|
||||
| `SUPER + SHIFT + ALT + DOWN` | Move workspace to down monitor |
|
||||
| `SUPER + SHIFT + ALT + LEFT` | Move workspace to left monitor |
|
||||
| `SUPER + SHIFT + ALT + RIGHT` | Move workspace to right monitor |
|
||||
| `SUPER + SHIFT + ALT + UP` | Move workspace to up monitor |
|
||||
| `SUPER + SHIFT + code:20` | Shrink window up |
|
||||
| `SUPER + SHIFT + code:21` | Expand window down |
|
||||
| `SUPER + SHIFT + DOWN` | Swap window down |
|
||||
| `SUPER + SHIFT + LEFT` | Swap window to the left |
|
||||
| `SUPER + SHIFT + RIGHT` | Swap window to the right |
|
||||
| `SUPER + SHIFT + TAB` | Previous workspace |
|
||||
| `SUPER + SHIFT + UP` | Swap window up |
|
||||
| `SUPER + SLASH` | Monitor scaling up |
|
||||
| `SUPER + T` | Toggle window floating/tiling |
|
||||
| `SUPER + TAB` | Next workspace |
|
||||
| `SUPER + UP` | Focus on above window |
|
||||
| `SUPER + W` | Close window |
|
||||
|
||||
## Media
|
||||
|
||||
| Keys | Action |
|
||||
| --- | --- |
|
||||
| `ALT + SHIFT + XF86AudioPlay` | Previous track |
|
||||
| `ALT + XF86AudioLowerVolume` | Volume down precise |
|
||||
| `ALT + XF86AudioPlay` | Next track |
|
||||
| `ALT + XF86AudioRaiseVolume` | Volume up precise |
|
||||
| `ALT + XF86MonBrightnessDown` | Brightness down precise |
|
||||
| `ALT + XF86MonBrightnessUp` | Brightness up precise |
|
||||
| `SHIFT + XF86AudioMute` | Switch audio output |
|
||||
| `SHIFT + XF86AudioPause` | Switch media source |
|
||||
| `SHIFT + XF86AudioPlay` | Switch media source |
|
||||
| `SHIFT + XF86MonBrightnessDown` | Brightness minimum |
|
||||
| `SHIFT + XF86MonBrightnessUp` | Brightness maximum |
|
||||
| `XF86AudioLowerVolume` | Volume down |
|
||||
| `XF86AudioMicMute` | Mute microphone |
|
||||
| `XF86AudioMute` | Mute |
|
||||
| `XF86AudioNext` | Next track |
|
||||
| `XF86AudioPause` | Pause |
|
||||
| `XF86AudioPlay` | Play |
|
||||
| `XF86AudioPrev` | Previous track |
|
||||
| `XF86AudioRaiseVolume` | Volume up |
|
||||
| `XF86Eject` | Eject media |
|
||||
| `XF86KbdBrightnessDown` | Keyboard brightness down |
|
||||
| `XF86KbdBrightnessUp` | Keyboard brightness up |
|
||||
| `XF86KbdLightOnOff` | Keyboard backlight cycle |
|
||||
| `XF86MonBrightnessDown` | Brightness down |
|
||||
| `XF86MonBrightnessUp` | Brightness up |
|
||||
| `XF86TouchpadOff` | Disable touchpad |
|
||||
| `XF86TouchpadOn` | Enable touchpad |
|
||||
| `XF86TouchpadToggle` | Toggle touchpad |
|
||||
|
||||
## Clipboard
|
||||
|
||||
| Keys | Action |
|
||||
| --- | --- |
|
||||
| `SUPER + C` | Universal copy |
|
||||
| `SUPER + CTRL + V` | Clipboard manager |
|
||||
| `SUPER + V` | Universal paste |
|
||||
| `SUPER + X` | Universal cut |
|
||||
|
||||
## Utilities
|
||||
|
||||
| Keys | Action |
|
||||
| --- | --- |
|
||||
| `ALT + PRINT` | Screenrecording |
|
||||
| `PRINT` | Screenshot |
|
||||
| `SUPER + ALT + code:34` | Make webcam overlay smaller |
|
||||
| `SUPER + ALT + code:35` | Make webcam overlay larger |
|
||||
| `SUPER + ALT + comma` | Invoke last notification |
|
||||
| `SUPER + ALT + SPACE` | Apps menu |
|
||||
| `SUPER + BACKSPACE` | Toggle window transparency |
|
||||
| `SUPER + comma` | Dismiss last notification |
|
||||
| `SUPER + CTRL + A` | Audio |
|
||||
| `SUPER + CTRL + ALT + B` | Show battery remaining |
|
||||
| `SUPER + CTRL + ALT + D` | Calendar |
|
||||
| `SUPER + CTRL + ALT + Delete` | Toggle laptop display mirroring |
|
||||
| `SUPER + CTRL + ALT + R` | Show reminders |
|
||||
| `SUPER + CTRL + ALT + T` | Show time |
|
||||
| `SUPER + CTRL + ALT + W` | Toggle weather |
|
||||
| `SUPER + CTRL + ALT + Z` | Reset zoom |
|
||||
| `SUPER + CTRL + B` | Bluetooth |
|
||||
| `SUPER + CTRL + BACKSPACE` | Toggle single-window square aspect |
|
||||
| `SUPER + CTRL + C` | Capture menu |
|
||||
| `SUPER + CTRL + comma` | Toggle silencing notifications |
|
||||
| `SUPER + CTRL + D` | Display |
|
||||
| `SUPER + CTRL + Delete` | Toggle laptop display |
|
||||
| `SUPER + CTRL + E` | Emojis |
|
||||
| `SUPER + CTRL + H` | Hardware menu |
|
||||
| `SUPER + CTRL + I` | Toggle locking on idle |
|
||||
| `SUPER + CTRL + L` | Lock system |
|
||||
| `SUPER + CTRL + N` | Toggle nightlight |
|
||||
| `SUPER + CTRL + O` | Toggle menu |
|
||||
| `SUPER + CTRL + P` | Power |
|
||||
| `SUPER + CTRL + PERIOD` | Transcode |
|
||||
| `SUPER + CTRL + PRINT` | Extract text (OCR) from screenshot |
|
||||
| `SUPER + CTRL + R` | Set reminder |
|
||||
| `SUPER + CTRL + S` | Share |
|
||||
| `SUPER + CTRL + SPACE` | Background switcher |
|
||||
| `SUPER + CTRL + T` | Activity |
|
||||
| `SUPER + CTRL + W` | Network |
|
||||
| `SUPER + CTRL + Z` | Zoom in |
|
||||
| `SUPER + ESCAPE` | System menu |
|
||||
| `SUPER + K` | Keybindings |
|
||||
| `SUPER + PRINT` | Color picker |
|
||||
| `SUPER + SHIFT + ALT + comma` | Open notification history |
|
||||
| `SUPER + SHIFT + BACKSPACE` | Toggle window gaps |
|
||||
| `SUPER + SHIFT + code:201` | Blob menu |
|
||||
| `SUPER + SHIFT + comma` | Dismiss all notifications |
|
||||
| `SUPER + SHIFT + CTRL + R` | Clear reminders |
|
||||
| `SUPER + SHIFT + CTRL + SPACE` | Theme menu |
|
||||
| `SUPER + SHIFT + SPACE` | Toggle top bar |
|
||||
| `SUPER + SPACE` | Blob menu |
|
||||
| `XF86PowerOff` | Power menu |
|
||||
|
||||
## Unbound defaults
|
||||
|
||||
- `SUPER + SPACE`
|
||||
- `SUPER + ALT + SPACE`
|
||||
@@ -0,0 +1,91 @@
|
||||
# The shell
|
||||
|
||||
`blob-shell` is one long-running Quickshell process hosting the bar, panels,
|
||||
notifications, lock screen, OSD, and both menus. Everything runs inside it as a
|
||||
plugin, so summoning a panel is an IPC call into a process that is already up.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
shell/
|
||||
shell.qml entry point
|
||||
Commons/ Style, Color, Border, Util
|
||||
Ui/ shared components
|
||||
services/ plugin registry, bar widget registry, app library
|
||||
plugins/
|
||||
bar/ the bar and its widgets
|
||||
menu/ both menus
|
||||
notifications/ daemon, popups, DND, history
|
||||
notification-center/
|
||||
lock/ polkit/ osd/ background/ clipboard/ emojis/
|
||||
image-picker/ reminders/
|
||||
panels/ audio bluetooth clock disk-speedtest monitor network
|
||||
power quick-settings speedtest sysmon weather wifiqr
|
||||
services/ battery idle media nightlight
|
||||
```
|
||||
|
||||
First-party plugins are discovered under `shell/plugins` at a depth of two or
|
||||
three and are enabled without being listed anywhere. Third-party plugins live in
|
||||
`~/.config/blob/plugins/<id>/` and are enabled through `shell.json`.
|
||||
|
||||
## Config
|
||||
|
||||
`shell.json` is the whole bar config. It hot-reloads on save, and dragging a bar
|
||||
widget rewrites `~/.config/blob/shell.json` directly, so after rearranging by
|
||||
hand copy it back:
|
||||
|
||||
```
|
||||
cp ~/.config/blob/shell.json shell.json
|
||||
```
|
||||
|
||||
`./install.sh --check` reports the drift.
|
||||
|
||||
Defaults live at `config/blob/shell.json`. A valid user file replaces them
|
||||
entirely rather than merging.
|
||||
|
||||
## Bar layout
|
||||
|
||||
| Section | Widgets |
|
||||
| --- | --- |
|
||||
| left | `blob.menu`, `blob.workspaces` |
|
||||
| center | `blob.clock` (anchored), `blob.system-update` |
|
||||
| right | `blob.tray`, `blob.bluetooth`, `blob.network`, `blob.audio`, `blob.monitor`, `blob.power`, `blob.notifications` |
|
||||
|
||||
`blob.clock` and `blob.notifications` are `type: "command"` modules, which is how
|
||||
they carry their own click actions: the clock opens quick settings on left click,
|
||||
the shell's calendar on middle, and the timezone picker on right; the bell opens
|
||||
the notification centre on left and toggles silencing on right.
|
||||
|
||||
The centre module named by `centerAnchor` cannot be dragged out of the centre.
|
||||
Every other widget still reorders.
|
||||
|
||||
## Differences from upstream
|
||||
|
||||
This shell was forked from Omarchy 4.0.4 and is not tracked against it. The
|
||||
changes beyond renaming:
|
||||
|
||||
- `agents`, `tailscale`, `dropbox`, and `dev-gallery` plugins removed, about
|
||||
7,000 lines.
|
||||
- The `Dictation` bar indicator removed with voice typing.
|
||||
- Persistent workspaces 1-9 instead of 1-5.
|
||||
- Menu cards 440 wide instead of 300, with the Blob icon on the bar button.
|
||||
- The lock screen carries `branding/screensaver.txt` and an AGS-style password
|
||||
field: square corners, a 2px border on the blue slot at half alpha going to
|
||||
solid accent once typing starts, and urgent on a failed attempt.
|
||||
- `quick-settings`, `sysmon`, and `notification-center` are new, ported from the
|
||||
GTK widgets. See [widgets.md](widgets.md).
|
||||
|
||||
None of these are clones or overrides. They are edits to first-party source, so
|
||||
there is no clone machinery and nothing to re-apply after an upstream release.
|
||||
The fork base is recorded in [upstream.md](upstream.md).
|
||||
|
||||
## Debugging
|
||||
|
||||
```
|
||||
journalctl --user -t blob-shell -f
|
||||
blob-shell-restart
|
||||
```
|
||||
|
||||
A QML error in a `service` plugin means that service never loads. For the lock
|
||||
that leaves the machine unlockable rather than locked open, so test it with
|
||||
`blob-shell lock lock` before trusting a change to it.
|
||||
@@ -0,0 +1,68 @@
|
||||
# Themes
|
||||
|
||||
A theme is a directory under `themes/` holding at minimum a `colors.toml`.
|
||||
`blob-theme-set <name>` stages it, renders every template against it, and
|
||||
retints the running desktop.
|
||||
|
||||
## What a theme can ship
|
||||
|
||||
| File | Used by |
|
||||
| --- | --- |
|
||||
| `colors.toml` | required; the palette everything else derives from |
|
||||
| `backgrounds/` | the background switcher |
|
||||
| `shell.lock.toml` | lock screen surface tokens |
|
||||
| `neovim.lua` | the editor colorscheme |
|
||||
| `icons.theme` | GTK icon theme name |
|
||||
| `preview.png`, `unlock.png`, `preview-unlock.png` | theme and lock previews |
|
||||
|
||||
## colors.toml
|
||||
|
||||
Named keys are preferred: `background`, `foreground`, `accent`, `muted`,
|
||||
`selection`, the eight base colors, and their `bright_` variants. The pywal
|
||||
`color0`..`color15` form also works and is resolved through the same cascade:
|
||||
`color0` is the background, `color4` is blue, `color5` is magenta.
|
||||
|
||||
That cascade lives in `blob-theme-color`, and `Commons/Color.qml` mirrors it so
|
||||
the shell and the CLI never disagree about what a slot means.
|
||||
|
||||
## Templates
|
||||
|
||||
`default/themed/*.tpl` are rendered per theme into
|
||||
`~/.local/state/blob/current/theme/`. Nine ship: foot, kitty, btop, chromium,
|
||||
hyprland, the screenshare picker, neovim, `shell.toml`, and zen.
|
||||
|
||||
An app picks its colors up in one of two ways. Most include the generated file
|
||||
directly, so nothing has to run:
|
||||
|
||||
```
|
||||
include ~/.local/state/blob/current/theme/kitty.conf
|
||||
```
|
||||
|
||||
Zen does the same through a CSS import in `userChrome.css`. The rest are pushed
|
||||
by a short applier listed in `post_theme_commands` inside `blob-theme-set`:
|
||||
`blob-theme-foot`, `blob-theme-browser`, `blob-restart-terminal`,
|
||||
`blob-restart-btop`, `blob-hypr-restart`.
|
||||
|
||||
Adding a template for another app means dropping a `.tpl` in `default/themed/`
|
||||
and, if it cannot include a file, adding an applier to that list.
|
||||
|
||||
## The three ways to set a theme
|
||||
|
||||
| Command | Does |
|
||||
| --- | --- |
|
||||
| `blob theme menu` | pick from the bundled and local sets |
|
||||
| `blob theme set <name>` | apply one by name |
|
||||
| `blob wallpaper set <image>` | extract a palette from a wallpaper with pywal, fix flat palettes with `blob-theme-contrast`, and apply it as `blob-dynamic` |
|
||||
| `blob theme share <link>` | fetch a shared palette and apply it as `blob-dynamic` |
|
||||
|
||||
Aether is also installed and does the same job with a GUI. To have its palettes
|
||||
land where these do, add Blob as a custom app in Aether with a template writing
|
||||
`colors.toml` into `~/.local/state/blob/current/theme/` and a post-apply hook
|
||||
calling `blob-theme-refresh`.
|
||||
|
||||
## Bundled themes
|
||||
|
||||
Twenty-two came across from upstream, plus `flats` and `pitch-dark`. Their
|
||||
`backgrounds/` directories are empty on purpose: the upstream images were
|
||||
Omarchy branding, and `~/wallpapers` holds 103 of your own. A theme with no
|
||||
background of its own leaves the current wallpaper alone.
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This hook is called with the current battery percentage when the low battery
|
||||
# notification is sent. To put it into use, remove .sample from the name.
|
||||
|
||||
SOUND_FILE="/usr/share/sounds/freedesktop/stereo/dialog-warning.oga"
|
||||
|
||||
if blob-cmd-present mpv && [[ -f $SOUND_FILE ]]; then
|
||||
mpv --no-video "$SOUND_FILE" >/dev/null 2>&1
|
||||
fi
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This hook is called with the snake-cased name of the font that has just been set.
|
||||
# To put it into use, remove .sample from the name.
|
||||
|
||||
# Example: Show the name of the theme that was just set.
|
||||
# notify-send -u low "New font" "Your new font is $1"
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This hook is called after an Blob system update has been performed.
|
||||
# To put it into use, remove .sample from the name.
|
||||
|
||||
# Example: Show notification after the system has been updated.
|
||||
# notify-send -u low "Update Performed" "Your system is now up to date"
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This hook is called with the snake-cased name of the theme that has just been set.
|
||||
# To put it into use, remove .sample from the name.
|
||||
|
||||
# Example: Show the name of the theme that was just set.
|
||||
# notify-send -u low "New theme" "Your new theme is $1"
|
||||
Executable
+196
@@ -0,0 +1,196 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
blob_path="$HOME/.local/share/blob"
|
||||
config_dir="$HOME/.config"
|
||||
state_dir="$HOME/.local/state/blob"
|
||||
session_dir="/usr/share/wayland-sessions"
|
||||
font_dir="$HOME/.local/share/fonts"
|
||||
|
||||
force=false
|
||||
check=false
|
||||
changes=0
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: ./install.sh [OPTIONS]
|
||||
|
||||
--check Report what would change, write nothing
|
||||
--force Overwrite files that have local changes
|
||||
--help Show this message
|
||||
USAGE
|
||||
exit 0
|
||||
}
|
||||
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
--force) force=true; shift ;;
|
||||
--check) check=true; shift ;;
|
||||
--help) usage ;;
|
||||
*) echo "Unknown option: $1" >&2; usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
say() {
|
||||
printf '%s\n' "$1"
|
||||
}
|
||||
|
||||
note_change() {
|
||||
changes=$((changes + 1))
|
||||
}
|
||||
|
||||
# BLOB_PATH is a symlink to this checkout rather than a copy, so bin/, shell/,
|
||||
# themes/ and default/ are always the working tree. Every path the shell
|
||||
# resolves as $BLOB_PATH/... therefore needs no install step at all.
|
||||
link_blob_path() {
|
||||
local current=""
|
||||
[[ -L $blob_path ]] && current="$(readlink -f "$blob_path")"
|
||||
|
||||
if [[ $current == "$repo_dir" ]]; then
|
||||
say "ok BLOB_PATH -> $repo_dir"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -e $blob_path && ! -L $blob_path ]]; then
|
||||
say "WARN $blob_path exists and is not a symlink; move it aside first"
|
||||
note_change
|
||||
return
|
||||
fi
|
||||
|
||||
note_change
|
||||
if [[ $check == true ]]; then
|
||||
say "would link BLOB_PATH -> $repo_dir"
|
||||
return
|
||||
fi
|
||||
|
||||
ln -sfn "$repo_dir" "$blob_path"
|
||||
say "link BLOB_PATH -> $repo_dir"
|
||||
}
|
||||
|
||||
# Copy a file only when the destination is missing or matches what we last
|
||||
# wrote. A destination that differs is a local edit, reported and kept unless
|
||||
# --force says otherwise.
|
||||
install_file() {
|
||||
local source="$1" dest="$2" label="$3"
|
||||
|
||||
if [[ ! -e $source ]]; then
|
||||
say "SKIP $label (missing in repo)"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -e $dest ]] && cmp -s "$source" "$dest"; then
|
||||
say "ok $label"
|
||||
return
|
||||
fi
|
||||
|
||||
note_change
|
||||
|
||||
if [[ -e $dest ]] && [[ $force == false ]]; then
|
||||
say "DIFF $label (local changes kept; --force to overwrite)"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ $check == true ]]; then
|
||||
say "would write $label"
|
||||
return
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$dest")"
|
||||
[[ -e $dest ]] && cp "$dest" "$dest.bak"
|
||||
cp "$source" "$dest"
|
||||
say "write $label"
|
||||
}
|
||||
|
||||
install_tree() {
|
||||
local source="$1" dest="$2" label="$3"
|
||||
|
||||
if [[ ! -d $source ]]; then
|
||||
say "SKIP $label (missing in repo)"
|
||||
return
|
||||
fi
|
||||
|
||||
local relative
|
||||
while IFS= read -r relative; do
|
||||
install_file "$source/$relative" "$dest/$relative" "$label/$relative"
|
||||
done < <(cd "$source" && find . -type f -printf '%P\n' | sort)
|
||||
}
|
||||
|
||||
install_session_entry() {
|
||||
local source="$repo_dir/session/blob.desktop"
|
||||
local dest="$session_dir/blob.desktop"
|
||||
|
||||
if [[ -e $dest ]] && cmp -s "$source" "$dest"; then
|
||||
say "ok wayland session entry"
|
||||
return
|
||||
fi
|
||||
|
||||
note_change
|
||||
if [[ $check == true ]]; then
|
||||
say "would install wayland session entry (needs sudo)"
|
||||
return
|
||||
fi
|
||||
|
||||
sudo install -Dm644 "$source" "$dest"
|
||||
say "write wayland session entry"
|
||||
}
|
||||
|
||||
say "Blob installer"
|
||||
say "repo: $repo_dir"
|
||||
if [[ $check == true ]]; then
|
||||
say "mode: check (nothing will be written)"
|
||||
fi
|
||||
say ""
|
||||
|
||||
link_blob_path
|
||||
|
||||
if [[ $check == false ]]; then
|
||||
mkdir -p "$state_dir"/{toggles/hypr,indicators,notifications}
|
||||
mkdir -p "$config_dir/blob"/{hooks,extensions,plugins,themed,themes}
|
||||
mkdir -p "$HOME/wallpapers"
|
||||
fi
|
||||
|
||||
install_tree "$repo_dir/hypr" "$config_dir/hypr" "hypr"
|
||||
# config/ is the shipped-defaults directory. Everything in it is reachable as
|
||||
# $BLOB_PATH/config/... so `blob-refresh-config` can reset a file, and most of it
|
||||
# is also the right thing to deploy on a fresh install. Two subdirectories are
|
||||
# reference-only and must not be copied into ~/.config:
|
||||
#
|
||||
# blob/ the shell defaults; copying them over ~/.config/blob/shell.json would
|
||||
# replace the user's bar layout with the stock one
|
||||
# hypr/ the stock Hyprland config; the personal hypr/ below is authoritative
|
||||
# and is installed to the same place
|
||||
for entry in "$repo_dir"/config/*; do
|
||||
entry_name="$(basename "$entry")"
|
||||
[[ $entry_name == blob || $entry_name == hypr ]] && continue
|
||||
if [[ -d $entry ]]; then
|
||||
install_tree "$entry" "$config_dir/$entry_name" "config/$entry_name"
|
||||
else
|
||||
install_file "$entry" "$config_dir/$entry_name" "config/$entry_name"
|
||||
fi
|
||||
done
|
||||
install_tree "$repo_dir/hooks" "$config_dir/blob/hooks" "hooks"
|
||||
install_tree "$repo_dir/branding" "$config_dir/blob/branding" "branding"
|
||||
install_file "$repo_dir/shell.json" "$config_dir/blob/shell.json" "shell.json"
|
||||
install_file "$repo_dir/session/uwsm/default" "$config_dir/uwsm/default" "uwsm/default"
|
||||
install_file "$repo_dir/session/uwsm/env.d/10-blob" "$config_dir/uwsm/env.d/10-blob" "uwsm/env.d/10-blob"
|
||||
install_file "$repo_dir/fonts/omarchy.ttf" "$font_dir/omarchy.ttf" "fonts/omarchy.ttf"
|
||||
install_session_entry
|
||||
|
||||
zen_profile="$(find "$config_dir/zen" -maxdepth 1 -type d -name '*.Default (release)*' 2>/dev/null | head -1)"
|
||||
if [[ -n $zen_profile ]]; then
|
||||
install_file "$repo_dir/zen/userChrome.css" "$zen_profile/chrome/userChrome.css" "zen/userChrome.css"
|
||||
else
|
||||
say "SKIP zen/userChrome.css (no Zen profile found)"
|
||||
fi
|
||||
|
||||
say ""
|
||||
if [[ $check == true ]]; then
|
||||
say "$changes item(s) would change."
|
||||
(( changes == 0 )) || exit 1
|
||||
else
|
||||
say "$changes item(s) changed."
|
||||
say ""
|
||||
say "Next: log out and pick the Blob session, then run 'blob theme set <name>'."
|
||||
fi
|
||||
Executable
+94
@@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
blob_path="$HOME/.local/share/blob"
|
||||
config_dir="$HOME/.config"
|
||||
session_entry="/usr/share/wayland-sessions/blob.desktop"
|
||||
|
||||
keep_state=false
|
||||
assume_yes=false
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: ./uninstall.sh [OPTIONS]
|
||||
|
||||
Removes what install.sh wrote. The checkout itself is never touched.
|
||||
|
||||
--keep-state Leave ~/.local/state/blob in place
|
||||
--yes Do not prompt
|
||||
--help Show this message
|
||||
USAGE
|
||||
exit 0
|
||||
}
|
||||
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
--keep-state) keep_state=true; shift ;;
|
||||
--yes) assume_yes=true; shift ;;
|
||||
--help) usage ;;
|
||||
*) echo "Unknown option: $1" >&2; usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
say() {
|
||||
printf '%s\n' "$1"
|
||||
}
|
||||
|
||||
restore_or_remove() {
|
||||
local path="$1" label="$2"
|
||||
|
||||
if [[ -e $path.bak ]]; then
|
||||
mv "$path.bak" "$path"
|
||||
say "restore $label (from .bak)"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -e $path ]]; then
|
||||
rm -f "$path"
|
||||
say "remove $label"
|
||||
fi
|
||||
}
|
||||
|
||||
say "This removes the Blob session, its config, and the BLOB_PATH symlink."
|
||||
say "Your checkout, wallpapers, and themes stay where they are."
|
||||
if [[ $keep_state == false ]]; then
|
||||
say "It also removes ~/.local/state/blob (active theme, toggles, notifications)."
|
||||
fi
|
||||
say ""
|
||||
|
||||
if [[ $assume_yes == false ]]; then
|
||||
read -rp "Continue? [y/N] " answer
|
||||
[[ $answer == [yY] ]] || exit 0
|
||||
fi
|
||||
|
||||
if [[ -L $blob_path ]]; then
|
||||
rm -f "$blob_path"
|
||||
say "remove BLOB_PATH symlink"
|
||||
fi
|
||||
|
||||
for relative in blob/shell.json uwsm/default uwsm/env.d/10-blob; do
|
||||
restore_or_remove "$config_dir/$relative" "$relative"
|
||||
done
|
||||
|
||||
if [[ -d $config_dir/blob ]]; then
|
||||
rm -rf "$config_dir/blob"
|
||||
say "remove ~/.config/blob"
|
||||
fi
|
||||
|
||||
for lua in hyprland.lua monitors.lua input.lua looknfeel.lua bindings.lua autostart.lua hyprsunset.conf xdph.conf; do
|
||||
restore_or_remove "$config_dir/hypr/$lua" "hypr/$lua"
|
||||
done
|
||||
|
||||
if [[ $keep_state == false && -d $HOME/.local/state/blob ]]; then
|
||||
rm -rf "$HOME/.local/state/blob"
|
||||
say "remove ~/.local/state/blob"
|
||||
fi
|
||||
|
||||
if [[ -e $session_entry ]]; then
|
||||
sudo rm -f "$session_entry"
|
||||
say "remove wayland session entry"
|
||||
fi
|
||||
|
||||
say ""
|
||||
say "Done. Pick another session at the login screen before rebooting."
|
||||
Reference in New Issue
Block a user