Compare commits

..
2 Commits
14 changed files with 240 additions and 136 deletions
+14 -12
View File
@@ -74,24 +74,26 @@ It runs these steps, in this order, and every one of them is idempotent:
| Step | What it does |
| --- | --- |
| 1. `BLOB_PATH` | symlinks `~/.local/share/blob` to this checkout |
| 2. Directories | creates the 19 directories the shell and scripts expect |
| 1. Directories | creates the 19 directories the shell and scripts expect, before anything writes into them |
| 2. `BLOB_PATH` | symlinks `~/.local/share/blob` to this checkout |
| 3. Packages | installs what is missing from `packages/`, bootstrapping `yay` first on a machine that has no AUR helper |
| 4. Config | copies `hypr/`, the themed app configs, `blob/shell.json`, the uwsm environment, and the icon font into `~/.config` |
| 5. User units | installs the session services into `~/.config/systemd/user` and enables them |
| 6. systemd drop-ins | writes the shutdown timeouts, oomd thresholds, plocate prune paths, and sleep hooks under `/etc` |
| 7. System services | enables NetworkManager, resolved, bluetooth, cups, avahi, docker, power-profiles-daemon, and oomd, and masks `NetworkManager-wait-online` |
| 8. Login screen | installs the session entry and the SDDM config, enables `sddm.service`, and sets the default target to `graphical.target` |
| 9. Launch | starts the login screen, so the desktop comes up without a reboot |
| 5. First theme | links `~/wallpapers` to the checkout, applies `flats` headless, picks a background, and links the btop and Neovim theme files, so the first login is not a blank desktop |
| 6. User units | installs the session services into `~/.config/systemd/user` and enables them |
| 7. systemd drop-ins | writes the shutdown timeouts, oomd thresholds, plocate prune paths, and sleep hooks under `/etc` |
| 8. System services | enables NetworkManager, resolved, bluetooth, cups, avahi, docker, power-profiles-daemon, and oomd, and masks `NetworkManager-wait-online` |
| 9. Login screen | installs the session entry and the SDDM config, enables `sddm.service`, and sets the default target to `graphical.target` |
| 10. Launch | starts the login screen, so the desktop comes up without a reboot |
Step 8 is the one a plain Arch install cannot do without: SDDM's stock Wayland
Step 9 is the one a plain Arch install cannot do without: SDDM's stock Wayland
greeter runs on `weston`, which Blob does not install, so the greeter is pointed
at Hyprland instead. It also reports any leftover drop-in that autologs into a
session that is not installed, which is what a black screen with no login prompt
usually is. [install.md](docs/install.md) covers each step in full.
at Hyprland with a Lua config, the only format Hyprland still reads. It also
reports any leftover drop-in that autologs into a session that is not installed,
which is the other thing a black screen with no login prompt usually is.
[install.md](docs/install.md) covers each step in full.
To back out, `./uninstall.sh` removes the session entry, the config, the user
services, and the symlink, restoring any `.bak` the installer made. The
services, and the symlinks, restoring any `.bak` the installer made. The
checkout, the wallpapers, and the themes stay where they are.
## Regenerating docs
-83
View File
@@ -1,83 +0,0 @@
# vim:ft=kitty
## name: Flats
## upstream: themes/flats/colors.toml
## blurb: Dark theme with a soft blue accent. Hand-translated from the
## Omarchy colors.toml, since macOS has no omarchy-theme-set.
# The basic colors
foreground #cbd2d9
background #0f1012
selection_foreground #0f1012
selection_background #7babe3
# Cursor colors
cursor #cbd2d9
cursor_text_color #0f1012
# URL underline color when hovering with mouse
url_color #7babe3
# Kitty window border colors
active_border_color #7babe3
inactive_border_color #949494
bell_border_color #d58638
# OS Window titlebar colors
wayland_titlebar_color system
macos_titlebar_color system
# Tab bar colors
active_tab_foreground #0f1012
active_tab_background #7babe3
inactive_tab_foreground #cbd2d9
inactive_tab_background #0f1012
tab_bar_background #000000
# Colors for marks (marked text in the terminal)
mark1_foreground #0f1012
mark1_background #7babe3
mark2_foreground #0f1012
mark2_background #38d586
mark3_foreground #0f1012
mark3_background #d58638
# The 16 terminal colors
#
# Note: flats maps the ANSI slots unconventionally — color4 (blue) holds a
# magenta and color6 (cyan) holds the blue, among others. Kept faithful to
# colors.toml so both machines render identically; the comments below name
# the ANSI slot, not the resulting hue.
# black
color0 #000000
color8 #949494
# red
color1 #d53838
color9 #d53838
# green
color2 #38d4d5
color10 #38d4d5
# yellow
color3 #d58638
color11 #d58638
# blue
color4 #d53886
color12 #d53886
# magenta
color5 #38d586
color13 #38d586
# cyan
color6 #3886d5
color14 #3886d5
# white
color7 #d4d4d4
color15 #d4d4d4
+4 -5
View File
@@ -1,11 +1,10 @@
# vim:ft=kitty
#
# kitty config for macOS. The Arch side of these dotfiles gets its terminal
# colors from omarchy-theme-set rewriting the active theme; there is no
# omarchy here, so current-theme.conf is checked in alongside this file and
# generated by hand from themes/<name>/colors.toml.
# Colors come from the active theme, the same way foot.ini takes them: the
# theme pipeline renders default/themed/kitty.conf.tpl into the current-theme
# directory, and blob-restart-terminal signals kitty to reload it.
include current-theme.conf
include ~/.local/state/blob/current/theme/kitty.conf
# Fonts
-9
View File
@@ -1,9 +0,0 @@
misc {
disable_hyprland_logo = true
disable_splash_rendering = true
force_default_wallpaper = 0
}
animations {
enabled = false
}
+13
View File
@@ -0,0 +1,13 @@
-- Hyprland config for the SDDM Wayland greeter. SDDM starts the greeter itself
-- once the compositor is up, so nothing here launches anything.
hl.config({
misc = {
disable_hyprland_logo = true,
disable_splash_rendering = true,
force_default_wallpaper = 0,
},
animations = {
enabled = false,
},
})
+1 -1
View File
@@ -2,7 +2,7 @@
DisplayServer=wayland
[Wayland]
CompositorCommand=start-hyprland -- --config /etc/sddm/hyprland-greeter.conf
CompositorCommand=start-hyprland -- --config /etc/sddm/hyprland-greeter.lua
[Theme]
Current=
+36 -19
View File
@@ -15,42 +15,59 @@ with `--check`.
| `install/steps/packages.sh` | repo and AUR packages, and the yay bootstrap |
| `install/steps/config.sh` | the `BLOB_PATH` symlink and everything under `~/.config` |
| `install/steps/services.sh` | system services, systemd drop-ins, user units |
| `install/steps/theme.sh` | the wallpaper link, the first theme, the first background |
| `install/steps/login.sh` | SDDM, the session entry, and the first launch |
## Steps
1. **`BLOB_PATH`** is symlinked to the checkout at `~/.local/share/blob`. Nothing
under `bin/`, `shell/`, `themes/` or `default/` is ever copied.
2. **Directories** are created, including `~/.config/systemd/user/app.slice.d`
and `~/wallpapers`.
1. **Directories** are created first, because nothing below can write into a
directory that is not there: `~/.config/blob`, `~/.config/systemd/user/app.slice.d`,
`~/.local/state/blob`, `~/.local/share` and `~/.cache/blob`. A fresh Arch
install has none of them.
2. **`BLOB_PATH`** is symlinked to the checkout at `~/.local/share/blob`. Nothing
under `bin/`, `shell/`, `themes/` or `default/` is ever copied. `ln` does not
create the parent of a link, so this step makes `~/.local/share` itself rather
than assuming step 1 ran.
3. **Packages** come from `packages/blob.packages` and
`packages/blob-aur.packages`. A machine with no AUR helper gets `yay-bin`
built once with `makepkg`. See [packages.md](packages.md).
4. **Config** is copied into `~/.config`: `hypr/`, the themed app configs,
`blob/shell.json`, the uwsm environment, and the icon font.
5. **User units** land in `~/.config/systemd/user` and are enabled, not started:
5. **The first theme** is applied. `~/wallpapers` is symlinked to the checkout's
`wallpapers/`, `flats` is applied with `BLOB_THEME_HEADLESS=1` (no shell or
session bus exists yet), and the background symlink is pointed at the first
image in `~/wallpapers`. Without this the first login has no palette and no
wallpaper, which draws as a black desktop. It then links the per-app theme
files the pipeline renders but no app would otherwise find:
`~/.config/btop/themes/current.theme` (btop's `color_theme = "current"`) and,
only when `~/.config/nvim/lua/plugins` already exists, `theme.lua` there.
Every step is skipped when a theme, a background, a wallpaper directory or a
theme file of your own is already in place.
6. **User units** land in `~/.config/systemd/user` and are enabled, not started:
they are all `WantedBy=graphical-session.target`, so they come up with the
session. `blob-speaker-tuning.service` is left out, because
`blob-audio-tuning` installs it only on machines with a tuning profile.
6. **systemd drop-ins** are written under `/etc`: the shutdown timeouts, the
7. **systemd drop-ins** are written under `/etc`: the shutdown timeouts, the
oomd pressure thresholds, the plocate prune paths, and the sleep hooks.
7. **System services** are enabled: NetworkManager, resolved, bluetooth, cups,
8. **System services** are enabled: NetworkManager, resolved, bluetooth, cups,
avahi, docker, power-profiles-daemon, and oomd.
`NetworkManager-wait-online.service` is masked so a slow DHCP lease cannot
hold up the login screen.
8. **The login screen** is SDDM. Three pieces have to be in place:
9. **The login screen** is SDDM. Three pieces have to be in place:
| File | Why |
| --- | --- |
| `/usr/local/share/wayland-sessions/blob.desktop` | the session SDDM offers |
| `/etc/sddm.conf.d/zz-blob.conf` | Wayland greeter, run on Hyprland, stock theme, remember the last session |
| `/etc/sddm/hyprland-greeter.conf` | the greeter's own tiny Hyprland config |
| `/etc/sddm/hyprland-greeter.lua` | the greeter's own tiny Hyprland config |
The greeter config matters more than it looks: SDDM's stock Wayland greeter
runs on `weston`, which Blob does not install, so a default SDDM would fail
to draw anything. `sddm.service` is enabled and the default systemd target is
set to `graphical.target`, which an Arch install without a display manager
does not have.
to draw anything. That greeter config has to be Lua: Hyprland dropped the old
hyprlang format, and a config it cannot parse leaves the greeter with no
compositor and the screen black. `sddm.service` is enabled and the default
systemd target is set to `graphical.target`, which an Arch install without a
display manager does not have.
The `zz-` prefix is load-bearing. SDDM reads `/etc/sddm.conf.d` in
alphabetical order and the last file wins, so a leftover `10-wayland.conf`,
@@ -70,10 +87,10 @@ with `--check`.
`--autologin` writes `zzz-blob-autologin.conf`, the only file that sorts after
`zz-blob.conf`.
9. **Launch.** When the installer was run from a console and nothing graphical
is running, it starts `sddm.service` so the desktop appears without a reboot.
`--no-launch` prints the command instead. `--autologin` writes
`/etc/sddm.conf.d/99-blob-autologin.conf` for the current user.
10. **Launch.** When the installer was run from a console and nothing graphical
is running, it starts `sddm.service` so the desktop appears without a reboot.
`--no-launch` prints the command instead. `--autologin` writes
`/etc/sddm.conf.d/zzz-blob-autologin.conf` for the current user.
## Flags
@@ -88,7 +105,7 @@ with `--check`.
## Uninstall
`./uninstall.sh` removes the config, the user units, the `/etc` drop-ins, the
session entry and the symlink, restoring any `.bak` the installer left.
`--keep-session` keeps the session entry and the SDDM config, `--keep-state`
keeps `~/.local/state/blob`. `sddm.service` is left enabled either way, so the
session entry, the wallpaper link and the symlink, restoring any `.bak` the
installer left. `--keep-session` keeps the session entry and the SDDM config,
`--keep-state` keeps `~/.local/state/blob`. `sddm.service` is left enabled either way, so the
machine still reaches a login screen.
+11
View File
@@ -27,6 +27,12 @@ the shell and the CLI never disagree about what a slot means.
## Templates
Each rendered file is picked up differently: `foot.ini` and `kitty.conf` by an
`include` in the shipped terminal config, `hyprland.lua` through Hyprland's Lua
module path, `shell.toml` and `chromium.theme` read straight from the state
directory, and `btop.theme` and `neovim.lua` through symlinks the installer
makes into those apps' own config trees.
`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.
@@ -66,3 +72,8 @@ 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.
Because of that, a machine with no wallpaper yet would get none from the theme
either. The installer covers the gap: it symlinks `~/wallpapers` to the
checkout, applies `flats`, and points the background symlink at the first image
it finds. See [install.md](install.md).
+8 -2
View File
@@ -48,6 +48,7 @@ source "$repo_dir/install/steps/directories.sh"
source "$repo_dir/install/steps/packages.sh"
source "$repo_dir/install/steps/config.sh"
source "$repo_dir/install/steps/services.sh"
source "$repo_dir/install/steps/theme.sh"
source "$repo_dir/install/steps/login.sh"
say "Blob installer"
@@ -57,8 +58,8 @@ if [[ $check == true ]]; then
fi
say ""
link_blob_path
create_directories
link_blob_path
install_packages
install_shipped_config
@@ -66,6 +67,11 @@ install_user_units
install_systemd_dropins
install_zen_config
link_wallpapers
install_default_theme
install_default_background
link_app_themes
enable_system_services
mask_system_services
enable_user_units
@@ -82,7 +88,7 @@ if [[ $check == true ]]; then
(( changes == 0 )) || exit 1
else
say "$changes item(s) changed."
say "Set a theme with 'blob theme set <name>' once the session is up."
say "Change the theme with 'blob theme set <name>' once the session is up."
say ""
launch_desktop
fi
+5
View File
@@ -1,6 +1,10 @@
# 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.
#
# ~/.local/share does not exist on a fresh Arch install, and ln does not create
# the parent of its link, so make it here rather than relying on another step
# having run first.
link_blob_path() {
local current=""
[[ -L $blob_path ]] && current="$(readlink -f "$blob_path")"
@@ -22,6 +26,7 @@ link_blob_path() {
return 0
fi
mkdir -p "$(dirname "$blob_path")"
ln -sfn "$repo_dir" "$blob_path"
say "link BLOB_PATH -> $repo_dir"
}
+1 -1
View File
@@ -17,7 +17,7 @@ directories=(
"$HOME/.local/share/fonts"
"$HOME/.local/share/applications"
"$HOME/.local/share/icons/hicolor/256x256/apps"
"$HOME/wallpapers"
"$HOME/.cache/blob"
)
create_directories() {
+6 -4
View File
@@ -1,7 +1,7 @@
session_entry_dir=/usr/local/share/wayland-sessions
session_entry_search_dirs=(/usr/local/share/wayland-sessions /usr/share/wayland-sessions)
greeter_config_dir=/etc/sddm.conf.d
greeter_compositor_config=/etc/sddm/hyprland-greeter.conf
greeter_compositor_config=/etc/sddm/hyprland-greeter.lua
# SDDM reads /etc/sddm.conf.d in alphabetical order and the last file wins, so
# Blob's drop-ins are named to sort after anything a distribution, an installer
@@ -10,10 +10,12 @@ blob_greeter_conf="$greeter_config_dir/zz-blob.conf"
blob_autologin_conf="$greeter_config_dir/zzz-blob-autologin.conf"
# SDDM's stock Wayland greeter runs on weston, which Blob does not install.
# Point it at Hyprland with a greeter-only config instead.
# Point it at Hyprland with a greeter-only config instead. That config is Lua:
# Hyprland dropped the old hyprlang format, and a config it cannot parse leaves
# the greeter with no compositor and the screen black.
install_login_config() {
install_root_file "$repo_dir/default/sddm/hyprland-greeter.conf" \
"$greeter_compositor_config" "sddm/hyprland-greeter.conf"
install_root_file "$repo_dir/default/sddm/hyprland-greeter.lua" \
"$greeter_compositor_config" "sddm/hyprland-greeter.lua"
install_root_file "$repo_dir/default/sddm/zz-blob.conf" \
"$blob_greeter_conf" "sddm.conf.d/zz-blob.conf"
}
+136
View File
@@ -0,0 +1,136 @@
default_theme=flats
wallpaper_dir="$HOME/wallpapers"
current_state_dir="$HOME/.local/state/blob/current"
# ~/wallpapers is what the wallpaper picker and `blob wallpaper set` read, and
# the checkout already carries the collection. Link it for the same reason
# BLOB_PATH is a link: one copy, and edits in the checkout are live.
link_wallpapers() {
local source="$repo_dir/wallpapers"
local current=""
[[ -L $wallpaper_dir ]] && current="$(readlink -f "$wallpaper_dir")"
if [[ $current == "$source" ]]; then
say "ok wallpapers"
return 0
fi
if [[ -d $wallpaper_dir && ! -L $wallpaper_dir ]] && [[ -n "$(ls -A "$wallpaper_dir")" ]]; then
say "ok wallpapers (own directory kept)"
return 0
fi
note_change
if [[ $check == true ]]; then
say "would link $wallpaper_dir -> $source"
return 0
fi
[[ -d $wallpaper_dir && ! -L $wallpaper_dir ]] && rmdir "$wallpaper_dir"
ln -sfn "$source" "$wallpaper_dir"
say "link wallpapers -> $source"
}
# Without a theme there is no palette, no themed app config, and no background
# for the shell to draw, so the first login lands on a black screen that looks
# like a failed session. Applied headless: no shell and no session bus exist
# while the installer runs.
install_default_theme() {
if [[ -s $current_state_dir/theme.name ]]; then
say "ok theme ($(cat "$current_state_dir/theme.name"))"
return 0
fi
note_change
if [[ $check == true ]]; then
say "would apply the $default_theme theme"
return 0
fi
if BLOB_PATH="$blob_path" BLOB_THEME_HEADLESS=1 PATH="$repo_dir/bin:$PATH" \
blob-theme-set "$default_theme" >/dev/null; then
say "theme $default_theme"
else
say "WARN could not apply the $default_theme theme"
fi
}
# Only a theme that ships its own backgrounds/ sets this link, and most ship
# none: they are meant to leave the current wallpaper alone. On a first install
# there is no current wallpaper, so pick one.
install_default_background() {
local link="$current_state_dir/background"
local search="$wallpaper_dir"
local first
if [[ -e $link ]]; then
say "ok background"
return 0
fi
# --check writes nothing, so the wallpaper link is not there yet to look in.
[[ -d $search ]] || search="$repo_dir/wallpapers"
first="$(find -L "$search" -maxdepth 1 -type f \
\( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.webp' \) \
2>/dev/null | sort | head -1)"
if [[ -z $first ]]; then
say "SKIP background (no image in $search)"
return 0
fi
note_change
if [[ $check == true ]]; then
say "would set the background to $(basename "$first")"
return 0
fi
ln -nsf "$first" "$link"
say "bg $(basename "$first")"
}
# The theme pipeline renders one file per app into the current-theme directory,
# but each app only ever reads its own config tree. These links are what join
# the two. Without them a theme switch silently leaves the app on stock colors,
# because nothing errors: the app just never sees the generated file.
link_theme_file() {
local theme_file="$1" dest="$2" label="$3"
local target="$current_state_dir/theme/$theme_file"
if [[ -L $dest ]] && [[ "$(readlink "$dest")" == "$target" ]]; then
say "ok $label"
return 0
fi
if [[ -e $dest && ! -L $dest ]] && [[ $force == false ]]; then
note_change
say "DIFF $label (own file kept; --force to replace)"
return 0
fi
note_change
if [[ $check == true ]]; then
say "would link $label"
return 0
fi
mkdir -p "$(dirname "$dest")"
ln -nsf "$target" "$dest"
say "link $label"
}
# btop reads color_theme = "current" out of its own themes directory, and
# Neovim loads whatever theme.lua its plugin directory holds. Neovim is only
# linked when that directory already exists: Blob ships no Neovim config, so
# creating the tree would be inventing a layout the user has not chosen.
link_app_themes() {
link_theme_file btop.theme "$config_dir/btop/themes/current.theme" "btop theme"
if [[ -d $config_dir/nvim/lua/plugins ]]; then
link_theme_file neovim.lua "$config_dir/nvim/lua/plugins/theme.lua" "neovim theme"
else
say "SKIP neovim theme (no ~/.config/nvim/lua/plugins)"
fi
}
+5
View File
@@ -92,6 +92,11 @@ if [[ -L $blob_path ]]; then
say "remove BLOB_PATH symlink"
fi
if [[ -L $HOME/wallpapers ]]; then
rm -f "$HOME/wallpapers"
say "remove ~/wallpapers symlink"
fi
for relative in blob/shell.json uwsm/default uwsm/env.d/10-blob; do
restore_or_remove "$config_dir/$relative" "$relative"
done