feat(installer): modules.conf registry + sentinel-based code generation

Collapse 5 manual touch points per optional app module (counter, checklist,
summary, conflict check, dispatch in tui-install.sh + answerfile generator +
docs table row) into a single source of truth at setup/modules.conf.

- setup/modules.conf: 80-module registry (id|category|description|default|excludes)
  with sensible defaults (python, firefox-browser, onlyoffice) and conflict pairs
  (plymouth-custom ↔ plymouth)
- setup/sync-modules.sh: scans apps/*.sh, stubs any IDs missing from modules.conf
- setup/generate-modules.sh: regenerates all sentinel regions from modules.conf
  (supports --dry-run); fixes Python re.sub backslash-n corruption via lambda repl
- tui-install.sh: 5 sentinel regions added (module-counters, module-checklist,
  module-summary, module-conflicts, module-dispatch); fixes 19 modules missing
  from count_steps() and mail-notmuch/caldav-sync missing from SUMMARY
- generate-answerfile.sh: module-checklist sentinel; list-height now auto-computed
- docs/md/modules.md: per-category sentinels; all sections regenerated from conf
- Renames: prismlauncher→prism, freeipa-image-builder→freeipa-image,
  firefox→firefox-browser, zed→zed-ide; moves python/zfs/wprs into apps/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
Amir Alexander Abdelbaki 2026-06-26 13:24:02 +02:00
parent 9289f01965
commit 5d7c97409b
13 changed files with 966 additions and 525 deletions

View File

@ -84,31 +84,175 @@ Install via `tui-install.sh` at first install, or add later:
bash ~/Dotfiles/setup/install-modules.sh bash ~/Dotfiles/setup/install-modules.sh
``` ```
> This table is generated from `setup/modules.conf`. Run `setup/generate-modules.sh` to regenerate.
### AI & Machine Learning ### AI & Machine Learning
| ID | Package | Description | <!-- BEGIN GENERATED MODULES: ai -->
|----|---------|-------------| | ID | Description |
| `ollama` | ollama | Local LLM runner with REST API server | |----|-------------|
| `llama-cpp` | llama.cpp | Standalone inference CLI + server | | `ollama` | local LLM runner and API server |
| `open-webui` | open-webui | Browser UI for Ollama / OpenAI-compatible backends | | `llama-cpp` | standalone LLM inference CLI and server |
| `claude` | claude (npm) | Anthropic Claude Code CLI | | `open-webui` | browser UI for Ollama and LLM backends |
| `claude` | Anthropic Claude Code CLI via npm |
<!-- END GENERATED MODULES: ai -->
### Networking & Security ### Networking & Security
| ID | Packages | Description | <!-- BEGIN GENERATED MODULES: networking -->
|----|---------|-------------| | ID | Description |
| `networking-cli` | nmap · nethogs · mitmproxy · httpie | Network analysis and HTTP tooling | |----|-------------|
| `disk-recovery` | ddrescue · f3 | Disk imaging and flash drive testing | | `networking-cli` | nmap, nethogs, mitmproxy, httpie |
| `ssh-server` | openssh | SSH daemon with key-auth enforcement | | `disk-recovery` | ddrescue and f3 disk recovery tools |
| `wireshark` | wireshark-qt | Packet capture and analysis GUI | | `himalaya` | terminal email client (AUR) |
| `mail-notmuch` | isync, msmtp, notmuch, alot mail stack |
| `caldav-sync` | vdirsyncer and khal CalDAV calendar sync |
| `ssh-server` | openssh with key-auth and systemd unit enabled |
| `wireshark` | network packet analyser GUI |
| `anti-malware` | ClamAV, rkhunter, chkrootkit |
<!-- END GENERATED MODULES: networking -->
### Development
<!-- BEGIN GENERATED MODULES: dev -->
| ID | Description |
|----|-------------|
| `gnuplot` | scientific plotting tool |
| `blender-povray` | 3D modelling and ray-tracing (Blender + POV-Ray) |
| `toot` | Mastodon CLI client (AUR) |
| `db-clients` | pgcli and mycli interactive database CLIs |
| `mysql` | MariaDB server with initial setup |
| `productivity` | taskwarrior, watson, jrnl — task management and time tracking |
| `python` | pyright, pipx, pynvim Python tooling |
| `k8s` | kubectl and podman-desktop Kubernetes tools |
| `docker` | docker and docker-compose |
| `podman` | rootless containers with buildah |
| `cockpit` | web UI for machines and containers |
<!-- END GENERATED MODULES: dev -->
### System Utilities
<!-- BEGIN GENERATED MODULES: system -->
| ID | Description |
|----|-------------|
| `tlp` | laptop battery optimisation |
| `butter` | btrfs snapshot backup manager (AUR) |
| `localsend` | LAN file transfer, AirDrop-like (AUR) |
| `croc` | cross-platform encrypted file transfer |
| `opendeck` | Stream Deck controller — ydotool + OpenDeck (Flatpak) |
| `localtunnel` | expose localhost over a public URL |
| `timeshift` | system snapshot and backup with autosnap |
| `zfs` | zfs-dkms kernel module |
| `wprs` | Wayland proxy for remote sessions (wprs-git, AUR) |
| `plymouth-custom` | boot splash with a user-supplied image |
<!-- END GENERATED MODULES: system -->
### Gaming
<!-- BEGIN GENERATED MODULES: gaming -->
| ID | Description |
|----|-------------|
| `steam` | Steam gaming platform |
| `vesktop` | Discord client with Vencord theme |
| `spotify` | Spotify launcher with Spicetify theming |
| `prism` | PrismLauncher Minecraft launcher (Flatpak) |
| `vintagestory` | Vintage Story survival game (AUR) |
| `openarena` | open-source Quake III Arena |
| `tetris` | bastet and vitetris terminal Tetris |
| `doom` | Chocolate Doom with Freedoom data |
| `sauerbraten` | Sauerbraten open-source FPS (Cube 2) |
| `stuntrally` | Stunt Rally racing game (Flatpak) |
<!-- END GENERATED MODULES: gaming -->
### Notes & Office
<!-- BEGIN GENERATED MODULES: notes -->
| ID | Description |
|----|-------------|
| `onlyoffice` | office suite — Docs, Sheets, Slides (AUR) |
| `xournal` | note-taking and PDF annotator |
| `rnote` | handwriting and note-taking with stylus support (Flatpak) |
| `obsidian` | knowledge base and Markdown note-taking (Flatpak) |
| `tangent-notes` | networked Markdown note-taking (Flatpak) |
<!-- END GENERATED MODULES: notes -->
### Media
<!-- BEGIN GENERATED MODULES: media -->
| ID | Description |
|----|-------------|
| `ffmpeg` | GStreamer codecs and ffmpegthumbnailer |
| `sox` | command-line audio processing toolkit |
| `imagemagick` | image manipulation suite |
| `yt-dlp` | YouTube and media downloader |
<!-- END GENERATED MODULES: media -->
### Graphic Design
<!-- BEGIN GENERATED MODULES: graphics -->
| ID | Description |
|----|-------------|
| `gimp` | GNU Image Manipulation Program |
| `inkscape` | vector graphics editor |
| `krita` | digital painting and illustration |
<!-- END GENERATED MODULES: graphics -->
### Video Editing
<!-- BEGIN GENERATED MODULES: video -->
| ID | Description |
|----|-------------|
| `kdenlive` | KDE non-linear video editor |
| `openshot` | cross-platform video editor |
| `shotcut` | cross-platform video editor |
<!-- END GENERATED MODULES: video -->
### Audio Production
<!-- BEGIN GENERATED MODULES: audio -->
| ID | Description |
|----|-------------|
| `ardour` | professional DAW |
| `audacity` | multi-track audio editor |
| `lmms` | Linux MultiMedia Studio music production |
| `mixxx` | DJ mixing software |
| `cecilia` | audio synthesis and signal processing (AUR) |
<!-- END GENERATED MODULES: audio -->
### Browsers
<!-- BEGIN GENERATED MODULES: browsers -->
| ID | Description |
|----|-------------|
| `chromium` | open-source Chromium browser (official) |
| `firefox-browser` | Mozilla Firefox (official) |
| `zen-browser` | privacy-focused Firefox fork (AUR) |
| `nyxt` | keyboard-driven hackable browser (AUR) |
| `librewolf` | hardened Firefox fork (AUR) |
| `min-browser` | minimal Electron browser (AUR) |
<!-- END GENERATED MODULES: browsers -->
### IDEs & Editors
<!-- BEGIN GENERATED MODULES: editors -->
| ID | Description |
|----|-------------|
| `vscodium` | telemetry-free VS Code build (AUR) |
| `zed-ide` | high-performance Rust IDE (official) |
| `geany` | lightweight IDE with plugins (official) |
| `codeblocks` | C/C++ IDE (official) |
| `kate` | KDE advanced text editor (official) |
<!-- END GENERATED MODULES: editors -->
### Virtualisation & Remote Desktop ### Virtualisation & Remote Desktop
| ID | Packages | Description | <!-- BEGIN GENERATED MODULES: virt -->
|----|---------|-------------| | ID | Description |
| `qemu` | qemu-full · libvirt · virt-manager · virt-viewer · dnsmasq · bridge-utils · edk2-ovmf · swtpm · vde2 | Full QEMU/KVM stack with virt-manager GUI; enables libvirtd, auto-starts default NAT network, adds user to `libvirt` and `kvm` groups | |----|-------------|
| `rdp-client` | remmina · freerdp · libvncserver | Remmina remote desktop client with RDP (FreeRDP) and VNC support | | `rdp-client` | Remmina with FreeRDP and VNC plugins |
| `lamco-rdp-server` | lamco-rdp-server (AUR) | Native Wayland RDP server written in Rust with H.264/VA-API encoding; runs as a systemd user service | | `lamco-rdp-server` | native Wayland RDP server (AUR, Rust) |
| `qemu` | full QEMU/KVM stack with virt-manager |
<!-- END GENERATED MODULES: virt -->
**lamco-rdp-server notes:** **lamco-rdp-server notes:**
- Enabled as a user service: `systemctl --user enable lamco-rdp-server.service` - Enabled as a user service: `systemctl --user enable lamco-rdp-server.service`
@ -116,122 +260,15 @@ bash ~/Dotfiles/setup/install-modules.sh
- Optional GUI tray: `lamco-rdp-server-gui` - Optional GUI tray: `lamco-rdp-server-gui`
- Requires an `xdg-desktop-portal` matching your compositor (`-hyprland`, `-wlr`, `-gnome`, `-kde`) - Requires an `xdg-desktop-portal` matching your compositor (`-hyprland`, `-wlr`, `-gnome`, `-kde`)
### Development
| ID | Packages | Description |
|----|---------|-------------|
| `python` | pyright · pipx · pynvim | Python LSP, isolated tool runner, Neovim integration |
| `docker` | docker · docker-compose | Container runtime |
| `podman` | podman · buildah · podman-compose | Rootless containers |
| `cockpit` | cockpit · machines · podman | Web-based system management UI |
| `k8s` | kubectl · podman-desktop | Kubernetes CLI and desktop client |
| `db-clients` | pgcli · mycli | Enhanced interactive database CLIs |
| `mysql` | mariadb | MariaDB server with initial setup |
### IDEs & Editors
| ID | Package | Description |
|----|---------|-------------|
| `vscodium` | vscodium-bin (AUR) | VS Code without telemetry |
| `zed-ide` | zed | High-performance Rust IDE |
| `geany` | geany · geany-plugins | Lightweight IDE |
| `codeblocks` | codeblocks | C/C++ IDE |
| `kate` | kate | KDE advanced text editor |
### Browsers
| ID | Package | Description |
|----|---------|-------------|
| `chromium` | chromium | Open-source Chromium |
| `firefox-browser` | firefox | Mozilla Firefox |
| `zen-browser` | zen-browser-bin (AUR) | Privacy-focused Firefox fork |
| `nyxt` | nyxt (AUR) | Keyboard-driven, hackable browser |
| `librewolf` | librewolf-bin (AUR) | Hardened Firefox fork |
| `min-browser` | min (AUR) | Minimal Electron browser |
### Gaming
| ID | Package | Description |
|----|---------|-------------|
| `steam` | steam | Steam gaming platform |
| `vesktop` | vesktop (AUR) | Discord client with Vencord built-in |
| `spotify` | spotify (AUR) + spicetify | Music player with CyberQueer theme |
| `prism` | prismlauncher (Flatpak) | Minecraft launcher |
| `vintagestory` | vintagestory (AUR) | Survival / voxel game |
### Media & Creative
| ID | Packages | Description |
|----|---------|-------------|
| `ffmpeg` | gst-plugin-pipewire · gst-plugins-good · ffmpegthumbnailer | GStreamer codecs + thumbnailer |
| `sox` | sox | Command-line audio processing |
| `imagemagick` | imagemagick | Image manipulation suite |
| `yt-dlp` | yt-dlp | YouTube / media downloader |
| `blender` | blender | 3D creation suite |
| `gnuplot` | gnuplot | Scientific plotting |
| `povray` | povray | Ray-tracing renderer |
#### Graphic Design
| ID | Package | Description |
|----|---------|-------------|
| `gimp` | gimp | GNU Image Manipulation Program |
| `inkscape` | inkscape | Vector graphics editor |
| `krita` | krita | Digital painting and illustration |
#### Video Editing
| ID | Package | Description |
|----|---------|-------------|
| `kdenlive` | kdenlive | KDE non-linear video editor |
| `openshot` | openshot | Cross-platform video editor |
| `shotcut` | shotcut | Cross-platform video editor |
#### Audio Production
| ID | Package | Description |
|----|---------|-------------|
| `audacity` | audacity | Multi-track audio editor |
| `lmms` | lmms | Linux MultiMedia Studio (music production) |
| `ardour` | ardour | Professional DAW |
| `mixxx` | mixxx | DJ mixing software |
| `cecilia` | cecilia (AUR) | Audio synthesis and signal processing |
### Productivity
| ID | Packages | Description |
|----|---------|-------------|
| `productivity` | taskwarrior · watson · jrnl | Task management, time tracking, journaling |
| `himalaya` | himalaya (AUR) | Terminal email client |
| `toot` | toot (AUR) | Mastodon CLI client |
| `xournal` | xournalpp | Note-taking and PDF annotator |
| `rnote` | Rnote (Flatpak) | Handwriting and note-taking with stylus support |
| `obsidian` | Obsidian (Flatpak) | Knowledge base and Markdown note-taking |
| `tangent-notes` | Tangent Notes (Flatpak) | Networked Markdown note-taking |
### System Utilities
| ID | Packages | Description |
|----|---------|-------------|
| `plymouth` | plymouth · librsvg | Boot splash (bundled skull logo + spinner) — same as the core component |
| `plymouth-custom` | plymouth · librsvg or imagemagick | Boot splash with a user-supplied image; prompts for a PNG or SVG path |
| `tlp` | tlp · tlp-rdw | Laptop battery optimisation |
| `zfs` | zfs-dkms | ZFS kernel module |
| `wprs` | wprs-git (AUR) | Wayland proxy for remote sessions |
| `butter` | butter (AUR) | Btrfs snapshot backup manager |
| `localsend` | localsend (AUR) | LAN file transfer (AirDrop-like) |
| `croc` | croc | Cross-platform encrypted file transfer |
| `opendeck` | ydotool (pacman) · OpenDeck (Flatpak) | Stream Deck controller; ydotoold + OpenDeck added to Hyprland autostart |
| `localtunnel` | localtunnel (npm) | Expose localhost over a public URL |
| `onlyoffice` | onlyoffice-bin (AUR) | Office suite (Docs, Sheets, Slides) |
### Identity & Infrastructure ### Identity & Infrastructure
<!-- BEGIN GENERATED MODULES: infra -->
| ID | Description | | ID | Description |
|----|-------------| |----|-------------|
| `freeipa-client` | sssd + ipa-client-install + auto-enrollment (see [FreeIPA](freeipa-ansible.md)) | | `freeipa-client` | sssd and ipa-client-install with auto-enrollment |
| `freeipa-server` | Interactive FreeIPA server setup + client generator | | `freeipa-server` | interactive FreeIPA server setup with client generator |
| `freeipa-image` | OCI / LXC / Proxmox LXC image builder + Keycloak | | `freeipa-image` | OCI/LXC/Proxmox image builder with Keycloak |
<!-- END GENERATED MODULES: infra -->
--- ---

View File

@ -216,89 +216,92 @@ if [[ "$AF_RUN_TUI" == "true" ]]; then
3>&1 1>&2 2>&3) || AF_DE="none" 3>&1 1>&2 2>&3) || AF_DE="none"
# ── Apps ────────────────────────────────────────────────────────────────── # ── Apps ──────────────────────────────────────────────────────────────────
# BEGIN GENERATED MODULES: module-checklist
AF_APPS=$(dialog --backtitle "$BACKTITLE" \ AF_APPS=$(dialog --backtitle "$BACKTITLE" \
--title " Applications " \ --title " Applications " \
--checklist "Optional applications — installed after base components:" 40 76 32 \ --checklist "Optional applications — installed after base components:" 40 76 80 \
"ollama" "Ollama local LLM runner + API server" off \ "ollama" "ollama local LLM runner and API server" off \
"llama-cpp" "llama.cpp standalone inference CLI + server" off \ "llama-cpp" "llama-cpp standalone LLM inference CLI and server" off \
"open-webui" "Open WebUI browser UI for Ollama / LLM backends" off \ "open-webui" "open-webui browser UI for Ollama and LLM backends" off \
"claude" "Claude Code Anthropic CLI via npm" off \ "claude" "claude Anthropic Claude Code CLI via npm" off \
"networking-cli" "Networking CLI nmap · nethogs · mitmproxy · httpie" off \ "networking-cli" "networking-cli nmap, nethogs, mitmproxy, httpie" off \
"disk-recovery" "Disk Recovery ddrescue · f3" off \ "disk-recovery" "disk-recovery ddrescue and f3 disk recovery tools" off \
"himalaya" "Himalaya terminal email client (AUR)" off \ "himalaya" "himalaya terminal email client (AUR)" off \
"mail-notmuch" "Mail (notmuch) isync · msmtp · notmuch · alot stack" off \ "mail-notmuch" "mail-notmuch isync, msmtp, notmuch, alot mail stack" off \
"caldav-sync" "CalDAV Sync vdirsyncer · khal calendar sync" off \ "caldav-sync" "caldav-sync vdirsyncer and khal CalDAV calendar sync" off \
"gnuplot" "Gnuplot scientific plotting" off \ "ssh-server" "ssh-server openssh with key-auth and systemd unit enabled" off \
"blender-povray" "Blender + POV-Ray 3D modelling & ray-tracing" off \ "wireshark" "wireshark network packet analyser GUI" off \
"toot" "toot Mastodon CLI client (AUR)" off \ "anti-malware" "anti-malware ClamAV, rkhunter, chkrootkit" off \
"db-clients" "DB Clients pgcli · mycli" off \ "gnuplot" "gnuplot scientific plotting tool" off \
"mysql" "MySQL / MariaDB mariadb server + setup" off \ "blender-povray" "blender-povray 3D modelling and ray-tracing (Blender + POV-Ray)" off \
"productivity" "Productivity taskwarrior · watson · jrnl" off \ "toot" "toot Mastodon CLI client (AUR)" off \
"yt-dlp" "yt-dlp YouTube / media downloader" off \ "db-clients" "db-clients pgcli and mycli interactive database CLIs" off \
"sox" "SoX audio processing toolkit" off \ "mysql" "mysql MariaDB server with initial setup" off \
"imagemagick" "ImageMagick image manipulation" off \ "productivity" "productivity taskwarrior, watson, jrnl — task management and time tracking" off \
"ffmpeg" "FFmpeg extras thumbnailer · GStreamer codecs" off \ "python" "python pyright, pipx, pynvim Python tooling" on \
"localtunnel" "LocalTunnel expose localhost via tunnel" off \ "k8s" "k8s kubectl and podman-desktop Kubernetes tools" off \
"butter" "butter btrfs snapshot backup (AUR)" off \ "docker" "docker docker and docker-compose" off \
"tlp" "TLP laptop power management" off \ "podman" "podman rootless containers with buildah" off \
"steam" "Steam gaming platform" off \ "cockpit" "cockpit web UI for machines and containers" off \
"vesktop" "Vesktop Discord + Vencord theme" off \ "tlp" "tlp laptop battery optimisation" off \
"spotify" "Spotify launcher + Spicetify theming" off \ "butter" "butter btrfs snapshot backup manager (AUR)" off \
"prism" "PrismLauncher Minecraft launcher (Flatpak)" off \ "localsend" "localsend LAN file transfer, AirDrop-like (AUR)" off \
"vintagestory" "Vintage Story survival game (AUR)" off \ "croc" "croc cross-platform encrypted file transfer" off \
"openarena" "OpenArena open-source Quake III Arena" off \ "opendeck" "opendeck Stream Deck controller — ydotool + OpenDeck (Flatpak)" off \
"tetris" "Tetris CLI bastet · vitetris" off \ "localtunnel" "localtunnel expose localhost over a public URL" off \
"doom" "Doom Chocolate Doom + Freedoom data" off \ "timeshift" "timeshift system snapshot and backup with autosnap" off \
"sauerbraten" "Sauerbraten open-source FPS (Cube 2)" off \ "zfs" "zfs zfs-dkms kernel module" off \
"stuntrally" "Stunt Rally rally racing game (Flatpak)" off \ "wprs" "wprs Wayland proxy for remote sessions (wprs-git, AUR)" off \
"localsend" "LocalSend LAN file transfer (AUR)" off \ "plymouth-custom" "plymouth-custom boot splash with a user-supplied image" off \
"croc" "croc cross-platform file transfer" off \ "steam" "steam Steam gaming platform" off \
"opendeck" "OpenDeck Stream Deck controller (Flatpak+ydotool)" off \ "vesktop" "vesktop Discord client with Vencord theme" off \
"onlyoffice" "OnlyOffice office suite (AUR)" off \ "spotify" "spotify Spotify launcher with Spicetify theming" off \
"xournal" "Xournal++ note-taking & PDF annotator" off \ "prism" "prism PrismLauncher Minecraft launcher (Flatpak)" off \
"rnote" "Rnote handwriting & note-taking (Flatpak)" off \ "vintagestory" "vintagestory Vintage Story survival game (AUR)" off \
"obsidian" "Obsidian knowledge base & Markdown notes (Flatpak)" off \ "openarena" "openarena open-source Quake III Arena" off \
"tangent-notes" "Tangent Notes networked Markdown notes (Flatpak)" off \ "tetris" "tetris bastet and vitetris terminal Tetris" off \
"gimp" "GIMP GNU image manipulation program" off \ "doom" "doom Chocolate Doom with Freedoom data" off \
"inkscape" "Inkscape vector graphics editor" off \ "sauerbraten" "sauerbraten Sauerbraten open-source FPS (Cube 2)" off \
"krita" "Krita digital painting application" off \ "stuntrally" "stuntrally Stunt Rally racing game (Flatpak)" off \
"ardour" "Ardour professional DAW" off \ "onlyoffice" "onlyoffice office suite — Docs, Sheets, Slides (AUR)" on \
"audacity" "Audacity multi-track audio editor" off \ "xournal" "xournal note-taking and PDF annotator" off \
"lmms" "LMMS Linux MultiMedia Studio DAW" off \ "rnote" "rnote handwriting and note-taking with stylus support (Flatpak)" off \
"mixxx" "Mixxx DJ mixing software" off \ "obsidian" "obsidian knowledge base and Markdown note-taking (Flatpak)" off \
"cecilia" "Cecilia audio signal processing (AUR)" off \ "tangent-notes" "tangent-notes networked Markdown note-taking (Flatpak)" off \
"kdenlive" "Kdenlive KDE non-linear video editor" off \ "ffmpeg" "ffmpeg GStreamer codecs and ffmpegthumbnailer" off \
"openshot" "OpenShot easy video editor" off \ "sox" "sox command-line audio processing toolkit" off \
"shotcut" "Shotcut cross-platform video editor" off \ "imagemagick" "imagemagick image manipulation suite" off \
"anti-malware" "Anti-Malware ClamAV · rkhunter · chkrootkit" off \ "yt-dlp" "yt-dlp YouTube and media downloader" off \
"timeshift" "Timeshift system snapshot / backup + autosnap" off \ "gimp" "gimp GNU Image Manipulation Program" off \
"wireshark" "Wireshark network packet analyser (GUI)" off \ "inkscape" "inkscape vector graphics editor" off \
"k8s" "Kubernetes tools kubectl · podman-desktop" off \ "krita" "krita digital painting and illustration" off \
"docker" "Docker docker · docker-compose" off \ "kdenlive" "kdenlive KDE non-linear video editor" off \
"podman" "Podman rootless containers · buildah" off \ "openshot" "openshot cross-platform video editor" off \
"cockpit" "Cockpit web UI · machines · podman" off \ "shotcut" "shotcut cross-platform video editor" off \
"ssh-server" "SSH server openssh · key-auth · enabled" off \ "ardour" "ardour professional DAW" off \
"freeipa-client" "FreeIPA Client sssd + ipa-client-install + enrollment" off \ "audacity" "audacity multi-track audio editor" off \
"freeipa-server" "FreeIPA Server interactive server setup + client gen" off \ "lmms" "lmms Linux MultiMedia Studio music production" off \
"freeipa-image" "FreeIPA Image OCI/LXC/Proxmox LXC builder + Keycloak" off \ "mixxx" "mixxx DJ mixing software" off \
"python" "Python tools pyright · pipx · pynvim" off \ "cecilia" "cecilia audio synthesis and signal processing (AUR)" off \
"zfs" "ZFS zfs-dkms kernel module" off \ "chromium" "chromium open-source Chromium browser (official)" off \
"wprs" "WPRS wprs-git (AUR)" off \ "firefox-browser" "firefox-browser Mozilla Firefox (official)" on \
"chromium" "Chromium open-source browser (official)" off \ "zen-browser" "zen-browser privacy-focused Firefox fork (AUR)" off \
"firefox-browser" "Firefox Mozilla browser (official)" off \ "nyxt" "nyxt keyboard-driven hackable browser (AUR)" off \
"zen-browser" "Zen Browser Firefox-based privacy browser (AUR)" off \ "librewolf" "librewolf hardened Firefox fork (AUR)" off \
"nyxt" "Nyxt keyboard-driven browser (AUR)" off \ "min-browser" "min-browser minimal Electron browser (AUR)" off \
"librewolf" "LibreWolf hardened Firefox fork (AUR)" off \ "vscodium" "vscodium telemetry-free VS Code build (AUR)" off \
"min-browser" "Min minimal Electron browser (AUR)" off \ "zed-ide" "zed-ide high-performance Rust IDE (official)" off \
"vscodium" "VSCodium telemetry-free VS Code (AUR)" off \ "geany" "geany lightweight IDE with plugins (official)" off \
"zed-ide" "Zed high-performance Rust IDE (official)" off \ "codeblocks" "codeblocks C/C++ IDE (official)" off \
"geany" "Geany lightweight IDE + plugins (official)" off \ "kate" "kate KDE advanced text editor (official)" off \
"codeblocks" "Code::Blocks C/C++ IDE (official)" off \ "rdp-client" "rdp-client Remmina with FreeRDP and VNC plugins" off \
"kate" "Kate KDE advanced text editor (official)" off \ "lamco-rdp-server" "lamco-rdp-server native Wayland RDP server (AUR, Rust)" off \
"rdp-client" "RDP Client Remmina + FreeRDP + VNC plugins" off \ "qemu" "qemu full QEMU/KVM stack with virt-manager" off \
"lamco-rdp-server" "Lamco RDP Server native Wayland RDP server (AUR, Rust)" off \ "freeipa-client" "freeipa-client sssd and ipa-client-install with auto-enrollment" off \
"qemu" "QEMU/KVM full virt stack + virt-manager GUI" off \ "freeipa-server" "freeipa-server interactive FreeIPA server setup with client generator" off \
"freeipa-image" "freeipa-image OCI/LXC/Proxmox image builder with Keycloak" off \
3>&1 1>&2 2>&3) || AF_APPS="" 3>&1 1>&2 2>&3) || AF_APPS=""
# END GENERATED MODULES: module-checklist
# ── Shell RC preference ─────────────────────────────────────────────────── # ── Shell RC preference ───────────────────────────────────────────────────
AF_SHELL_RC=$(dialog --backtitle "$BACKTITLE" \ AF_SHELL_RC=$(dialog --backtitle "$BACKTITLE" \

234
setup/generate-modules.sh Executable file
View File

@ -0,0 +1,234 @@
#!/bin/bash
# generate-modules.sh — regenerate all sentinel regions from modules.conf
#
# Usage:
# ./generate-modules.sh — apply changes in-place
# ./generate-modules.sh --dry-run — print a unified diff, make no changes
#
# Sentinel format in target files:
# bash/sh: # BEGIN GENERATED MODULES: <tag> / # END GENERATED MODULES: <tag>
# markdown: <!-- BEGIN GENERATED MODULES: <tag> --> / <!-- END GENERATED MODULES: <tag> -->
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONF="$SCRIPT_DIR/modules.conf"
TUI="$SCRIPT_DIR/tui-install.sh"
AF="$SCRIPT_DIR/generate-answerfile.sh"
DOCS="$SCRIPT_DIR/../docs/md/modules.md"
DRY_RUN=false
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
# ── Parse modules.conf ────────────────────────────────────────────────────────
declare -a M_IDS M_CATS M_DESCS M_DEFAULTS M_EXCLUDES
skipped=()
while IFS='|' read -r id cat desc def excl; do
# Strip leading/trailing whitespace from each field
id="${id#"${id%%[![:space:]]*}"}"; id="${id%"${id##*[![:space:]]}"}"
[[ "$id" =~ ^# || -z "$id" ]] && continue
M_IDS+=("$id")
M_CATS+=("$cat")
M_DESCS+=("$desc")
M_DEFAULTS+=("${def:-off}")
M_EXCLUDES+=("${excl:-}")
done < "$CONF"
# Identify incomplete entries (missing category or description) — skip them.
declare -a ACTIVE_IDS
for i in "${!M_IDS[@]}"; do
if [[ -z "${M_CATS[$i]}" || -z "${M_DESCS[$i]}" ]]; then
skipped+=("${M_IDS[$i]}")
else
ACTIVE_IDS+=("$i")
fi
done
if (( ${#skipped[@]} > 0 )); then
printf '[warn] skipping %d stub(s) with empty category/description:\n' "${#skipped[@]}" >&2
printf ' %s\n' "${skipped[@]}" >&2
fi
# ── Build generated sections ──────────────────────────────────────────────────
# -- module-counters (tui-install.sh: count_steps function body) --------------
gen_counters=""
for i in "${ACTIVE_IDS[@]}"; do
id="${M_IDS[$i]}"
gen_counters+=" [[ \"\$a\" == *\"${id}\"* ]] && TOTAL=\$(( TOTAL + 1 ))\n"
done
# -- module-checklist (tui-install.sh and generate-answerfile.sh) -------------
# Builds the full SELECTED_APPS=$(dialog ...) call including open/close.
build_checklist_tui() {
local out
out=' SELECTED_APPS=$(dialog --backtitle "$BACKTITLE" \\\n'
out+=' --title " Applications " \\\n'
out+=' --checklist "Optional applications — installed after base components:" "$_APP_H" 76 "$_APP_LIST_H" \\\n'
for i in "${ACTIVE_IDS[@]}"; do
local id="${M_IDS[$i]}" desc="${M_DESCS[$i]}" def="${M_DEFAULTS[$i]}"
local padded
padded=$(printf '%-20s' "$id")
out+=" \"${id}\" \"${padded} ${desc}\" ${def} \\\\\n"
done
out+=' 3>&1 1>&2 2>&3) || SELECTED_APPS=""\n'
printf '%s' "$out"
}
build_checklist_af() {
local count="${#ACTIVE_IDS[@]}"
local out
out=" AF_APPS=\$(dialog --backtitle \"\$BACKTITLE\" \\\\\n"
out+=" --title \" Applications \" \\\\\n"
out+=" --checklist \"Optional applications — installed after base components:\" 40 76 ${count} \\\\\n"
for i in "${ACTIVE_IDS[@]}"; do
local id="${M_IDS[$i]}" desc="${M_DESCS[$i]}" def="${M_DEFAULTS[$i]}"
local padded
padded=$(printf '%-20s' "$id")
out+=" \"${id}\" \"${padded} ${desc}\" ${def} \\\\\n"
done
out+=' 3>&1 1>&2 2>&3) || AF_APPS=""\n'
printf '%s' "$out"
}
gen_checklist_tui=$(build_checklist_tui)
gen_checklist_af=$(build_checklist_af)
# -- module-summary (tui-install.sh: confirmation dialog) ---------------------
gen_summary=""
for i in "${ACTIVE_IDS[@]}"; do
id="${M_IDS[$i]}"
gen_summary+=" [[ \"\$SELECTED_APPS\" == *\"${id}\"* ]] && SUMMARY+=\" ✦ ${id}\\\\n\"\n"
done
# -- module-conflicts (tui-install.sh: before dispatch) -----------------------
gen_conflicts=""
for i in "${ACTIVE_IDS[@]}"; do
id="${M_IDS[$i]}"
excl="${M_EXCLUDES[$i]}"
[[ -z "$excl" ]] && continue
IFS=',' read -ra excl_list <<< "$excl"
for eid in "${excl_list[@]}"; do
eid="${eid#"${eid%%[![:space:]]*}"}"; eid="${eid%"${eid##*[![:space:]]}"}"
gen_conflicts+="if [[ \"\$SELECTED_APPS\" == *\"${id}\"* && \"\$SELECTED_APPS\" == *\"${eid}\"* ]]; then\n"
gen_conflicts+=" warn \"${id} and ${eid} are mutually exclusive — skipping ${eid}\"\n"
gen_conflicts+=" SELECTED_APPS=\"\${SELECTED_APPS/${eid}/}\"\n"
gen_conflicts+="fi\n"
done
done
# -- module-dispatch (tui-install.sh: installation section) -------------------
gen_dispatch=""
for i in "${ACTIVE_IDS[@]}"; do
id="${M_IDS[$i]}"
gen_dispatch+="[[ \"\$SELECTED_APPS\" == *\"${id}\"* ]] && run_module \"${id}\" \"\$APPS/${id}.sh\"\n"
done
# -- per-category doc tables (docs/md/modules.md) -----------------------------
declare -A gen_docs_cat
for i in "${ACTIVE_IDS[@]}"; do
id="${M_IDS[$i]}"
cat="${M_CATS[$i]}"
desc="${M_DESCS[$i]}"
gen_docs_cat[$cat]+="| \`${id}\` | ${desc} |\n"
done
build_doc_table() {
local cat="$1"
local rows="${gen_docs_cat[$cat]:-}"
if [[ -z "$rows" ]]; then
printf ''
return
fi
printf '| ID | Description |\n|----|-------------|\n'
printf '%b' "$rows"
}
# ── Splice helper (uses python3 for safe multiline replacement) ───────────────
splice() {
local file="$1" tag="$2" content="$3" style="${4:-bash}"
local tmpfile
tmpfile=$(mktemp)
printf '%b' "$content" > "$tmpfile"
if $DRY_RUN; then
python3 - "$file" "$tag" "$tmpfile" "$style" "dry" <<'PYEOF'
import sys, re, difflib
filepath, tag, tmpfile, style, _ = sys.argv[1:]
with open(tmpfile) as f:
new_content = f.read()
if style == 'md':
begin = f'<!-- BEGIN GENERATED MODULES: {tag} -->'
end = f'<!-- END GENERATED MODULES: {tag} -->'
else:
begin = f'# BEGIN GENERATED MODULES: {tag}'
end = f'# END GENERATED MODULES: {tag}'
with open(filepath) as f:
original = f.read()
pattern = re.compile(re.escape(begin) + r'\n.*?' + re.escape(end), re.DOTALL)
if not pattern.search(original):
print(f'ERROR: sentinel not found in {filepath}: {begin}', file=sys.stderr)
sys.exit(1)
replacement = begin + '\n' + new_content + end
updated = pattern.sub(lambda m: replacement, original)
if updated == original:
print(f' (no change) {filepath} [{tag}]', file=sys.stderr)
sys.exit(0)
diff = difflib.unified_diff(
original.splitlines(keepends=True),
updated.splitlines(keepends=True),
fromfile=f'{filepath}',
tofile=f'{filepath} (generated)',
)
sys.stdout.writelines(diff)
PYEOF
else
python3 - "$file" "$tag" "$tmpfile" "$style" <<'PYEOF'
import sys, re
filepath, tag, tmpfile, style = sys.argv[1:]
with open(tmpfile) as f:
new_content = f.read()
if style == 'md':
begin = f'<!-- BEGIN GENERATED MODULES: {tag} -->'
end = f'<!-- END GENERATED MODULES: {tag} -->'
else:
begin = f'# BEGIN GENERATED MODULES: {tag}'
end = f'# END GENERATED MODULES: {tag}'
with open(filepath) as f:
original = f.read()
pattern = re.compile(re.escape(begin) + r'\n.*?' + re.escape(end), re.DOTALL)
if not pattern.search(original):
print(f'ERROR: sentinel not found in {filepath}: {begin}', file=sys.stderr)
sys.exit(1)
replacement = begin + '\n' + new_content + end
updated = pattern.sub(lambda m: replacement, original)
if updated == original:
print(f' (no change) {filepath} [{tag}]', file=sys.stderr)
sys.exit(0)
with open(filepath, 'w') as f:
f.write(updated)
print(f' (updated) {filepath} [{tag}]', file=sys.stderr)
PYEOF
fi
rm -f "$tmpfile"
}
# ── Apply all regions ─────────────────────────────────────────────────────────
echo "==> tui-install.sh"
splice "$TUI" "module-counters" "$gen_counters"
splice "$TUI" "module-checklist" "$gen_checklist_tui"
splice "$TUI" "module-summary" "$gen_summary"
splice "$TUI" "module-conflicts" "$gen_conflicts"
splice "$TUI" "module-dispatch" "$gen_dispatch"
echo "==> generate-answerfile.sh"
splice "$AF" "module-checklist" "$gen_checklist_af"
echo "==> docs/md/modules.md"
for cat in ai networking dev system gaming notes media graphics video audio browsers editors virt productivity infra; do
table=$(build_doc_table "$cat")
[[ -z "$table" ]] && continue
splice "$DOCS" "$cat" "${table}"$'\n' md
done
echo "Done."

119
setup/modules.conf Normal file
View File

@ -0,0 +1,119 @@
# modules.conf — optional app module registry
# Format: id|category|description|default|excludes
# Run setup/sync-modules.sh to add stubs for newly created app scripts.
# Run setup/generate-modules.sh to regenerate installer + docs from this file.
#
# Categories:
# ai networking dev system gaming notes media graphics video audio
# browsers editors virt productivity infra
#
# default: on / off (pre-ticked in TUI and answerfile checklists)
# excludes: comma-separated IDs that must not run alongside this one
# ── AI & Machine Learning ─────────────────────────────────────────────────────
ollama|ai|local LLM runner and API server|off|
llama-cpp|ai|standalone LLM inference CLI and server|off|
open-webui|ai|browser UI for Ollama and LLM backends|off|
claude|ai|Anthropic Claude Code CLI via npm|off|
# ── Networking & Security ─────────────────────────────────────────────────────
networking-cli|networking|nmap, nethogs, mitmproxy, httpie|off|
disk-recovery|networking|ddrescue and f3 disk recovery tools|off|
himalaya|networking|terminal email client (AUR)|off|
mail-notmuch|networking|isync, msmtp, notmuch, alot mail stack|off|
caldav-sync|networking|vdirsyncer and khal CalDAV calendar sync|off|
ssh-server|networking|openssh with key-auth and systemd unit enabled|off|
wireshark|networking|network packet analyser GUI|off|
anti-malware|networking|ClamAV, rkhunter, chkrootkit|off|
# ── Development ───────────────────────────────────────────────────────────────
gnuplot|dev|scientific plotting tool|off|
blender-povray|dev|3D modelling and ray-tracing (Blender + POV-Ray)|off|
toot|dev|Mastodon CLI client (AUR)|off|
db-clients|dev|pgcli and mycli interactive database CLIs|off|
mysql|dev|MariaDB server with initial setup|off|
productivity|dev|taskwarrior, watson, jrnl — task management and time tracking|off|
python|dev|pyright, pipx, pynvim Python tooling|on|
k8s|dev|kubectl and podman-desktop Kubernetes tools|off|
docker|dev|docker and docker-compose|off|
podman|dev|rootless containers with buildah|off|
cockpit|dev|web UI for machines and containers|off|
# ── System Utilities ──────────────────────────────────────────────────────────
tlp|system|laptop battery optimisation|off|
butter|system|btrfs snapshot backup manager (AUR)|off|
localsend|system|LAN file transfer, AirDrop-like (AUR)|off|
croc|system|cross-platform encrypted file transfer|off|
opendeck|system|Stream Deck controller — ydotool + OpenDeck (Flatpak)|off|
localtunnel|system|expose localhost over a public URL|off|
timeshift|system|system snapshot and backup with autosnap|off|
zfs|system|zfs-dkms kernel module|off|
wprs|system|Wayland proxy for remote sessions (wprs-git, AUR)|off|
plymouth-custom|system|boot splash with a user-supplied image|off|plymouth
# ── Gaming ────────────────────────────────────────────────────────────────────
steam|gaming|Steam gaming platform|off|
vesktop|gaming|Discord client with Vencord theme|off|
spotify|gaming|Spotify launcher with Spicetify theming|off|
prism|gaming|PrismLauncher Minecraft launcher (Flatpak)|off|
vintagestory|gaming|Vintage Story survival game (AUR)|off|
openarena|gaming|open-source Quake III Arena|off|
tetris|gaming|bastet and vitetris terminal Tetris|off|
doom|gaming|Chocolate Doom with Freedoom data|off|
sauerbraten|gaming|Sauerbraten open-source FPS (Cube 2)|off|
stuntrally|gaming|Stunt Rally racing game (Flatpak)|off|
# ── Notes & Office ────────────────────────────────────────────────────────────
onlyoffice|notes|office suite — Docs, Sheets, Slides (AUR)|on|
xournal|notes|note-taking and PDF annotator|off|
rnote|notes|handwriting and note-taking with stylus support (Flatpak)|off|
obsidian|notes|knowledge base and Markdown note-taking (Flatpak)|off|
tangent-notes|notes|networked Markdown note-taking (Flatpak)|off|
# ── Media & Creative ──────────────────────────────────────────────────────────
ffmpeg|media|GStreamer codecs and ffmpegthumbnailer|off|
sox|media|command-line audio processing toolkit|off|
imagemagick|media|image manipulation suite|off|
yt-dlp|media|YouTube and media downloader|off|
# ── Graphic Design ────────────────────────────────────────────────────────────
gimp|graphics|GNU Image Manipulation Program|off|
inkscape|graphics|vector graphics editor|off|
krita|graphics|digital painting and illustration|off|
# ── Video Editing ─────────────────────────────────────────────────────────────
kdenlive|video|KDE non-linear video editor|off|
openshot|video|cross-platform video editor|off|
shotcut|video|cross-platform video editor|off|
# ── Audio Production ──────────────────────────────────────────────────────────
ardour|audio|professional DAW|off|
audacity|audio|multi-track audio editor|off|
lmms|audio|Linux MultiMedia Studio music production|off|
mixxx|audio|DJ mixing software|off|
cecilia|audio|audio synthesis and signal processing (AUR)|off|
# ── Browsers ─────────────────────────────────────────────────────────────────
chromium|browsers|open-source Chromium browser (official)|off|
firefox-browser|browsers|Mozilla Firefox (official)|on|
zen-browser|browsers|privacy-focused Firefox fork (AUR)|off|
nyxt|browsers|keyboard-driven hackable browser (AUR)|off|
librewolf|browsers|hardened Firefox fork (AUR)|off|
min-browser|browsers|minimal Electron browser (AUR)|off|
# ── IDEs & Editors ────────────────────────────────────────────────────────────
vscodium|editors|telemetry-free VS Code build (AUR)|off|
zed-ide|editors|high-performance Rust IDE (official)|off|
geany|editors|lightweight IDE with plugins (official)|off|
codeblocks|editors|C/C++ IDE (official)|off|
kate|editors|KDE advanced text editor (official)|off|
# ── Virtualisation & Remote Desktop ───────────────────────────────────────────
rdp-client|virt|Remmina with FreeRDP and VNC plugins|off|
lamco-rdp-server|virt|native Wayland RDP server (AUR, Rust)|off|
qemu|virt|full QEMU/KVM stack with virt-manager|off|
# ── Identity & Infrastructure ─────────────────────────────────────────────────
freeipa-client|infra|sssd and ipa-client-install with auto-enrollment|off|
freeipa-server|infra|interactive FreeIPA server setup with client generator|off|
freeipa-image|infra|OCI/LXC/Proxmox image builder with Keycloak|off|

33
setup/sync-modules.sh Executable file
View File

@ -0,0 +1,33 @@
#!/bin/bash
# sync-modules.sh — scan apps/ and stub any missing entries in modules.conf
# Safe to run repeatedly; never removes or modifies existing entries.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONF="$SCRIPT_DIR/modules.conf"
APPS="$SCRIPT_DIR/modules/optional-Modules/apps"
# Read all existing IDs from modules.conf into a set.
declare -A known
while IFS='|' read -r id _rest; do
[[ "$id" =~ ^[[:space:]]*# || -z "${id// /}" ]] && continue
known["$id"]=1
done < "$CONF"
# Scan apps/ for .sh files not yet in modules.conf and append stubs.
added=0
for script in "$APPS"/*.sh; do
id="${script##*/}"
id="${id%.sh}"
if [[ -z "${known[$id]+set}" ]]; then
printf '%s||||\n' "$id" >> "$CONF"
echo " [added stub] $id"
(( added++ )) || true
fi
done
if (( added == 0 )); then
echo "modules.conf is up to date — no new stubs added."
else
echo "$added stub(s) added to modules.conf. Fill in category and description, then run generate-modules.sh."
fi

