diff --git a/.gitignore b/.gitignore index 8a58357..33c93e3 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,9 @@ swaylock/* swayidle/* !swayidle/config +!waybar +!bin + # flameshot !flameshot flameshot/* diff --git a/audit/remaining.md b/audit/remaining.md new file mode 100644 index 0000000..c6468c7 --- /dev/null +++ b/audit/remaining.md @@ -0,0 +1,71 @@ +# Remaining Audit Findings + +Items already completed: zsh, starship, git, ghostty, tmux config + scripts. + +## Sway + +### Security + +- **[high]** waybar `custom/ssh-login` on-click runs `pkill -9 -t $(who | awk '{ print $2 }')`. Unquoted command substitution causes word-splitting across multiple TTYs. Use `pkill -HUP` instead of `-9`, and quote the substitution or target a specific TTY. +- **[medium]** `80-autostart.conf` starts `protonmail-bridge -n` as bare `exec`. Consider systemd user service for proper lifecycle management. +- **[medium]** swayidle timeout of 300s (5 min) is relatively long for an unattended workstation. +- **[medium]** `swaylock/config` lacks `show-failed-attempts` and `ignore-empty-password` verification. +- **[low]** `wlsunset -l 46.1 -L 14.5` exposes approximate geographic coordinates in public dotfiles. + +### Idiomacy + +- **[issue]** host.d include is before config.d/* in main sway config. Host files cannot reference `$variables` from `10-variables.conf`. Move host.d include after config.d/*. +- **[issue]** `41-theme-swayfx.conf` uses SwayFX-specific directives that error on stock sway. Gate or document. +- **[issue]** Volume keybindings mix `pactl` (mute toggle) and `pamixer` (volume up/down). Pick one consistently. Mic mute on F16 also uses `pactl` instead of `pamixer`. +- **[issue]** wob FIFO setup has race condition on sway restart. Consider wob 0.14+ `--socket` flag or `$XDG_RUNTIME_DIR/wob.sock` path. +- **[issue]** swayidle missing `lock` event handler (`lock 'swaylock -f'`). `loginctl lock-session` won't lock the screen without it. +- **[issue]** No idle inhibitor configured. Fullscreen video will trigger lock after timeout. Options: waybar `idle_inhibitor` module, `for_window` rule with `inhibit_idle fullscreen`, or `sway-audio-idle-inhibit`. + +### Waybar + +- **[issue]** `custom/ssh-login` polls every 1 second. Reduce to 10-30s. +- **[issue]** `custom/ssh-login` on-click uses `pkill -9` (SIGKILL). Use SIGHUP. +- **[dead]** `custom/power` module defined but not included in any bar's module list. +- **[issue]** `style.css` references `@define-color` names (`@gray`, `@background-light`, `@foreground`, `@red`, etc.) that are not defined in the file. They must come from an external GTK theme. Define them in `style.css` for self-containment or document the dependency. +- **[issue]** Hardcoded `#1e1e2e` (Catppuccin Mocha) in `#waybar .module` conflicts with gruvbox scheme. Leftover from a template. +- **[note]** `cpu` on-click hardcodes `ghostty -e htop` (waybar JSONC doesn't support sway variables). + +### Typos + +- `50-keybind.conf` line 13: `# programs'` — trailing apostrophe +- `50-keybind.conf` line 88: `# xf86-brightnes` — missing trailing 's' +- `80-autostart.conf` line 1: `# deamon` — should be "daemon" + +## Structure + +### .gitignore + +- **[dead]** `!alacritty` is tracked but alacritty is no longer used (ghostty replaced it). Remove or keep intentionally. +- **[issue]** `!waybar` and `!bin` un-ignore entire directories with no interior filter. Every other program explicitly whitelists files. Tighten to two-level pattern: + ``` + !waybar + waybar/* + !waybar/config.jsonc + !waybar/style.css + ``` +- **[note]** `!ghostty/themes`, `!sway/config.d`, `!sway/host.d` also un-ignore whole subdirectories. Intentional for sway (new drop-in files auto-tracked), worth noting for ghostty themes. + +### bin/ directory + +- `bin/waybar-custom-cider.sh` is the only script and is waybar-specific. Consider moving to `waybar/cider.sh` and updating the exec path in `waybar/config.jsonc`. + +### SSH preview duplication + +- `zsh/ssh-menu` defines `_ssh_menu_preview` and `tmux/tmux-ssher` defines `_preview`. Same function with cosmetic differences. Extract to a shared script (e.g. `bin/ssh-preview`) to eliminate drift. The `command -v host` guard is only in ssh-menu, not ssher. + +### Paths + +- `tmux.conf` hardcodes `~/.config/tmux/...` in run-shell bindings instead of `$XDG_CONFIG_HOME`. +- `flameshot.ini` hardcodes `/home/matej/screens` — breaks on other usernames/machines. +- `swaylock/config` and `sway/config.d/20-output.conf` reference `~/.assets/` — not XDG, but consistent with each other. + +### Consistency + +- Waybar CSS color variables depend on external GTK theme (see waybar section above). +- Swaylock uses `#000000`/`#ffffff` (black/white) instead of gruvbox. May be intentional for contrast. +- Alacritty config is dead weight if no longer used. diff --git a/bin/waybar-custom-cider.sh b/bin/waybar-custom-cider.sh new file mode 100755 index 0000000..54c5208 --- /dev/null +++ b/bin/waybar-custom-cider.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +url="http://localhost:10767/api/v1/playback" + +status_raw=$(curl -s --max-time 2 "$url/is-playing") + +# CASE 1: API is totally unreachable (App closed) +if [[ -z "$status_raw" ]]; then + echo '{"text": "offline", "class": "offline", "alt": "offline"}' + exit 0 +fi + +# Extract playing status, defaulting to false if null +is_playing=$(echo "$status_raw" | jq -r '.is_playing // false') + +# 2. Get song info +info_raw=$(curl -s --max-time 2 "$url/now-playing") + +# CASE 2 & 3: Handle empty metadata vs. active metadata +echo "$info_raw" | jq -c \ + --argjson is_playing "$is_playing" \ + ' + # Helper function to escape & for Pango + def pango_escape: sub("&"; "&"; "g"); + + if (.info.name == null or .info.name == "") then + {text: "idle", class: "paused"} + else + { + text: "\(.info.name | pango_escape) - \(.info.artistName | pango_escape)", + tooltip: "\(.info.albumName | pango_escape // "Unknown")", + class: (if $is_playing then "playing" else "paused" end) + } + end' diff --git a/sway/host.d/matej-tower.conf b/sway/host.d/tower.conf similarity index 100% rename from sway/host.d/matej-tower.conf rename to sway/host.d/tower.conf diff --git a/waybar/config.jsonc b/waybar/config.jsonc new file mode 100644 index 0000000..70fcd1a --- /dev/null +++ b/waybar/config.jsonc @@ -0,0 +1,214 @@ +[ + { + "layer": "top", + "position": "top", + "height": 30, + "spacing": 1, + "margin": 0, + "modules-left": [ + "sway/workspaces", "sway/mode" + ], + "modules-center": [ + "privacy", + "custom/ssh-login" + ], + "modules-right": [ + "custom/cider", + "pulseaudio", + "power-profiles-daemon", + "cpu", + "memory", + "temperature", + "backlight", + "battery", + "tray", + ], + // + "sway/workspaces": { + "disable-scroll": true, + "format": "{icon}", + "format-icons": { + "1": "I", + "2": "II", + "3": "III", + "4": "IV", + "5": "V", + "6": "VI", + "7": "VII", + "8": "VIII", + "9": "IX", + "10": "X", + }, + }, + "sway/mode": { + "format": "{}", + }, + "custom/cider": { + "format": "{}", + "return-type": "json", + "max-length": 18, + "interval": 1, + "tooltip": true, + "exec": "~/.config/bin/waybar-custom-cider.sh", + "on-click": "curl -s -X POST http://localhost:10767/api/v1/playback/playpause", + "on-click-right": "curl -s -X POST http://localhost:10767/api/v1/playback/next", + }, + "privacy": { + "icon-spacing": 4, + "icon-size": 18, + "transition-duration": 250, + "modules": [ + { + "type": "screenshare", + "tooltip": true, + "tooltip-icon-size": 24 + }, + { + "type": "audio-out", + "tooltip": true, + "tooltip-icon-size": 24 + }, + { + "type": "audio-in", + "tooltip": true, + "tooltip-icon-size": 24 + } + ], + "ignore-monitor": true, + "ignore": [ + { + "type": "audio-in", + "name": "cava" + }, + { + "type": "screenshare", + "name": "obs" + } + ] + }, + "tray": { + "icon-size": 18, + "spacing": 10, + }, + "cpu": { + "format": "cpu:{usage}%", + "tooltip": false, + "on-click": "ghostty -e htop", + }, + "memory": { + "interval": 10, + "format": "ram:{}%", + "tooltip-format": "total: {total:0.2f}GiB\nused: {used:0.2f}GiB\navailable: {avail:0.2f}GiB\nswap: {swapUsed:0.2f}/{swapTotal:0.2f}GiB", + "on-click": "ghostty -e htop", + }, + "temperature": { + // "thermal-zone": 2, + // "hwmon-path": "/sys/class/hwmon/hwmon2/temp1_input", + "critical-threshold": 80, + // "format-critical": "{temperatureC}°C {icon}", + "format": "{temperatureC}°C {icon}", + "format-icons": ["", "", ""], + "on-click": "ghostty -e s-tui", + "tooltip": false, + }, + "backlight": { + // "device": "acpi_video1", + "format": "{percent}% {icon}", + "format-icons": ["", "", "", "", "", "", "", "", ""], + "tooltip": false, + }, + "battery": { + "states": { + // "good": 95, + "warning": 30, + "critical": 15, + }, + "format": "{capacity}% {icon}", + "format-full": "{capacity}% {icon}", + "format-charging": "{capacity}% ", + "format-plugged": "{capacity}% ", + "format-alt": "{time} {icon}", + // "format-good": "", // An empty format will hide the module + // "format-full": "", + "format-icons": ["", "", "", "", ""], + }, + "power-profiles-daemon": { + "format": "{icon}", + "tooltip-format": "Power profile: {profile}\nDriver: {driver}", + "tooltip": true, + "format-icons": { + "default": "", + "performance": "", + "balanced": "", + "power-saver": "", + }, + }, + "network": { + // "interface": "wlp2*", // (Optional) To force the use of this interface + "format-wifi": "{essid} ({signalStrength}%) ", + "format-ethernet": "ipv4:{ipaddr}/{cidr}", + "tooltip-format": "{ifname} via {gwaddr} ", + "format-linked": "{ifname} (No IP) ", + "format-disconnected": "Disconnected ⚠", + "format-alt": "{ifname}: {ipaddr}/{cidr}", + }, + "pulseaudio": { + // "scroll-step": 1, // %, can be a float + "format": "vol:{volume}%/{format_source}", + "format-bluetooth": "{volume}% {icon} {format_source}", + "format-bluetooth-muted": " {icon} {format_source}", + "format-muted": " {format_source}", + "format-source": "{volume}%", + "format-source-muted": "", + "format-icons": { + "headphone": "", + "hands-free": "", + "headset": "", + "phone": "", + "portable": "", + "car": "", + "default": ["", "", ""], + }, + "on-click": "pavucontrol", + }, + "custom/power": { + "format": "⏻ ", + "tooltip": false, + "menu": "on-click", + "menu-file": "$HOME/.config/waybar/power_menu.xml", // Menu file in resources folder + "menu-actions": { + "shutdown": "shutdown", + "reboot": "reboot", + "suspend": "systemctl suspend", + "hibernate": "systemctl hibernate", + }, + }, + "custom/ssh-login": { + "format": "{}", + "interval": 1, + // "exec-if": "who", + "exec": "who | awk '{ print $2 }'", + "on-click": "pkill -9 -t $(who | awk '{ print $2 }')", + }, + }, + { + "layer": "top", + "position": "bottom", + "height": 30, + "spacing": 1, + "margin": 0, + "modules-right": [ + "network", + "clock", + ], + "network": { + "format-ethernet": "{ipaddr}/{cidr}", + "format-disconnected": "disconnected" + }, + "clock": { + "interval": 1, + "format": "{:%Y/%m/%d %H:%M:%S}", + "tooltip-format": "{calendar}", + }, + } +] diff --git a/waybar/style.css b/waybar/style.css new file mode 100644 index 0000000..0431a4d --- /dev/null +++ b/waybar/style.css @@ -0,0 +1,297 @@ +@define-color workspaces-color @gray; +@define-color workspaces-focused-bg @background-light; +@define-color workspaces-focused-fg @foreground; +@define-color workspaces-urgent-bg @red; +@define-color workspaces-urgent-fg @background; + +@define-color cider-color @red; +@define-color clock-color @foreground; +@define-color pulseaudio-color @blue; +@define-color pulseaudio-muted-color @gray; +@define-color network-color @magenta; +@define-color network-disconnected-color @red; +@define-color cpu-color @orange; +@define-color memory-color @cyan; +@define-color temperature-color @yellow; +@define-color temperature-critical-color @red; +@define-color backlight-color @white; +@define-color battery-color @foreground; +@define-color battery-charging-color @green; +@define-color battery-warning-color @yellow; +@define-color battery-critical-color @red; + +* { + font-family: "JetBrainsMono Nerd Font"; + font-size: 14px; + font-weight: bold; +} + +window#waybar { + color: #ffffff; + transition-property: background-color; + transition-duration: .5s; +} + +window#waybar.hidden { + opacity: 0.2; +} + +/* This targets every module */ +#waybar .module { + background-color: #1e1e2e; /* Your desired module color */ +} + +/* Specific grouping (Optional: if you want modules to stick together in islands) */ +.modules-left, .modules-center, .modules-right {} + +button { + box-shadow: none; + border: none; + border-radius: 0; +} + +button:hover { + background: inherit; + box-shadow: none; +} + +/* ----------------------------------------------------------------------------- + * Workspaces + * ----------------------------------------------------------------------------- */ +#workspaces button { + padding: 0 8px; + background-color: transparent; + color: @workspaces-color; + margin: 0; +} + +#workspaces button:hover { + background: @background-light; + color: @foreground; + box-shadow: none; +} + +/* The "Neovim Buffer" Look: + Active workspace is lighter background + cream text. + No flashy underlines. */ +#workspaces button.focused { + background-color: @background-light; + color: @workspaces-focused-fg; + box-shadow: none; +} + +#workspaces button.urgent { + background-color: @workspaces-urgent-bg; + color: @workspaces-urgent-fg; +} + +/* ----------------------------------------------------------------------------- + * Modules + * ----------------------------------------------------------------------------- */ +#mode, +#clock, +#battery, +#cpu, +#memory, +#disk, +#temperature, +#backlight, +#network, +#pulseaudio, +#wireplumber, +#custom-media, +#tray, +#idle_inhibitor, +#scratchpad, +#power-profiles-daemon, +#mpd, +#custom-cider { + color: @clock-color; + font-weight: bold; + padding: 0 10px; + margin: 0 2px; + background-color: transparent; + border: none; /* Removed the underlines for a cleaner look */ +} + +#window, +#workspaces { + margin: 0 4px; +} + +.modules-left > widget:first-child > #workspaces { margin-left: 0; } +.modules-right > widget:last-child > #workspaces { margin-right: 0; } + +/* ----------------------------------------------------------------------------- + * Module Specifics + * ----------------------------------------------------------------------------- */ + + +#battery { + color: @battery-color; +} + +#battery.charging, #battery.plugged { + color: @battery-charging-color; +} + +#battery.warning:not(.charging) { + color: @battery-warning-color; +} + +@keyframes blink { + to { + background-color: @red; + color: @background; + } +} + +#battery.critical:not(.charging) { + color: @battery-critical-color; + animation-name: blink; + animation-duration: 0.5s; + animation-timing-function: steps(12); + animation-iteration-count: infinite; + animation-direction: alternate; +} + +/* #cpu { */ +/* color: @cpu-color; */ +/* } */ +/**/ +/* #memory { */ +/* color: @memory-color; */ +/* } */ +/**/ +/* #disk { */ +/* color: @yellow; */ +/* } */ +/**/ +/* #backlight { */ +/* color: @backlight-color; */ +/* } */ +/**/ +/* #network { */ +/* color: @network-color; */ +/* } */ +/**/ +/* #network.disconnected { */ +/* color: @network-disconnected-color; */ +/* } */ +/**/ +/* #pulseaudio { */ +/* color: @pulseaudio-color; */ +/* } */ +/**/ +/* #pulseaudio.muted { */ +/* color: @pulseaudio-muted-color; */ +/* } */ +/**/ +/* #wireplumber { */ +/* color: @foreground; */ +/* } */ +/**/ +/* #wireplumber.muted { */ +/* color: @red; */ +/* } */ + +#custom-media { + color: @green; + min-width: 100px; +} + +#temperature { + color: @temperature-color; +} + +#temperature.critical { + color: @temperature-critical-color; +} + +#tray { + background-color: @background-light; + border-radius: 4px; /* Slight roundness for the tray container */ + padding: 0 8px; +} + +#tray > .passive { + -gtk-icon-effect: dim; +} + +#tray > .needs-attention { + -gtk-icon-effect: highlight; + color: @red; +} + +#idle_inhibitor { + color: @gray; +} + +#idle_inhibitor.activated { + color: @foreground; +} + +#mpd { + color: @green; +} + +#mpd.disconnected { + color: @red; +} + +#mpd.stopped { + color: @gray; +} + +#mpd.paused { + color: @blue; +} + +#language { + background: @background-light; + color: @foreground; + padding: 0 5px; + margin: 0 5px; + min-width: 16px; +} + +#keyboard-state { + color: @foreground; + padding: 0 0px; + margin: 0 5px; + min-width: 16px; +} + +#keyboard-state > label.locked { + color: @red; +} + +#scratchpad { + background: transparent; + color: @gray; +} + +#scratchpad.empty { + background-color: transparent; +} + +#privacy { + padding: 0; +} + +#privacy-item { + padding: 0 5px; + color: @foreground; +} + +#privacy-item.screenshare { + color: @orange; +} + +#privacy-item.audio-in { + color: @green; +} + +#privacy-item.audio-out { + color: @blue; +}