View File

@ -162,85 +162,88 @@ count_steps() {
# Optional app modules: one glob check per app, one increment per match. # Optional app modules: one glob check per app, one increment per match.
# Glob syntax *"tag"* matches if the space-separated SELECTED_APPS string # Glob syntax *"tag"* matches if the space-separated SELECTED_APPS string
# contains the tag anywhere — works because tags never share substrings. # contains the tag anywhere — works because tags never share substrings.
[[ "$a" == *"ollama"* ]] && TOTAL=$(( TOTAL + 1 )) # BEGIN GENERATED MODULES: module-counters
[[ "$a" == *"llama-cpp"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"ollama"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"open-webui"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"llama-cpp"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"claude"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"open-webui"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"claude"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"networking-cli"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"networking-cli"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"disk-recovery"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"disk-recovery"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"himalaya"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"himalaya"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"gnuplot"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"mail-notmuch"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"blender-povray"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"caldav-sync"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"toot"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"ssh-server"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"db-clients"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"wireshark"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"mysql"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"anti-malware"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"productivity"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"gnuplot"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"yt-dlp"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"blender-povray"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"sox"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"toot"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"imagemagick"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"db-clients"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"ffmpeg"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"mysql"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"localtunnel"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"productivity"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"butter"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"python"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"tlp"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"k8s"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"steam"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"docker"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"vesktop"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"podman"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"spotify"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"cockpit"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"prism"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"tlp"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"vintagestory"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"butter"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"openarena"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"localsend"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"tetris"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"croc"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"doom"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"opendeck"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"sauerbraten"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"localtunnel"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"stuntrally"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"timeshift"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"localsend"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"zfs"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"croc"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"wprs"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"opendeck"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"plymouth-custom"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"onlyoffice"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"steam"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"wireshark"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"vesktop"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"k8s"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"spotify"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"docker"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"prism"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"podman"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"vintagestory"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"cockpit"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"openarena"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"ssh-server"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"tetris"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"freeipa-client"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"doom"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"freeipa-server"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"sauerbraten"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"freeipa-image"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"stuntrally"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"python"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"onlyoffice"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"zfs"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"xournal"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"wprs"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"rnote"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"chromium"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"obsidian"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"tangent-notes"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"ffmpeg"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"sox"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"imagemagick"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"yt-dlp"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"gimp"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"inkscape"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"krita"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"kdenlive"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"openshot"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"shotcut"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"ardour"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"audacity"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"lmms"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"mixxx"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"cecilia"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"chromium"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"firefox-browser"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"firefox-browser"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"zen-browser"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"zen-browser"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"nyxt"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"nyxt"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"librewolf"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"librewolf"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"min-browser"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"min-browser"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"vscodium"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"vscodium"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"zed-ide"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"zed-ide"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"geany"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"geany"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"codeblocks"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"codeblocks"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"kate"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"kate"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"rdp-client"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"rdp-client"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"lamco-rdp-server"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"lamco-rdp-server"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"qemu"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"qemu"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"xournal"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"freeipa-client"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"rnote"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"freeipa-server"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"obsidian"* ]] && TOTAL=$(( TOTAL + 1 )) [[ "$a" == *"freeipa-image"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"tangent-notes"* ]] && TOTAL=$(( TOTAL + 1 )) # END GENERATED MODULES: module-counters
[[ "$a" == *"gimp"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"inkscape"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"krita"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"ardour"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"audacity"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"lmms"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"mixxx"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"cecilia"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"kdenlive"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"openshot"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"shotcut"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"anti-malware"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"timeshift"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"mail-notmuch"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$a" == *"caldav-sync"* ]] && TOTAL=$(( TOTAL + 1 ))
} }
# ── Answerfile ──────────────────────────────────────────────────────────────── # ── Answerfile ────────────────────────────────────────────────────────────────
@ -441,94 +444,92 @@ else
# prompt, buttons), with a minimum of 4 to remain usable on tiny screens. # prompt, buttons), with a minimum of 4 to remain usable on tiny screens.
_APP_H=$(( TERM_H - 2 < 40 ? TERM_H - 2 : 40 )) _APP_H=$(( TERM_H - 2 < 40 ? TERM_H - 2 : 40 ))
_APP_LIST_H=$(( _APP_H - 8 < 4 ? 4 : _APP_H - 8 )) _APP_LIST_H=$(( _APP_H - 8 < 4 ? 4 : _APP_H - 8 ))
# BEGIN GENERATED MODULES: module-checklist
SELECTED_APPS=$(dialog --backtitle "$BACKTITLE" \ SELECTED_APPS=$(dialog --backtitle "$BACKTITLE" \
--title " Applications " \ --title " Applications " \
--checklist "Optional applications — installed after base components:" "$_APP_H" 76 "$_APP_LIST_H" \ --checklist "Optional applications — installed after base components:" "$_APP_H" 76 "$_APP_LIST_H" \
"ollama" "Ollama local LLM runner + API server" off \ "ollama" "ollama local LLM runner and API server" off \
"llama-cpp" "llama.cpp standalone inference CLI + server" off \ "llama-cpp" "llama-cpp standalone LLM inference CLI and server" off \
"open-webui" "Open WebUI browser UI for Ollama / LLM backends" off \ "open-webui" "open-webui browser UI for Ollama and LLM backends" off \
"claude" "Claude Code Anthropic CLI via npm" off \ "claude" "claude Anthropic Claude Code CLI via npm" off \
"networking-cli" "Networking CLI nmap · nethogs · mitmproxy · httpie" off \ "networking-cli" "networking-cli nmap, nethogs, mitmproxy, httpie" off \
"disk-recovery" "Disk Recovery ddrescue · f3" off \ "disk-recovery" "disk-recovery ddrescue and f3 disk recovery tools" off \
"himalaya" "Himalaya terminal email client (AUR)" off \ "himalaya" "himalaya terminal email client (AUR)" off \
"mail-notmuch" "Mail (notmuch) isync · msmtp · notmuch · alot stack" off \ "mail-notmuch" "mail-notmuch isync, msmtp, notmuch, alot mail stack" off \
"caldav-sync" "CalDAV Sync vdirsyncer · khal calendar sync" off \ "caldav-sync" "caldav-sync vdirsyncer and khal CalDAV calendar sync" off \
"gnuplot" "Gnuplot scientific plotting" off \ "ssh-server" "ssh-server openssh with key-auth and systemd unit enabled" off \
"blender-povray" "Blender + POV-Ray 3D modelling & ray-tracing" off \ "wireshark" "wireshark network packet analyser GUI" off \
"toot" "toot Mastodon CLI client (AUR)" off \ "anti-malware" "anti-malware ClamAV, rkhunter, chkrootkit" off \
"db-clients" "DB Clients pgcli · mycli" off \ "gnuplot" "gnuplot scientific plotting tool" off \
"mysql" "MySQL / MariaDB mariadb server + setup" off \ "blender-povray" "blender-povray 3D modelling and ray-tracing (Blender + POV-Ray)" off \
"productivity" "Productivity taskwarrior · watson · jrnl" off \ "toot" "toot Mastodon CLI client (AUR)" off \
"yt-dlp" "yt-dlp YouTube / media downloader" off \ "db-clients" "db-clients pgcli and mycli interactive database CLIs" off \
"sox" "SoX audio processing toolkit" off \ "mysql" "mysql MariaDB server with initial setup" off \
"imagemagick" "ImageMagick image manipulation" off \ "productivity" "productivity taskwarrior, watson, jrnl — task management and time tracking" off \
"ffmpeg" "FFmpeg extras thumbnailer · GStreamer codecs" off \ "python" "python pyright, pipx, pynvim Python tooling" on \
"localtunnel" "LocalTunnel expose localhost via tunnel" off \ "k8s" "k8s kubectl and podman-desktop Kubernetes tools" off \
"butter" "butter btrfs snapshot backup (AUR)" off \ "docker" "docker docker and docker-compose" off \
"tlp" "TLP laptop power management" off \ "podman" "podman rootless containers with buildah" off \
"steam" "Steam gaming platform" off \ "cockpit" "cockpit web UI for machines and containers" off \
"vesktop" "Vesktop Discord + Vencord theme" off \ "tlp" "tlp laptop battery optimisation" off \
"spotify" "Spotify launcher + Spicetify theming" off \ "butter" "butter btrfs snapshot backup manager (AUR)" off \
"prism" "PrismLauncher Minecraft launcher (Flatpak)" off \ "localsend" "localsend LAN file transfer, AirDrop-like (AUR)" off \
"vintagestory" "Vintage Story survival game (AUR)" off \ "croc" "croc cross-platform encrypted file transfer" off \
"openarena" "OpenArena open-source Quake III Arena" off \ "opendeck" "opendeck Stream Deck controller — ydotool + OpenDeck (Flatpak)" off \
"tetris" "Tetris CLI bastet · vitetris" off \ "localtunnel" "localtunnel expose localhost over a public URL" off \
"doom" "Doom Chocolate Doom + Freedoom data" off \ "timeshift" "timeshift system snapshot and backup with autosnap" off \
"sauerbraten" "Sauerbraten open-source FPS (Cube 2)" off \ "zfs" "zfs zfs-dkms kernel module" off \
"stuntrally" "Stunt Rally rally racing game (Flatpak)" off \ "wprs" "wprs Wayland proxy for remote sessions (wprs-git, AUR)" off \
"localsend" "LocalSend LAN file transfer (AUR)" off \ "plymouth-custom" "plymouth-custom boot splash with a user-supplied image" off \
"croc" "croc cross-platform file transfer" off \ "steam" "steam Steam gaming platform" off \
"opendeck" "OpenDeck Stream Deck controller (Flatpak+ydotool)" off \ "vesktop" "vesktop Discord client with Vencord theme" off \
"onlyoffice" "OnlyOffice office suite (AUR)" off \ "spotify" "spotify Spotify launcher with Spicetify theming" off \
"xournal" "Xournal++ note-taking & PDF annotator" off \ "prism" "prism PrismLauncher Minecraft launcher (Flatpak)" off \
"rnote" "Rnote handwriting & note-taking (Flatpak)" off \ "vintagestory" "vintagestory Vintage Story survival game (AUR)" off \
"obsidian" "Obsidian knowledge base & Markdown notes (Flatpak)" off \ "openarena" "openarena open-source Quake III Arena" off \
"tangent-notes" "Tangent Notes networked Markdown notes (Flatpak)" off \ "tetris" "tetris bastet and vitetris terminal Tetris" off \
"gimp" "GIMP GNU image manipulation program" off \ "doom" "doom Chocolate Doom with Freedoom data" off \
"inkscape" "Inkscape vector graphics editor" off \ "sauerbraten" "sauerbraten Sauerbraten open-source FPS (Cube 2)" off \
"krita" "Krita digital painting application" off \ "stuntrally" "stuntrally Stunt Rally racing game (Flatpak)" off \
"ardour" "Ardour professional DAW" off \ "onlyoffice" "onlyoffice office suite — Docs, Sheets, Slides (AUR)" on \
"audacity" "Audacity multi-track audio editor" off \ "xournal" "xournal note-taking and PDF annotator" off \
"lmms" "LMMS Linux MultiMedia Studio DAW" off \ "rnote" "rnote handwriting and note-taking with stylus support (Flatpak)" off \
"mixxx" "Mixxx DJ mixing software" off \ "obsidian" "obsidian knowledge base and Markdown note-taking (Flatpak)" off \
"cecilia" "Cecilia audio signal processing (AUR)" off \ "tangent-notes" "tangent-notes networked Markdown note-taking (Flatpak)" off \
"kdenlive" "Kdenlive KDE non-linear video editor" off \ "ffmpeg" "ffmpeg GStreamer codecs and ffmpegthumbnailer" off \
"openshot" "OpenShot easy video editor" off \ "sox" "sox command-line audio processing toolkit" off \
"shotcut" "Shotcut cross-platform video editor" off \ "imagemagick" "imagemagick image manipulation suite" off \
"anti-malware" "Anti-Malware ClamAV · rkhunter · chkrootkit" off \ "yt-dlp" "yt-dlp YouTube and media downloader" off \
"timeshift" "Timeshift system snapshot / backup + autosnap" off \ "gimp" "gimp GNU Image Manipulation Program" off \
"wireshark" "Wireshark network packet analyser (GUI)" off \ "inkscape" "inkscape vector graphics editor" off \
"k8s" "Kubernetes tools kubectl · podman-desktop" off \ "krita" "krita digital painting and illustration" off \
"docker" "Docker docker · docker-compose" off \ "kdenlive" "kdenlive KDE non-linear video editor" off \
"podman" "Podman rootless containers · buildah" off \ "openshot" "openshot cross-platform video editor" off \
"cockpit" "Cockpit web UI · machines · podman" off \ "shotcut" "shotcut cross-platform video editor" off \
"ssh-server" "SSH server openssh · key-auth · enabled" off \ "ardour" "ardour professional DAW" off \
"freeipa-client" "FreeIPA Client sssd + ipa-client-install + enrollment" off \ "audacity" "audacity multi-track audio editor" off \
"freeipa-server" "FreeIPA Server interactive server setup + client gen" off \ "lmms" "lmms Linux MultiMedia Studio music production" off \
"freeipa-image" "FreeIPA Image OCI/LXC/Proxmox LXC builder + Keycloak" off \ "mixxx" "mixxx DJ mixing software" off \
"python" "Python tools pyright · pipx · pynvim" off \ "cecilia" "cecilia audio synthesis and signal processing (AUR)" off \
"zfs" "ZFS zfs-dkms kernel module" off \ "chromium" "chromium open-source Chromium browser (official)" off \
"wprs" "WPRS wprs-git (AUR)" off \ "firefox-browser" "firefox-browser Mozilla Firefox (official)" on \
\ "zen-browser" "zen-browser privacy-focused Firefox fork (AUR)" off \
"chromium" "Chromium open-source browser (official)" off \ "nyxt" "nyxt keyboard-driven hackable browser (AUR)" off \
"firefox-browser" "Firefox Mozilla browser (official)" off \ "librewolf" "librewolf hardened Firefox fork (AUR)" off \
"zen-browser" "Zen Browser Firefox-based privacy browser (AUR)" off \ "min-browser" "min-browser minimal Electron browser (AUR)" off \
"nyxt" "Nyxt keyboard-driven browser (AUR)" off \ "vscodium" "vscodium telemetry-free VS Code build (AUR)" off \
"librewolf" "LibreWolf hardened Firefox fork (AUR)" off \ "zed-ide" "zed-ide high-performance Rust IDE (official)" off \
"min-browser" "Min minimal Electron browser (AUR)" off \ "geany" "geany lightweight IDE with plugins (official)" off \
\ "codeblocks" "codeblocks C/C++ IDE (official)" off \
"vscodium" "VSCodium telemetry-free VS Code (AUR)" off \ "kate" "kate KDE advanced text editor (official)" off \
"zed-ide" "Zed high-performance Rust IDE (official)" off \ "rdp-client" "rdp-client Remmina with FreeRDP and VNC plugins" off \
"geany" "Geany lightweight IDE + plugins (official)" off \ "lamco-rdp-server" "lamco-rdp-server native Wayland RDP server (AUR, Rust)" off \
"codeblocks" "Code::Blocks C/C++ IDE (official)" off \ "qemu" "qemu full QEMU/KVM stack with virt-manager" off \
"kate" "Kate KDE advanced text editor (official)" off \ "freeipa-client" "freeipa-client sssd and ipa-client-install with auto-enrollment" off \
\ "freeipa-server" "freeipa-server interactive FreeIPA server setup with client generator" off \
"rdp-client" "RDP Client Remmina + FreeRDP + VNC plugins" off \ "freeipa-image" "freeipa-image OCI/LXC/Proxmox image builder with Keycloak" off \
"lamco-rdp-server" "Lamco RDP Server native Wayland RDP server (AUR, Rust)" off \
"qemu" "QEMU/KVM full virt stack + virt-manager GUI" off \
# Esc / Cancel yields exit code 1; treat it as "no optional apps selected"
# rather than a hard abort so the user can still install base components.
3>&1 1>&2 2>&3) || SELECTED_APPS="" 3>&1 1>&2 2>&3) || SELECTED_APPS=""
# END GENERATED MODULES: module-checklist
fi fi
# ── Shell RC preference ─────────────────────────────────────────────────────── # ── Shell RC preference ───────────────────────────────────────────────────────
@ -560,83 +561,88 @@ if ! $ANSWERFILE_MODE; then
if [[ -n "$SELECTED_APPS" ]]; then if [[ -n "$SELECTED_APPS" ]]; then
SUMMARY+="\n Applications:\n" SUMMARY+="\n Applications:\n"
[[ "$SELECTED_APPS" == *"ollama"* ]] && SUMMARY+=" ✦ Ollama\n" # BEGIN GENERATED MODULES: module-summary
[[ "$SELECTED_APPS" == *"llama-cpp"* ]] && SUMMARY+=" ✦ llama.cpp\n" [[ "$SELECTED_APPS" == *"ollama"* ]] && SUMMARY+=" ✦ ollama\n"
[[ "$SELECTED_APPS" == *"open-webui"* ]] && SUMMARY+=" ✦ Open WebUI\n" [[ "$SELECTED_APPS" == *"llama-cpp"* ]] && SUMMARY+=" ✦ llama-cpp\n"
[[ "$SELECTED_APPS" == *"claude"* ]] && SUMMARY+=" ✦ Claude Code\n" [[ "$SELECTED_APPS" == *"open-webui"* ]] && SUMMARY+=" ✦ open-webui\n"
[[ "$SELECTED_APPS" == *"networking-cli"* ]] && SUMMARY+=" ✦ Networking CLI (nmap, nethogs, mitmproxy, httpie)\n" [[ "$SELECTED_APPS" == *"claude"* ]] && SUMMARY+=" ✦ claude\n"
[[ "$SELECTED_APPS" == *"disk-recovery"* ]] && SUMMARY+=" ✦ Disk Recovery (ddrescue, f3)\n" [[ "$SELECTED_APPS" == *"networking-cli"* ]] && SUMMARY+=" ✦ networking-cli\n"
[[ "$SELECTED_APPS" == *"himalaya"* ]] && SUMMARY+=" ✦ Himalaya\n" [[ "$SELECTED_APPS" == *"disk-recovery"* ]] && SUMMARY+=" ✦ disk-recovery\n"
[[ "$SELECTED_APPS" == *"gnuplot"* ]] && SUMMARY+=" ✦ Gnuplot\n" [[ "$SELECTED_APPS" == *"himalaya"* ]] && SUMMARY+=" ✦ himalaya\n"
[[ "$SELECTED_APPS" == *"blender-povray"* ]] && SUMMARY+=" ✦ Blender + POV-Ray\n" [[ "$SELECTED_APPS" == *"mail-notmuch"* ]] && SUMMARY+=" ✦ mail-notmuch\n"
[[ "$SELECTED_APPS" == *"toot"* ]] && SUMMARY+=" ✦ toot\n" [[ "$SELECTED_APPS" == *"caldav-sync"* ]] && SUMMARY+=" ✦ caldav-sync\n"
[[ "$SELECTED_APPS" == *"db-clients"* ]] && SUMMARY+=" ✦ DB Clients (pgcli, mycli)\n" [[ "$SELECTED_APPS" == *"ssh-server"* ]] && SUMMARY+=" ✦ ssh-server\n"
[[ "$SELECTED_APPS" == *"mysql"* ]] && SUMMARY+=" ✦ MySQL / MariaDB\n" [[ "$SELECTED_APPS" == *"wireshark"* ]] && SUMMARY+=" ✦ wireshark\n"
[[ "$SELECTED_APPS" == *"productivity"* ]] && SUMMARY+=" ✦ Productivity (taskwarrior, watson, jrnl)\n" [[ "$SELECTED_APPS" == *"anti-malware"* ]] && SUMMARY+=" ✦ anti-malware\n"
[[ "$SELECTED_APPS" == *"yt-dlp"* ]] && SUMMARY+=" ✦ yt-dlp\n" [[ "$SELECTED_APPS" == *"gnuplot"* ]] && SUMMARY+=" ✦ gnuplot\n"
[[ "$SELECTED_APPS" == *"sox"* ]] && SUMMARY+=" ✦ SoX\n" [[ "$SELECTED_APPS" == *"blender-povray"* ]] && SUMMARY+=" ✦ blender-povray\n"
[[ "$SELECTED_APPS" == *"imagemagick"* ]] && SUMMARY+=" ✦ ImageMagick\n" [[ "$SELECTED_APPS" == *"toot"* ]] && SUMMARY+=" ✦ toot\n"
[[ "$SELECTED_APPS" == *"ffmpeg"* ]] && SUMMARY+=" ✦ FFmpeg extras\n" [[ "$SELECTED_APPS" == *"db-clients"* ]] && SUMMARY+=" ✦ db-clients\n"
[[ "$SELECTED_APPS" == *"localtunnel"* ]] && SUMMARY+=" ✦ LocalTunnel\n" [[ "$SELECTED_APPS" == *"mysql"* ]] && SUMMARY+=" ✦ mysql\n"
[[ "$SELECTED_APPS" == *"butter"* ]] && SUMMARY+=" ✦ butter (btrfs backup)\n" [[ "$SELECTED_APPS" == *"productivity"* ]] && SUMMARY+=" ✦ productivity\n"
[[ "$SELECTED_APPS" == *"tlp"* ]] && SUMMARY+=" ✦ TLP\n" [[ "$SELECTED_APPS" == *"python"* ]] && SUMMARY+=" ✦ python\n"
[[ "$SELECTED_APPS" == *"steam"* ]] && SUMMARY+=" ✦ Steam\n" [[ "$SELECTED_APPS" == *"k8s"* ]] && SUMMARY+=" ✦ k8s\n"
[[ "$SELECTED_APPS" == *"vesktop"* ]] && SUMMARY+=" ✦ Vesktop + Vencord theme\n" [[ "$SELECTED_APPS" == *"docker"* ]] && SUMMARY+=" ✦ docker\n"
[[ "$SELECTED_APPS" == *"spotify"* ]] && SUMMARY+=" ✦ Spotify + Spicetify\n" [[ "$SELECTED_APPS" == *"podman"* ]] && SUMMARY+=" ✦ podman\n"
[[ "$SELECTED_APPS" == *"prism"* ]] && SUMMARY+=" ✦ PrismLauncher\n" [[ "$SELECTED_APPS" == *"cockpit"* ]] && SUMMARY+=" ✦ cockpit\n"
[[ "$SELECTED_APPS" == *"vintagestory"* ]] && SUMMARY+=" ✦ Vintage Story\n" [[ "$SELECTED_APPS" == *"tlp"* ]] && SUMMARY+=" ✦ tlp\n"
[[ "$SELECTED_APPS" == *"openarena"* ]] && SUMMARY+=" ✦ OpenArena\n" [[ "$SELECTED_APPS" == *"butter"* ]] && SUMMARY+=" ✦ butter\n"
[[ "$SELECTED_APPS" == *"tetris"* ]] && SUMMARY+=" ✦ Tetris CLI (bastet · vitetris)\n" [[ "$SELECTED_APPS" == *"localsend"* ]] && SUMMARY+=" ✦ localsend\n"
[[ "$SELECTED_APPS" == *"doom"* ]] && SUMMARY+=" ✦ Doom\n" [[ "$SELECTED_APPS" == *"croc"* ]] && SUMMARY+=" ✦ croc\n"
[[ "$SELECTED_APPS" == *"sauerbraten"* ]] && SUMMARY+=" ✦ Sauerbraten\n" [[ "$SELECTED_APPS" == *"opendeck"* ]] && SUMMARY+=" ✦ opendeck\n"
[[ "$SELECTED_APPS" == *"stuntrally"* ]] && SUMMARY+=" ✦ Stunt Rally\n" [[ "$SELECTED_APPS" == *"localtunnel"* ]] && SUMMARY+=" ✦ localtunnel\n"
[[ "$SELECTED_APPS" == *"localsend"* ]] && SUMMARY+=" ✦ LocalSend\n" [[ "$SELECTED_APPS" == *"timeshift"* ]] && SUMMARY+=" ✦ timeshift\n"
[[ "$SELECTED_APPS" == *"croc"* ]] && SUMMARY+=" ✦ croc\n" [[ "$SELECTED_APPS" == *"zfs"* ]] && SUMMARY+=" ✦ zfs\n"
[[ "$SELECTED_APPS" == *"opendeck"* ]] && SUMMARY+=" ✦ OpenDeck + ydotool\n" [[ "$SELECTED_APPS" == *"wprs"* ]] && SUMMARY+=" ✦ wprs\n"
[[ "$SELECTED_APPS" == *"onlyoffice"* ]] && SUMMARY+=" ✦ OnlyOffice\n" [[ "$SELECTED_APPS" == *"plymouth-custom"* ]] && SUMMARY+=" ✦ plymouth-custom\n"
[[ "$SELECTED_APPS" == *"xournal"* ]] && SUMMARY+=" ✦ Xournal++\n" [[ "$SELECTED_APPS" == *"steam"* ]] && SUMMARY+=" ✦ steam\n"
[[ "$SELECTED_APPS" == *"rnote"* ]] && SUMMARY+=" ✦ Rnote\n" [[ "$SELECTED_APPS" == *"vesktop"* ]] && SUMMARY+=" ✦ vesktop\n"
[[ "$SELECTED_APPS" == *"obsidian"* ]] && SUMMARY+=" ✦ Obsidian\n" [[ "$SELECTED_APPS" == *"spotify"* ]] && SUMMARY+=" ✦ spotify\n"
[[ "$SELECTED_APPS" == *"tangent-notes"* ]] && SUMMARY+=" ✦ Tangent Notes\n" [[ "$SELECTED_APPS" == *"prism"* ]] && SUMMARY+=" ✦ prism\n"
[[ "$SELECTED_APPS" == *"gimp"* ]] && SUMMARY+=" ✦ GIMP\n" [[ "$SELECTED_APPS" == *"vintagestory"* ]] && SUMMARY+=" ✦ vintagestory\n"
[[ "$SELECTED_APPS" == *"inkscape"* ]] && SUMMARY+=" ✦ Inkscape\n" [[ "$SELECTED_APPS" == *"openarena"* ]] && SUMMARY+=" ✦ openarena\n"
[[ "$SELECTED_APPS" == *"krita"* ]] && SUMMARY+=" ✦ Krita\n" [[ "$SELECTED_APPS" == *"tetris"* ]] && SUMMARY+=" ✦ tetris\n"
[[ "$SELECTED_APPS" == *"ardour"* ]] && SUMMARY+=" ✦ Ardour\n" [[ "$SELECTED_APPS" == *"doom"* ]] && SUMMARY+=" ✦ doom\n"
[[ "$SELECTED_APPS" == *"audacity"* ]] && SUMMARY+=" ✦ Audacity\n" [[ "$SELECTED_APPS" == *"sauerbraten"* ]] && SUMMARY+=" ✦ sauerbraten\n"
[[ "$SELECTED_APPS" == *"lmms"* ]] && SUMMARY+=" ✦ LMMS\n" [[ "$SELECTED_APPS" == *"stuntrally"* ]] && SUMMARY+=" ✦ stuntrally\n"
[[ "$SELECTED_APPS" == *"mixxx"* ]] && SUMMARY+=" ✦ Mixxx\n" [[ "$SELECTED_APPS" == *"onlyoffice"* ]] && SUMMARY+=" ✦ onlyoffice\n"
[[ "$SELECTED_APPS" == *"cecilia"* ]] && SUMMARY+=" ✦ Cecilia\n" [[ "$SELECTED_APPS" == *"xournal"* ]] && SUMMARY+=" ✦ xournal\n"
[[ "$SELECTED_APPS" == *"kdenlive"* ]] && SUMMARY+=" ✦ Kdenlive\n" [[ "$SELECTED_APPS" == *"rnote"* ]] && SUMMARY+=" ✦ rnote\n"
[[ "$SELECTED_APPS" == *"openshot"* ]] && SUMMARY+=" ✦ OpenShot\n" [[ "$SELECTED_APPS" == *"obsidian"* ]] && SUMMARY+=" ✦ obsidian\n"
[[ "$SELECTED_APPS" == *"shotcut"* ]] && SUMMARY+=" ✦ Shotcut\n" [[ "$SELECTED_APPS" == *"tangent-notes"* ]] && SUMMARY+=" ✦ tangent-notes\n"
[[ "$SELECTED_APPS" == *"anti-malware"* ]] && SUMMARY+=" ✦ Anti-Malware (ClamAV, rkhunter, chkrootkit)\n" [[ "$SELECTED_APPS" == *"ffmpeg"* ]] && SUMMARY+=" ✦ ffmpeg\n"
[[ "$SELECTED_APPS" == *"timeshift"* ]] && SUMMARY+=" ✦ Timeshift\n" [[ "$SELECTED_APPS" == *"sox"* ]] && SUMMARY+=" ✦ sox\n"
[[ "$SELECTED_APPS" == *"wireshark"* ]] && SUMMARY+=" ✦ Wireshark\n" [[ "$SELECTED_APPS" == *"imagemagick"* ]] && SUMMARY+=" ✦ imagemagick\n"
[[ "$SELECTED_APPS" == *"k8s"* ]] && SUMMARY+=" ✦ Kubernetes tools\n" [[ "$SELECTED_APPS" == *"yt-dlp"* ]] && SUMMARY+=" ✦ yt-dlp\n"
[[ "$SELECTED_APPS" == *"docker"* ]] && SUMMARY+=" ✦ Docker + Compose\n" [[ "$SELECTED_APPS" == *"gimp"* ]] && SUMMARY+=" ✦ gimp\n"
[[ "$SELECTED_APPS" == *"podman"* ]] && SUMMARY+=" ✦ Podman (rootless) + Buildah\n" [[ "$SELECTED_APPS" == *"inkscape"* ]] && SUMMARY+=" ✦ inkscape\n"
[[ "$SELECTED_APPS" == *"cockpit"* ]] && SUMMARY+=" ✦ Cockpit web UI\n" [[ "$SELECTED_APPS" == *"krita"* ]] && SUMMARY+=" ✦ krita\n"
[[ "$SELECTED_APPS" == *"ssh-server"* ]] && SUMMARY+=" ✦ SSH server (openssh, key auth)\n" [[ "$SELECTED_APPS" == *"kdenlive"* ]] && SUMMARY+=" ✦ kdenlive\n"
[[ "$SELECTED_APPS" == *"freeipa-client"* ]] && SUMMARY+=" ✦ FreeIPA Client\n" [[ "$SELECTED_APPS" == *"openshot"* ]] && SUMMARY+=" ✦ openshot\n"
[[ "$SELECTED_APPS" == *"freeipa-server"* ]] && SUMMARY+=" ✦ FreeIPA Server\n" [[ "$SELECTED_APPS" == *"shotcut"* ]] && SUMMARY+=" ✦ shotcut\n"
[[ "$SELECTED_APPS" == *"freeipa-image"* ]] && SUMMARY+=" ✦ FreeIPA Image Builder\n" [[ "$SELECTED_APPS" == *"ardour"* ]] && SUMMARY+=" ✦ ardour\n"
[[ "$SELECTED_APPS" == *"python"* ]] && SUMMARY+=" ✦ Python tools\n" [[ "$SELECTED_APPS" == *"audacity"* ]] && SUMMARY+=" ✦ audacity\n"
[[ "$SELECTED_APPS" == *"zfs"* ]] && SUMMARY+=" ✦ ZFS\n" [[ "$SELECTED_APPS" == *"lmms"* ]] && SUMMARY+=" ✦ lmms\n"
[[ "$SELECTED_APPS" == *"wprs"* ]] && SUMMARY+=" ✦ WPRS\n" [[ "$SELECTED_APPS" == *"mixxx"* ]] && SUMMARY+=" ✦ mixxx\n"
[[ "$SELECTED_APPS" == *"chromium"* ]] && SUMMARY+=" ✦ Chromium\n" [[ "$SELECTED_APPS" == *"cecilia"* ]] && SUMMARY+=" ✦ cecilia\n"
[[ "$SELECTED_APPS" == *"firefox-browser"* ]] && SUMMARY+=" ✦ Firefox\n" [[ "$SELECTED_APPS" == *"chromium"* ]] && SUMMARY+=" ✦ chromium\n"
[[ "$SELECTED_APPS" == *"zen-browser"* ]] && SUMMARY+=" ✦ Zen Browser\n" [[ "$SELECTED_APPS" == *"firefox-browser"* ]] && SUMMARY+=" ✦ firefox-browser\n"
[[ "$SELECTED_APPS" == *"nyxt"* ]] && SUMMARY+=" ✦ Nyxt\n" [[ "$SELECTED_APPS" == *"zen-browser"* ]] && SUMMARY+=" ✦ zen-browser\n"
[[ "$SELECTED_APPS" == *"librewolf"* ]] && SUMMARY+=" ✦ LibreWolf\n" [[ "$SELECTED_APPS" == *"nyxt"* ]] && SUMMARY+=" ✦ nyxt\n"
[[ "$SELECTED_APPS" == *"min-browser"* ]] && SUMMARY+=" ✦ Min Browser\n" [[ "$SELECTED_APPS" == *"librewolf"* ]] && SUMMARY+=" ✦ librewolf\n"
[[ "$SELECTED_APPS" == *"vscodium"* ]] && SUMMARY+=" ✦ VSCodium\n" [[ "$SELECTED_APPS" == *"min-browser"* ]] && SUMMARY+=" ✦ min-browser\n"
[[ "$SELECTED_APPS" == *"zed-ide"* ]] && SUMMARY+=" ✦ Zed IDE\n" [[ "$SELECTED_APPS" == *"vscodium"* ]] && SUMMARY+=" ✦ vscodium\n"
[[ "$SELECTED_APPS" == *"geany"* ]] && SUMMARY+=" ✦ Geany\n" [[ "$SELECTED_APPS" == *"zed-ide"* ]] && SUMMARY+=" ✦ zed-ide\n"
[[ "$SELECTED_APPS" == *"codeblocks"* ]] && SUMMARY+=" ✦ Code::Blocks\n" [[ "$SELECTED_APPS" == *"geany"* ]] && SUMMARY+=" ✦ geany\n"
[[ "$SELECTED_APPS" == *"kate"* ]] && SUMMARY+=" ✦ Kate\n" [[ "$SELECTED_APPS" == *"codeblocks"* ]] && SUMMARY+=" ✦ codeblocks\n"
[[ "$SELECTED_APPS" == *"rdp-client"* ]] && SUMMARY+=" ✦ RDP Client (Remmina + FreeRDP)\n" [[ "$SELECTED_APPS" == *"kate"* ]] && SUMMARY+=" ✦ kate\n"
[[ "$SELECTED_APPS" == *"lamco-rdp-server"* ]] && SUMMARY+=" ✦ Lamco RDP Server (native Wayland)\n" [[ "$SELECTED_APPS" == *"rdp-client"* ]] && SUMMARY+=" ✦ rdp-client\n"
[[ "$SELECTED_APPS" == *"qemu"* ]] && SUMMARY+=" ✦ QEMU/KVM + virt-manager\n" [[ "$SELECTED_APPS" == *"lamco-rdp-server"* ]] && SUMMARY+=" ✦ lamco-rdp-server\n"
[[ "$SELECTED_APPS" == *"qemu"* ]] && SUMMARY+=" ✦ qemu\n"
[[ "$SELECTED_APPS" == *"freeipa-client"* ]] && SUMMARY+=" ✦ freeipa-client\n"
[[ "$SELECTED_APPS" == *"freeipa-server"* ]] && SUMMARY+=" ✦ freeipa-server\n"
[[ "$SELECTED_APPS" == *"freeipa-image"* ]] && SUMMARY+=" ✦ freeipa-image\n"
# END GENERATED MODULES: module-summary
fi fi
# Size the confirmation dialog to the terminal, capped at 24 rows. # Size the confirmation dialog to the terminal, capped at 24 rows.
@ -680,85 +686,94 @@ fi
# Same guard pattern as base components. Each line is independent; a missing # Same guard pattern as base components. Each line is independent; a missing
# module script will be caught by run_module()'s error handling rather than # module script will be caught by run_module()'s error handling rather than
# silently skipped — 'bash "$script"' will exit non-zero if the file is absent. # silently skipped — 'bash "$script"' will exit non-zero if the file is absent.
[[ "$SELECTED_APPS" == *"ollama"* ]] && run_module "Ollama" "$APPS/ollama.sh" # BEGIN GENERATED MODULES: module-conflicts
[[ "$SELECTED_APPS" == *"llama-cpp"* ]] && run_module "llama.cpp" "$APPS/llama-cpp.sh" if [[ "$SELECTED_APPS" == *"plymouth-custom"* && "$SELECTED_APPS" == *"plymouth"* ]]; then
[[ "$SELECTED_APPS" == *"open-webui"* ]] && run_module "Open WebUI" "$APPS/open-webui.sh" warn "plymouth-custom and plymouth are mutually exclusive — skipping plymouth"
[[ "$SELECTED_APPS" == *"claude"* ]] && run_module "Claude Code" "$APPS/claude.sh" SELECTED_APPS="${SELECTED_APPS/plymouth/}"
[[ "$SELECTED_APPS" == *"networking-cli"* ]] && run_module "Networking CLI" "$APPS/networking-cli.sh" fi
[[ "$SELECTED_APPS" == *"disk-recovery"* ]] && run_module "Disk Recovery" "$APPS/disk-recovery.sh" # END GENERATED MODULES: module-conflicts
[[ "$SELECTED_APPS" == *"himalaya"* ]] && run_module "Himalaya" "$APPS/himalaya.sh" # BEGIN GENERATED MODULES: module-dispatch
[[ "$SELECTED_APPS" == *"mail-notmuch"* ]] && run_module "Mail (notmuch)" "$APPS/mail-notmuch.sh" [[ "$SELECTED_APPS" == *"ollama"* ]] && run_module "ollama" "$APPS/ollama.sh"
[[ "$SELECTED_APPS" == *"caldav-sync"* ]] && run_module "CalDAV Sync" "$APPS/caldav-sync.sh" [[ "$SELECTED_APPS" == *"llama-cpp"* ]] && run_module "llama-cpp" "$APPS/llama-cpp.sh"
[[ "$SELECTED_APPS" == *"gnuplot"* ]] && run_module "Gnuplot" "$APPS/gnuplot.sh" [[ "$SELECTED_APPS" == *"open-webui"* ]] && run_module "open-webui" "$APPS/open-webui.sh"
[[ "$SELECTED_APPS" == *"blender-povray"* ]] && run_module "Blender + POV-Ray" "$APPS/blender-povray.sh" [[ "$SELECTED_APPS" == *"claude"* ]] && run_module "claude" "$APPS/claude.sh"
[[ "$SELECTED_APPS" == *"toot"* ]] && run_module "toot" "$APPS/toot.sh" [[ "$SELECTED_APPS" == *"networking-cli"* ]] && run_module "networking-cli" "$APPS/networking-cli.sh"
[[ "$SELECTED_APPS" == *"db-clients"* ]] && run_module "DB Clients" "$APPS/db-clients.sh" [[ "$SELECTED_APPS" == *"disk-recovery"* ]] && run_module "disk-recovery" "$APPS/disk-recovery.sh"
[[ "$SELECTED_APPS" == *"mysql"* ]] && run_module "MySQL / MariaDB" "$APPS/mysql.sh" [[ "$SELECTED_APPS" == *"himalaya"* ]] && run_module "himalaya" "$APPS/himalaya.sh"
[[ "$SELECTED_APPS" == *"productivity"* ]] && run_module "Productivity" "$APPS/productivity.sh" [[ "$SELECTED_APPS" == *"mail-notmuch"* ]] && run_module "mail-notmuch" "$APPS/mail-notmuch.sh"
[[ "$SELECTED_APPS" == *"yt-dlp"* ]] && run_module "yt-dlp" "$APPS/yt-dlp.sh" [[ "$SELECTED_APPS" == *"caldav-sync"* ]] && run_module "caldav-sync" "$APPS/caldav-sync.sh"
[[ "$SELECTED_APPS" == *"sox"* ]] && run_module "SoX" "$APPS/sox.sh" [[ "$SELECTED_APPS" == *"ssh-server"* ]] && run_module "ssh-server" "$APPS/ssh-server.sh"
[[ "$SELECTED_APPS" == *"imagemagick"* ]] && run_module "ImageMagick" "$APPS/imagemagick.sh" [[ "$SELECTED_APPS" == *"wireshark"* ]] && run_module "wireshark" "$APPS/wireshark.sh"
[[ "$SELECTED_APPS" == *"ffmpeg"* ]] && run_module "FFmpeg extras" "$APPS/ffmpeg.sh" [[ "$SELECTED_APPS" == *"anti-malware"* ]] && run_module "anti-malware" "$APPS/anti-malware.sh"
[[ "$SELECTED_APPS" == *"localtunnel"* ]] && run_module "LocalTunnel" "$APPS/localtunnel.sh" [[ "$SELECTED_APPS" == *"gnuplot"* ]] && run_module "gnuplot" "$APPS/gnuplot.sh"
[[ "$SELECTED_APPS" == *"butter"* ]] && run_module "butter" "$APPS/butter.sh" [[ "$SELECTED_APPS" == *"blender-povray"* ]] && run_module "blender-povray" "$APPS/blender-povray.sh"
[[ "$SELECTED_APPS" == *"tlp"* ]] && run_module "TLP" "$APPS/tlp.sh" [[ "$SELECTED_APPS" == *"toot"* ]] && run_module "toot" "$APPS/toot.sh"
[[ "$SELECTED_APPS" == *"steam"* ]] && run_module "Steam" "$APPS/steam.sh" [[ "$SELECTED_APPS" == *"db-clients"* ]] && run_module "db-clients" "$APPS/db-clients.sh"
[[ "$SELECTED_APPS" == *"vesktop"* ]] && run_module "Vesktop" "$APPS/vesktop.sh" [[ "$SELECTED_APPS" == *"mysql"* ]] && run_module "mysql" "$APPS/mysql.sh"
[[ "$SELECTED_APPS" == *"spotify"* ]] && run_module "Spotify" "$APPS/spotify.sh" [[ "$SELECTED_APPS" == *"productivity"* ]] && run_module "productivity" "$APPS/productivity.sh"
[[ "$SELECTED_APPS" == *"prism"* ]] && run_module "PrismLauncher" "$APPS/prismlauncher.sh" [[ "$SELECTED_APPS" == *"python"* ]] && run_module "python" "$APPS/python.sh"
[[ "$SELECTED_APPS" == *"vintagestory"* ]] && run_module "Vintage Story" "$APPS/vintagestory.sh" [[ "$SELECTED_APPS" == *"k8s"* ]] && run_module "k8s" "$APPS/k8s.sh"
[[ "$SELECTED_APPS" == *"openarena"* ]] && run_module "OpenArena" "$APPS/openarena.sh" [[ "$SELECTED_APPS" == *"docker"* ]] && run_module "docker" "$APPS/docker.sh"
[[ "$SELECTED_APPS" == *"tetris"* ]] && run_module "Tetris CLI" "$APPS/tetris.sh" [[ "$SELECTED_APPS" == *"podman"* ]] && run_module "podman" "$APPS/podman.sh"
[[ "$SELECTED_APPS" == *"doom"* ]] && run_module "Doom" "$APPS/doom.sh" [[ "$SELECTED_APPS" == *"cockpit"* ]] && run_module "cockpit" "$APPS/cockpit.sh"
[[ "$SELECTED_APPS" == *"sauerbraten"* ]] && run_module "Sauerbraten" "$APPS/sauerbraten.sh" [[ "$SELECTED_APPS" == *"tlp"* ]] && run_module "tlp" "$APPS/tlp.sh"
[[ "$SELECTED_APPS" == *"stuntrally"* ]] && run_module "Stunt Rally" "$APPS/stuntrally.sh" [[ "$SELECTED_APPS" == *"butter"* ]] && run_module "butter" "$APPS/butter.sh"
[[ "$SELECTED_APPS" == *"localsend"* ]] && run_module "LocalSend" "$APPS/localsend.sh" [[ "$SELECTED_APPS" == *"localsend"* ]] && run_module "localsend" "$APPS/localsend.sh"
[[ "$SELECTED_APPS" == *"croc"* ]] && run_module "croc" "$APPS/croc.sh" [[ "$SELECTED_APPS" == *"croc"* ]] && run_module "croc" "$APPS/croc.sh"
[[ "$SELECTED_APPS" == *"opendeck"* ]] && run_module "OpenDeck" "$APPS/opendeck.sh" [[ "$SELECTED_APPS" == *"opendeck"* ]] && run_module "opendeck" "$APPS/opendeck.sh"
[[ "$SELECTED_APPS" == *"onlyoffice"* ]] && run_module "OnlyOffice" "$APPS/onlyoffice.sh" [[ "$SELECTED_APPS" == *"localtunnel"* ]] && run_module "localtunnel" "$APPS/localtunnel.sh"
[[ "$SELECTED_APPS" == *"xournal"* ]] && run_module "Xournal++" "$APPS/xournal.sh" [[ "$SELECTED_APPS" == *"timeshift"* ]] && run_module "timeshift" "$APPS/timeshift.sh"
[[ "$SELECTED_APPS" == *"rnote"* ]] && run_module "Rnote" "$APPS/rnote.sh" [[ "$SELECTED_APPS" == *"zfs"* ]] && run_module "zfs" "$APPS/zfs.sh"
[[ "$SELECTED_APPS" == *"obsidian"* ]] && run_module "Obsidian" "$APPS/obsidian.sh" [[ "$SELECTED_APPS" == *"wprs"* ]] && run_module "wprs" "$APPS/wprs.sh"
[[ "$SELECTED_APPS" == *"tangent-notes"* ]] && run_module "Tangent Notes" "$APPS/tangent-notes.sh" [[ "$SELECTED_APPS" == *"plymouth-custom"* ]] && run_module "plymouth-custom" "$APPS/plymouth-custom.sh"
[[ "$SELECTED_APPS" == *"gimp"* ]] && run_module "GIMP" "$APPS/gimp.sh" [[ "$SELECTED_APPS" == *"steam"* ]] && run_module "steam" "$APPS/steam.sh"
[[ "$SELECTED_APPS" == *"inkscape"* ]] && run_module "Inkscape" "$APPS/inkscape.sh" [[ "$SELECTED_APPS" == *"vesktop"* ]] && run_module "vesktop" "$APPS/vesktop.sh"
[[ "$SELECTED_APPS" == *"krita"* ]] && run_module "Krita" "$APPS/krita.sh" [[ "$SELECTED_APPS" == *"spotify"* ]] && run_module "spotify" "$APPS/spotify.sh"
[[ "$SELECTED_APPS" == *"ardour"* ]] && run_module "Ardour" "$APPS/ardour.sh" [[ "$SELECTED_APPS" == *"prism"* ]] && run_module "prism" "$APPS/prism.sh"
[[ "$SELECTED_APPS" == *"audacity"* ]] && run_module "Audacity" "$APPS/audacity.sh" [[ "$SELECTED_APPS" == *"vintagestory"* ]] && run_module "vintagestory" "$APPS/vintagestory.sh"
[[ "$SELECTED_APPS" == *"lmms"* ]] && run_module "LMMS" "$APPS/lmms.sh" [[ "$SELECTED_APPS" == *"openarena"* ]] && run_module "openarena" "$APPS/openarena.sh"
[[ "$SELECTED_APPS" == *"mixxx"* ]] && run_module "Mixxx" "$APPS/mixxx.sh" [[ "$SELECTED_APPS" == *"tetris"* ]] && run_module "tetris" "$APPS/tetris.sh"
[[ "$SELECTED_APPS" == *"cecilia"* ]] && run_module "Cecilia" "$APPS/cecilia.sh" [[ "$SELECTED_APPS" == *"doom"* ]] && run_module "doom" "$APPS/doom.sh"
[[ "$SELECTED_APPS" == *"kdenlive"* ]] && run_module "Kdenlive" "$APPS/kdenlive.sh" [[ "$SELECTED_APPS" == *"sauerbraten"* ]] && run_module "sauerbraten" "$APPS/sauerbraten.sh"
[[ "$SELECTED_APPS" == *"openshot"* ]] && run_module "OpenShot" "$APPS/openshot.sh" [[ "$SELECTED_APPS" == *"stuntrally"* ]] && run_module "stuntrally" "$APPS/stuntrally.sh"
[[ "$SELECTED_APPS" == *"shotcut"* ]] && run_module "Shotcut" "$APPS/shotcut.sh" [[ "$SELECTED_APPS" == *"onlyoffice"* ]] && run_module "onlyoffice" "$APPS/onlyoffice.sh"
[[ "$SELECTED_APPS" == *"anti-malware"* ]] && run_module "Anti-Malware" "$APPS/anti-malware.sh" [[ "$SELECTED_APPS" == *"xournal"* ]] && run_module "xournal" "$APPS/xournal.sh"
[[ "$SELECTED_APPS" == *"timeshift"* ]] && run_module "Timeshift" "$APPS/timeshift.sh" [[ "$SELECTED_APPS" == *"rnote"* ]] && run_module "rnote" "$APPS/rnote.sh"
[[ "$SELECTED_APPS" == *"wireshark"* ]] && run_module "Wireshark" "$APPS/wireshark.sh" [[ "$SELECTED_APPS" == *"obsidian"* ]] && run_module "obsidian" "$APPS/obsidian.sh"
[[ "$SELECTED_APPS" == *"k8s"* ]] && run_module "Kubernetes Tools" "$APPS/k8s.sh" [[ "$SELECTED_APPS" == *"tangent-notes"* ]] && run_module "tangent-notes" "$APPS/tangent-notes.sh"
[[ "$SELECTED_APPS" == *"docker"* ]] && run_module "Docker" "$APPS/docker.sh" [[ "$SELECTED_APPS" == *"ffmpeg"* ]] && run_module "ffmpeg" "$APPS/ffmpeg.sh"
[[ "$SELECTED_APPS" == *"podman"* ]] && run_module "Podman" "$APPS/podman.sh" [[ "$SELECTED_APPS" == *"sox"* ]] && run_module "sox" "$APPS/sox.sh"
[[ "$SELECTED_APPS" == *"cockpit"* ]] && run_module "Cockpit" "$APPS/cockpit.sh" [[ "$SELECTED_APPS" == *"imagemagick"* ]] && run_module "imagemagick" "$APPS/imagemagick.sh"
[[ "$SELECTED_APPS" == *"ssh-server"* ]] && run_module "SSH Server" "$APPS/ssh-server.sh" [[ "$SELECTED_APPS" == *"yt-dlp"* ]] && run_module "yt-dlp" "$APPS/yt-dlp.sh"
[[ "$SELECTED_APPS" == *"freeipa-client"* ]] && run_module "FreeIPA Client" "$APPS/freeipa-client.sh" [[ "$SELECTED_APPS" == *"gimp"* ]] && run_module "gimp" "$APPS/gimp.sh"
[[ "$SELECTED_APPS" == *"freeipa-server"* ]] && run_module "FreeIPA Server" "$APPS/freeipa-server.sh" [[ "$SELECTED_APPS" == *"inkscape"* ]] && run_module "inkscape" "$APPS/inkscape.sh"
[[ "$SELECTED_APPS" == *"freeipa-image"* ]] && run_module "FreeIPA Image" "$APPS/freeipa-image-builder.sh" [[ "$SELECTED_APPS" == *"krita"* ]] && run_module "krita" "$APPS/krita.sh"
[[ "$SELECTED_APPS" == *"python"* ]] && run_module "Python Tools" "$MODULES/optional-Modules/python.sh" [[ "$SELECTED_APPS" == *"kdenlive"* ]] && run_module "kdenlive" "$APPS/kdenlive.sh"
[[ "$SELECTED_APPS" == *"zfs"* ]] && run_module "ZFS" "$MODULES/optional-Modules/zfs.sh" [[ "$SELECTED_APPS" == *"openshot"* ]] && run_module "openshot" "$APPS/openshot.sh"
[[ "$SELECTED_APPS" == *"wprs"* ]] && run_module "WPRS" "$MODULES/optional-Modules/wprs.sh" [[ "$SELECTED_APPS" == *"shotcut"* ]] && run_module "shotcut" "$APPS/shotcut.sh"
[[ "$SELECTED_APPS" == *"chromium"* ]] && run_module "Chromium" "$APPS/chromium.sh" [[ "$SELECTED_APPS" == *"ardour"* ]] && run_module "ardour" "$APPS/ardour.sh"
[[ "$SELECTED_APPS" == *"firefox-browser"* ]] && run_module "Firefox" "$APPS/firefox.sh" [[ "$SELECTED_APPS" == *"audacity"* ]] && run_module "audacity" "$APPS/audacity.sh"
[[ "$SELECTED_APPS" == *"zen-browser"* ]] && run_module "Zen Browser" "$APPS/zen-browser.sh" [[ "$SELECTED_APPS" == *"lmms"* ]] && run_module "lmms" "$APPS/lmms.sh"
[[ "$SELECTED_APPS" == *"nyxt"* ]] && run_module "Nyxt" "$APPS/nyxt.sh" [[ "$SELECTED_APPS" == *"mixxx"* ]] && run_module "mixxx" "$APPS/mixxx.sh"
[[ "$SELECTED_APPS" == *"librewolf"* ]] && run_module "LibreWolf" "$APPS/librewolf.sh" [[ "$SELECTED_APPS" == *"cecilia"* ]] && run_module "cecilia" "$APPS/cecilia.sh"
[[ "$SELECTED_APPS" == *"min-browser"* ]] && run_module "Min Browser" "$APPS/min-browser.sh" [[ "$SELECTED_APPS" == *"chromium"* ]] && run_module "chromium" "$APPS/chromium.sh"
[[ "$SELECTED_APPS" == *"vscodium"* ]] && run_module "VSCodium" "$APPS/vscodium.sh" [[ "$SELECTED_APPS" == *"firefox-browser"* ]] && run_module "firefox-browser" "$APPS/firefox-browser.sh"
[[ "$SELECTED_APPS" == *"zed-ide"* ]] && run_module "Zed IDE" "$APPS/zed.sh" [[ "$SELECTED_APPS" == *"zen-browser"* ]] && run_module "zen-browser" "$APPS/zen-browser.sh"
[[ "$SELECTED_APPS" == *"geany"* ]] && run_module "Geany" "$APPS/geany.sh" [[ "$SELECTED_APPS" == *"nyxt"* ]] && run_module "nyxt" "$APPS/nyxt.sh"
[[ "$SELECTED_APPS" == *"codeblocks"* ]] && run_module "Code::Blocks" "$APPS/codeblocks.sh" [[ "$SELECTED_APPS" == *"librewolf"* ]] && run_module "librewolf" "$APPS/librewolf.sh"
[[ "$SELECTED_APPS" == *"kate"* ]] && run_module "Kate" "$APPS/kate.sh" [[ "$SELECTED_APPS" == *"min-browser"* ]] && run_module "min-browser" "$APPS/min-browser.sh"
[[ "$SELECTED_APPS" == *"rdp-client"* ]] && run_module "RDP Client" "$APPS/rdp-client.sh" [[ "$SELECTED_APPS" == *"vscodium"* ]] && run_module "vscodium" "$APPS/vscodium.sh"
[[ "$SELECTED_APPS" == *"lamco-rdp-server"* ]] && run_module "Lamco RDP Server" "$APPS/lamco-rdp-server.sh" [[ "$SELECTED_APPS" == *"zed-ide"* ]] && run_module "zed-ide" "$APPS/zed-ide.sh"
[[ "$SELECTED_APPS" == *"qemu"* ]] && run_module "QEMU/KVM" "$APPS/qemu.sh" [[ "$SELECTED_APPS" == *"geany"* ]] && run_module "geany" "$APPS/geany.sh"
[[ "$SELECTED_APPS" == *"codeblocks"* ]] && run_module "codeblocks" "$APPS/codeblocks.sh"
[[ "$SELECTED_APPS" == *"kate"* ]] && run_module "kate" "$APPS/kate.sh"
[[ "$SELECTED_APPS" == *"rdp-client"* ]] && run_module "rdp-client" "$APPS/rdp-client.sh"
[[ "$SELECTED_APPS" == *"lamco-rdp-server"* ]] && run_module "lamco-rdp-server" "$APPS/lamco-rdp-server.sh"
[[ "$SELECTED_APPS" == *"qemu"* ]] && run_module "qemu" "$APPS/qemu.sh"
[[ "$SELECTED_APPS" == *"freeipa-client"* ]] && run_module "freeipa-client" "$APPS/freeipa-client.sh"
[[ "$SELECTED_APPS" == *"freeipa-server"* ]] && run_module "freeipa-server" "$APPS/freeipa-server.sh"
[[ "$SELECTED_APPS" == *"freeipa-image"* ]] && run_module "freeipa-image" "$APPS/freeipa-image.sh"
# END GENERATED MODULES: module-dispatch
# ── Colorway (final step) ───────────────────────────────────────────────────── # ── Colorway (final step) ─────────────────────────────────────────────────────
# Read defaults from repo colors.conf for pre-population. # Read defaults from repo colors.conf for pre-population.