From 98ac3499d766d29207d56cbceabd45bb7e95674b Mon Sep 17 00:00:00 2001 From: jrosh Date: Sun, 16 Nov 2025 15:30:41 +0100 Subject: [PATCH] 16.11.25 --- arch_news.sh | 254 ----------------------------- lorem | 40 ----- niri/config.kdl | 17 +- rentry | 160 ------------------- speak.sh | 165 ------------------- waybar/config.jsonc | 133 +++++++--------- waybar/style.css | 377 ++++++++++++++++---------------------------- waylock | 47 ------ 8 files changed, 208 insertions(+), 985 deletions(-) delete mode 100755 arch_news.sh delete mode 100755 lorem delete mode 100755 rentry delete mode 100755 speak.sh delete mode 100755 waylock diff --git a/arch_news.sh b/arch_news.sh deleted file mode 100755 index 8aaadb7..0000000 --- a/arch_news.sh +++ /dev/null @@ -1,254 +0,0 @@ -#!/bin/bash - -# Arch Linux News Notifier -# Fetches latest news from Arch Linux RSS feed and shows notifications for new items - -# Configuration -RSS_URL="https://archlinux.org/feeds/news/" -CACHE_FILE="$HOME/.cache/arch-news-seen" -TEMP_FILE="/tmp/arch-news-latest" - -ICON_NORMAL="/usr/share/pixmaps/tabler--coffee.svg" -ICON_URGENT="/usr/share/pixmaps/charm--circle-warning.svg" - -# Create cache directory if it doesn't existnClick to open: -mkdir -p "$(dirname "$CACHE_FILE")" - -# Function to extract and format news items -parse_rss() { - # Debug: show what we're working with - if [[ "$DEBUG" == "1" ]]; then - echo "=== DEBUG: RSS file content preview ===" >&2 - head -20 "$TEMP_FILE" >&2 - echo "=== END DEBUG ===" >&2 - fi - - if command -v xmllint >/dev/null 2>&1; then - # Use xmllint to extract items properly - xmllint --xpath "//item[position()<=5]" "$TEMP_FILE" 2>/dev/null | - sed 's||\n|g' | - sed 's||\n|g' | - grep -A 20 '' | - awk -v RS='' ' - // { - title = ""; link = ""; pubdate = "" - - # Extract title - if (match($0, /]*>([^<]*)<\/title>/, arr)) { - title = arr[1] - gsub(/>/, ">", title) - gsub(/</, "<", title) - gsub(/&/, "&", title) - } - - # Extract link (not atom:link) - if (match($0, /([^<]*)<\/link>/, arr)) { - link = arr[1] - } - - # Extract pubDate - if (match($0, /([^<]*)<\/pubDate>/, arr)) { - pubdate = arr[1] - } - - if (title && link && pubdate) { - print pubdate "|" title "|" link - } - }' - else - # Fallback: simple grep approach - local temp_items="/tmp/arch-items-$" - - # Extract each item block - awk '//,/<\/item>/' "$TEMP_FILE" >"$temp_items" - - # Process with simple pattern matching - while IFS= read -r line; do - if [[ "$line" =~ \([^<]*)\ ]]; then - title="${BASH_REMATCH[1]}" - title="${title//>/>}" - title="${title//</<}" - title="${title//&/&}" - elif [[ "$line" =~ \([^<]*)\ ]] && [[ ! "$line" =~ atom:link ]]; then - link="${BASH_REMATCH[1]}" - elif [[ "$line" =~ \([^<]*)\ ]]; then - pubdate="${BASH_REMATCH[1]}" - - # When we have all three, output and reset - if [[ -n "$title" && -n "$link" && -n "$pubdate" ]]; then - echo "$pubdate|$title|$link" - title="" - link="" - pubdate="" - fi - fi - done <"$temp_items" - - rm -f "$temp_items" - fi -} - -# Function to send notification -send_notification() { - local title="$1" - local body="$2" - local url="$3" - - # Determine urgency level based on content - local urgency="normal" - local icon=$ICON_NORMAL - - # Check for urgent intervention keywords (case insensitive) - if echo "$title $body" | grep -qi "\(manual intervention\|action required\|breaking change\|immediate action\|urgent\|critical\|important.*update\|requires.*intervention\)"; then - urgency="critical" - icon=$ICON_URGENT - fi - - # Send notification with appropriate urgency - notify-send \ - --urgency="$urgency" \ - --app-name="Arch News" \ - --icon="$icon" \ - --expire-time=20000 \ - "$title" \ - "$body\n$url" - - # Debug output - if [[ "$DEBUG" == "1" ]]; then - echo "=== DEBUG: Notification sent ===" >&2 - echo "Title: $title" >&2 - echo "Urgency: $urgency" >&2 - echo "Reason: $(echo "$title $body" | grep -i "\(manual intervention\|action required\|breaking change\|immediate action\|urgent\|critical\|important.*update\|requires.*intervention\)" || echo "normal news")" >&2 - echo "=== END DEBUG ===" >&2 - fi -} - -# Main execution -main() { - # Parse command line options - local force_show=false - local show_latest=false - - while [[ $# -gt 0 ]]; do - case $1 in - --force) - force_show=true - shift - ;; - --show-latest) - show_latest=true - shift - ;; - --clear-cache) - rm -f "$CACHE_FILE" - echo "Cache cleared" - exit 0 - ;; - --help | -h) - echo "Usage: $0 [options]" - echo "Options:" - echo " --force Show notifications for latest news even if already seen" - echo " --show-latest Show notification for the latest news item only" - echo " --clear-cache Clear the seen items cache" - echo " --help Show this help message" - exit 0 - ;; - *) - echo "Unknown option: $1" >&2 - exit 1 - ;; - esac - done - - # Check if required tools are available - if ! command -v xmllint >/dev/null 2>&1; then - echo "Error: xmllint is required (install libxml2-utils)" >&2 - exit 1 - fi - - if ! command -v notify-send >/dev/null 2>&1; then - echo "Error: notify-send is required" >&2 - exit 1 - fi - - # Fetch RSS feed - if ! curl -s --max-time 30 "$RSS_URL" >"$TEMP_FILE"; then - echo "Error: Failed to fetch RSS feed" >&2 - exit 1 - fi - - # Debug: Check if file was created and has content - if [[ "$DEBUG" == "1" ]]; then - echo "=== DEBUG: Temp file info ===" >&2 - ls -la "$TEMP_FILE" >&2 - echo "First few lines:" >&2 - head -5 "$TEMP_FILE" >&2 - echo "=== END DEBUG ===" >&2 - fi - - # Check if file is not empty - if [[ ! -s "$TEMP_FILE" ]]; then - echo "Error: RSS feed is empty" >&2 - exit 1 - fi - - # Create cache file if it doesn't exist - [[ ! -f "$CACHE_FILE" ]] && touch "$CACHE_FILE" - - # Parse RSS and check for new items - local new_items=0 - local items_processed=0 - - # Debug: show all parsed items - if [[ "$DEBUG" == "1" ]]; then - echo "=== DEBUG: All parsed RSS items ===" >&2 - parse_rss | nl >&2 - echo "=== END DEBUG ===" >&2 - fi - - while IFS='|' read -r pubdate title link; do - [[ -z "$title" ]] && continue - ((items_processed++)) - - # Create a unique identifier for this news item - local item_id=$(echo "$title$pubdate" | md5sum | cut -d' ' -f1) - - # Show latest item only if requested - if [[ "$show_latest" == true && $items_processed -eq 1 ]]; then - local formatted_date=$(date -d "$pubdate" "+%d.%m.%Y" 2>/dev/null || echo "Recent") - send_notification "$title" "Published: $formatted_date" "$link" - echo "$item_id" >>"$CACHE_FILE" - ((new_items++)) - break - fi - - # Check if we've already seen this item (or force showing) - if [[ "$force_show" == true ]] || ! grep -q "$item_id" "$CACHE_FILE"; then - # New item found - send notification - local formatted_date=$(date -d "$pubdate" "+%d.%m.%Y" 2>/dev/null || echo "Recent") - send_notification "$title" "Published: $formatted_date" "$link" - - # Mark as seen (only if not forcing) - if [[ "$force_show" == false ]]; then - echo "$item_id" >>"$CACHE_FILE" - fi - ((new_items++)) - - # Add delay between notifications for better mako stacking - [[ $new_items -gt 1 ]] && sleep 1.5 - fi - done < <(parse_rss) - - # Clean up - rm -f "$TEMP_FILE" - - # Limit cache file size (keep last 100 entries) - if [[ -f "$CACHE_FILE" ]]; then - tail -100 "$CACHE_FILE" >"$CACHE_FILE.tmp" && mv "$CACHE_FILE.tmp" "$CACHE_FILE" - fi - - echo "Checked Arch Linux news - $new_items new items found" -} - -# Run main function -main "$@" diff --git a/lorem b/lorem deleted file mode 100755 index 23ea9a5..0000000 --- a/lorem +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -# Check if argument is provided -if [ $# -eq 0 ]; then - echo "Usage: $0 " - exit 1 -fi - -# Check if argument is a number -if ! [[ "$1" =~ ^[0-9]+$ ]]; then - echo "Error: Please provide a valid number" - exit 1 -fi - -# Lorem ipsum word bank -words=( - "lorem" "ipsum" "dolor" "sit" "amet" "consectetur" "adipiscing" "elit" - "sed" "do" "eiusmod" "tempor" "incididunt" "ut" "labore" "et" "dolore" - "magna" "aliqua" "enim" "ad" "minim" "veniam" "quis" "nostrud" - "exercitation" "ullamco" "laboris" "nisi" "aliquip" "ex" "ea" "commodo" - "consequat" "duis" "aute" "irure" "in" "reprehenderit" "voluptate" - "velit" "esse" "cillum" "fugiat" "nulla" "pariatur" "excepteur" "sint" - "occaecat" "cupidatat" "non" "proident" "sunt" "culpa" "qui" "officia" - "deserunt" "mollit" "anim" "id" "est" "laborum" -) - -# Generate requested number of words -result="" -for ((i=1; i<=$1; i++)); do - # Get random word from array - word=${words[$RANDOM % ${#words[@]}]} - - if [ $i -eq 1 ]; then - result="$word" - else - result="$result $word" - fi -done - -echo "$result" diff --git a/niri/config.kdl b/niri/config.kdl index da8d10f..da87487 100644 --- a/niri/config.kdl +++ b/niri/config.kdl @@ -236,6 +236,10 @@ spawn-at-startup "wl-paste" "--type" "text" "--watch" "cliphist" "store" spawn-at-startup "wl-paste" "--type" "image" "--watch" "cliphist" "store" spawn-at-startup "arch_news.sh" +spawn-at-startup "days_notify.sh" + +spawn-at-startup "swayidle" "-w" "timeout" "300" "niri msg action power-off-monitors" "resume" "niri msg action power-on-monitors" + // Uncomment this line to ask the clients to omit their client-side decorations if possible. // If the client will specifically ask for CSD, the request will be honored. // Additionally, clients will be informed that they are tiled, removing some client-side rounded corners. @@ -264,8 +268,7 @@ animations { // https://variety4me.github.io/niri_docs/Configuration-Switch-Events/ switch-events { - lid-close { spawn "waylock"; } - lid-open { spawn "brightnessctl" "s" "50%"; } + lid-close { spawn "waylock" "&&" "systemctl" "hibernate"; } } // Window rules let you adjust behavior for individual windows. @@ -278,6 +281,9 @@ window-rule { // - host Firefox (app-id is "firefox") // - Flatpak Firefox (app-id is "org.mozilla.firefox") match app-id=r#"zen$"# title="^Picture-in-Picture$" + match app-id=r#"Blanket$"# + match app-id=r#"Dialect$"# + match app-id=r#"com.example.ollama-chat"# open-floating true } @@ -325,7 +331,9 @@ binds { XF86AudioLowerVolume allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1-"; } XF86AudioMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; } XF86AudioMicMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; } - + + XF86MonBrightnessUp allow-when-locked=true { spawn "brightnessctl" "set" "10+%"; } + XF86MonBrightnessDown allow-when-locked=true { spawn "brightnessctl" "set" "10-%"; } // Open/close the Overview: a zoomed-out view of workspaces and windows. // You can also move the mouse into the top-left hot corner, // or do a four-finger swipe up on a touchpad. @@ -535,6 +543,7 @@ binds { Print { screenshot; } Ctrl+Print { screenshot-screen; } Alt+Print { screenshot-window; } + Mod+Print { spawn "sh" "-c" "wl-paste | swappy -f - ";} // markup the image // Applications such as remote-desktop clients and software KVM switches may // request that niri stops processing the keyboard shortcuts defined here @@ -552,5 +561,5 @@ binds { // Powers off the monitors. To turn them back on, do any input like // moving the mouse or pressing any other key. - Mod+Shift+P { power-off-monitors; } + XF86Display { power-off-monitors; } } diff --git a/rentry b/rentry deleted file mode 100755 index 1b2ea15..0000000 --- a/rentry +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env python3 - -import getopt -import http.cookiejar -import sys -import urllib.parse -import urllib.request -from http.cookies import SimpleCookie -from json import loads as json_loads -from os import environ - -_headers = {"Referer": 'https://rentry.co'} - - -class UrllibClient: - """Simple HTTP Session Client, keeps cookies.""" - - def __init__(self): - self.cookie_jar = http.cookiejar.CookieJar() - self.opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(self.cookie_jar)) - urllib.request.install_opener(self.opener) - - def get(self, url, headers={}): - request = urllib.request.Request(url, headers=headers) - return self._request(request) - - def post(self, url, data=None, headers={}): - postdata = urllib.parse.urlencode(data).encode() - request = urllib.request.Request(url, postdata, headers) - return self._request(request) - - def _request(self, request): - response = self.opener.open(request) - response.status_code = response.getcode() - response.data = response.read().decode('utf-8') - return response - - -def raw(url): - client = UrllibClient() - return json_loads(client.get('https://rentry.co/api/raw/{}'.format(url)).data) - - -def new(url, edit_code, text): - client, cookie = UrllibClient(), SimpleCookie() - - cookie.load(vars(client.get('https://rentry.co'))['headers']['Set-Cookie']) - csrftoken = cookie['csrftoken'].value - - payload = { - 'csrfmiddlewaretoken': csrftoken, - 'url': url, - 'edit_code': edit_code, - 'text': text - } - - return json_loads(client.post('https://rentry.co/api/new', payload, headers=_headers).data) - - -def edit(url, edit_code, text): - client, cookie = UrllibClient(), SimpleCookie() - - cookie.load(vars(client.get('https://rentry.co'))['headers']['Set-Cookie']) - csrftoken = cookie['csrftoken'].value - - payload = { - 'csrfmiddlewaretoken': csrftoken, - 'edit_code': edit_code, - 'text': text - } - - return json_loads(client.post('https://rentry.co/api/edit/{}'.format(url), payload, headers=_headers).data) - - -def usage(): - print(''' -Usage: rentry {new | edit | raw} {-h | --help} {-u | --url} {-p | --edit-code} text - -Commands: - new create a new entry - edit edit an existing entry - raw get raw markdown text of an existing entry - -Options: - -h, --help show this help message and exit - -u, --url URL url for the entry, random if not specified - -p, --edit-code EDIT-CODE edit code for the entry, random if not specified - -Examples: - rentry new 'markdown text' # new entry with random url and edit code - rentry new -p pw -u example 'text' # with custom edit code and url - rentry edit -p pw -u example 'text' # edit the example entry - cat file | rentry new # read from pipe and paste it to rentry - rentry raw -u example # get raw markdown text - rentry raw -u https://rentry.co/example # -u accepts absolute and relative urls - ''') - - -if __name__ == '__main__': - try: - environ.pop('POSIXLY_CORRECT', None) - opts, args = getopt.gnu_getopt(sys.argv[1:], "hu:p:", ["help", "url=", "edit-code="]) - except getopt.GetoptError as e: - sys.exit("error: {}".format(e)) - - command, url, edit_code, text = None, '', '', None - - for o, a in opts: - if o in ("-h", "--help"): - usage() - sys.exit() - elif o in ("-u", "--url"): - url = urllib.parse.urlparse(a).path.strip('/') - elif o in ("-p", "--edit-code"): - edit_code = a - - command = (args[0:1] or [None])[0] - command or sys.exit(usage()) - command in ['new', 'edit', 'raw'] or sys.exit('error: command must be new, edit or raw') - - text = (args[1:2] or [None])[0] - if not text and command != 'raw': - text = sys.stdin.read().strip() - text or sys.exit('error: text is required') - - if command == 'new': - response = new(url, edit_code, text) - if response['status'] != '200': - print('error: {}'.format(response['content'])) - try: - for i in response['errors'].split('.'): - i and print(i) - sys.exit(1) - except: - sys.exit(1) - else: - print('Url: {}\nEdit code: {}'.format(response['url'], response['edit_code'])) - - elif command == 'edit': - url or sys.exit('error: url is required') - edit_code or sys.exit('error: edit code is required') - - response = edit(url, edit_code, text) - if response['status'] != '200': - print('error: {}'.format(response['content'])) - try: - for i in response['errors'].split('.'): - i and print(i) - sys.exit(1) - except: - sys.exit(1) - else: - print('Ok') - - elif command == 'raw': - url or sys.exit('error: url is required') - response = raw(url) - if response['status'] != '200': - sys.exit('error: {}'.format(response['content'])) - print(response['content']) diff --git a/speak.sh b/speak.sh deleted file mode 100755 index 0a28d3d..0000000 --- a/speak.sh +++ /dev/null @@ -1,165 +0,0 @@ -#!/bin/bash -# Chunked TTS processor - Divides text into chunks, processes and plays them sequentially - -EXEC_PATH="$HOME/kokoros/target/release/koko" -MODEL_PATH="$HOME/kokoros/checkpoints/kokoro-v1.0.onnx" -VOICE_DATA="$HOME/kokoros/voices-v1.0.bin" -SPEED=1.1 -VOICE_STYLE="af_heart" -# Style mixing supported for Kokoros: "af_sky.4+af_nicole.5" -# https://github.com/hexgrad/kokoro/tree/main/kokoro.js/voices - -# Chunking parameters -MIN_CHUNK_SIZE=80 -MAX_CHUNK_SIZE=200 -MIN_SENTENCES=2 - -export LC_ALL=en_US.UTF-8 -export LANG=en_US.UTF-8 - -clipboard_content=$(xclip -o) -filtered_content=$(echo "$clipboard_content" | sed -E ' - s/-(\r|\n)//g - s/\r|\n/ /g - s/ +/ /g - s/^ *//g - s/ *$//g - s/--/ — /g - s/ - / — /g - s/\.\.\./…/g - s/([0-9]),([0-9])/\1\2/g - s/([.,;:])([^ ])/\1 \2/g -') - -TEMP_DIR=$(mktemp -d) -echo "Using temporary directory: $TEMP_DIR" - -# Save the cleaned text to a file -echo "$filtered_content" > "$TEMP_DIR/full_text.txt" - -# Smart chunking: Split text into optimal chunks -cat "$TEMP_DIR/full_text.txt" | - sed -E 's/([.!?]) +/\1\n/g' | - awk -v min_size="$MIN_CHUNK_SIZE" -v max_size="$MAX_CHUNK_SIZE" -v min_sentences="$MIN_SENTENCES" ' - BEGIN { - chunk = ""; - sentence_count = 0; - } - - NF > 0 { - sentence = $0; - - if (length(chunk) > 0) { - test_chunk = chunk " " sentence; - } else { - test_chunk = sentence; - } - - should_output = 0; - - # If adding this sentence would exceed max size, output current chunk first - if (length(test_chunk) > max_size && length(chunk) > 0) { - should_output = 1; - } - # If we have minimum sentences and minimum size, we can output - else if (sentence_count >= min_sentences && length(chunk) >= min_size) { - if (length(sentence) > (max_size - min_size)) { - should_output = 1; - } - else if (length(test_chunk) >= min_size * 1.5) { - chunk = test_chunk; - sentence_count++; - should_output = 1; - } - } - - if (should_output && length(chunk) > 0) { - print chunk; - chunk = ""; - sentence_count = 0; - - if (length(test_chunk) > max_size) { - chunk = sentence; - sentence_count = 1; - } - } - else { - chunk = test_chunk; - sentence_count++; - } - } - - END { - if (length(chunk) > 0) { - print chunk; - } - }' > "$TEMP_DIR/chunks.txt" - -process_chunk() { - local chunk="$1" - local output_file="$2" - echo "Processing: ${chunk:0:40}..." - echo "$chunk" > "$TEMP_DIR/current_chunk.txt" - - "$EXEC_PATH" \ - --model "$MODEL_PATH" \ - --data "$VOICE_DATA" \ - --speed "$SPEED" \ - --style "$VOICE_STYLE" \ - text "$(cat "$TEMP_DIR/current_chunk.txt")" \ - --output "$output_file" - - if [ ! -f "$output_file" ]; then - echo "Error: Failed to create audio file for chunk. Skipping..." - return 1 - fi - - return 0 -} - -# Process the first chunk immediately -FIRST_CHUNK=$(head -n 1 "$TEMP_DIR/chunks.txt") -FIRST_OUTPUT="$TEMP_DIR/chunk_0.wav" -process_chunk "$FIRST_CHUNK" "$FIRST_OUTPUT" - -if [ -f "$FIRST_OUTPUT" ]; then - aplay "$FIRST_OUTPUT" & - PLAY_PID=$! -else - echo "Failed to process first chunk. Continuing with next chunks..." - PLAY_PID=0 -fi - -# Process remaining chunks -CHUNK_NUM=1 -while read -r chunk; do - if [ $CHUNK_NUM -eq 1 ]; then - CHUNK_NUM=$((CHUNK_NUM + 1)) - continue - fi - - OUTPUT_FILE="$TEMP_DIR/chunk_$CHUNK_NUM.wav" - - process_chunk "$chunk" "$OUTPUT_FILE" - - if [ $PLAY_PID -ne 0 ]; then - wait $PLAY_PID || true - fi - - if [ -f "$OUTPUT_FILE" ]; then - aplay "$OUTPUT_FILE" & - PLAY_PID=$! - else - echo "Skipping playback of chunk $CHUNK_NUM (file not created)" - PLAY_PID=0 - fi - - CHUNK_NUM=$((CHUNK_NUM + 1)) -done < "$TEMP_DIR/chunks.txt" - -if [ $PLAY_PID -ne 0 ]; then - wait $PLAY_PID || true -fi - -echo "Processing complete!" -rm -rf "$TEMP_DIR" \ No newline at end of file diff --git a/waybar/config.jsonc b/waybar/config.jsonc index 6e754f8..34bc8a3 100644 --- a/waybar/config.jsonc +++ b/waybar/config.jsonc @@ -5,91 +5,60 @@ "spacing": 0, "modules-left": [ "niri/workspaces", + "custom/keyboard", + "custom/bitcoin", "tray", - "group/power-wrapper", - "custom/keyboard" + "bluetooth", + "group/pulseaudio-wrapper", + "group/backlight-wrapper" ], "modules-center": ["niri/window"], "modules-right": [ "battery", - "bluetooth", - "group/pulseaudio-wrapper", - "group/backlight-wrapper", "custom/gpu-power", "temperature", "memory", "cpu", - "clock" + "clock", + "custom/power" ], -"niri/workspaces": { - "format": "{icon}", - "format-icons": { - "1": "Ⅰ", - "2": "Ⅱ", - "3": "Ⅲ", - "4": "Ⅳ", - "5": "Ⅴ", - "6": "Ⅵ", - "7": "Ⅶ", - "8": "Ⅷ", - "9": "Ⅸ", - "default": "󰊠" - } -}, - "group/power-wrapper": { - "orientation": "horizontal", - "drawer": { - "transition-duration": 300, - "children-class": "power-hidden", - "transition-left-to-right": false, - "click-to-reveal": true - }, - "modules": [ - "custom/power-button", - "custom/lock", - "custom/reboot", - "custom/power" - ] + + "niri/workspaces": { + "format": "{icon}", + "format-icons": { + "1": "Ⅰ", + "2": "Ⅱ", + "3": "Ⅲ", + "4": "Ⅳ", + "5": "Ⅴ", + "6": "Ⅵ", + "7": "Ⅶ", + "8": "Ⅷ", + "9": "Ⅸ", + "default": "󰊠" + } }, - - "niri/window": { - "format": "{title}", - "max-length": 50, - "tooltip": false - }, - - "custom/power-button": { + + "custom/power": { "format": "󰐥", + "on-click": "~/.local/bin/power-menu.sh", "tooltip": true, "tooltip-format": "Power Menu" }, - "custom/lock": { - "format": "󰍁", - "on-click": "/home/jrosh/.local/bin/waylock", - "tooltip": true, - "tooltip-format": "Lock Screen" - }, - "custom/reboot": { - "format": "󰜉", - "on-click": "zenity --question --text='Are you sure you want to reboot?' --icon-name='system-reboot' --title='Reboot System' && sleep 1 && systemctl reboot --force", - "tooltip": true, - "tooltip-format": "Reboot" - }, - "custom/power": { - "format": "󰐥", - "on-click": "zenity --question --text='Are you sure you want to power off?' --icon-name='system-shutdown' --title='Power Off System' && sleep 1 && systemctl poweroff --force", - "tooltip": true, - "tooltip-format": "Power Off" - }, "custom/keyboard": { - "format": "{}", - "exec": "niri msg keyboard-layouts | grep '\\*' | awk '{print $3}' | cut -d'(' -f1", - "on-click": "niri msg action switch-layout next", - "interval": 1, - "tooltip": false + "format": "{}", + "exec": "niri msg keyboard-layouts | grep '\\*' | awk '{print $3}' | cut -d'(' -f1", + "on-click": "niri msg action switch-layout next", + "interval": 1, + "tooltip": false }, - + "custom/bitcoin": { + "exec": "~/.local/bin/bitcoin.sh", + "format": "{}", + "interval": 180, // Update every 5 minutes + "return-type": "json" + }, "network": { "format-wifi": "󰤨 {essid}", "format-ethernet": "󰈀 Wired", @@ -99,6 +68,7 @@ "format-alt": "󰤨 {signalStrength}%", "interval": 1 }, + "battery": { "states": { "warning": 30, @@ -116,7 +86,7 @@ "drawer": { "transition-duration": 300, "children-class": "audio-hidden", - "transition-left-to-right": false, + "transition-left-to-right": true, "click-to-reveal": true }, "modules": [ @@ -126,9 +96,10 @@ "custom/audio-settings" ] }, + "pulseaudio": { - "format": "{icon} {volume}%", - "format-muted": "󰖁 0%", + "format": "{icon}", + "format-muted": "󰖁", "format-icons": { "headphone": "󰋋", "hands-free": "󰋎", @@ -138,29 +109,30 @@ "car": "󰄋", "default": ["󰕿", "󰖀", "󰕾"] }, - // Can't use on click events because it won't trigger drawer - //"on-click": "pavucontrol", - //"on-click-right": "pactl set-sink-mute @DEFAULT_SINK@ toggle", "tooltip": true, "tooltip-format": "Volume: {volume}%" }, + "pulseaudio/slider": { "min": 0, "max": 100, "orientation": "horizontal" }, + "custom/audio-settings": { "format": "", "tooltip": true, "tooltip-format": "Audio Settings", "on-click": "pavucontrol" }, + "custom/audio-mute": { "format": "󰖁", "tooltip": true, "tooltip-format": "Toggle Mute", "on-click": "pactl set-sink-mute @DEFAULT_SINK@ toggle" }, + "temperature": { "critical-threshold": 80, "format": "{icon} {temperatureC}°C", @@ -169,6 +141,7 @@ "tooltip": true, "tooltip-format": "CPU Temperature: {temperatureC}°C" }, + "custom/gpu-power": { "format": " {}W", "exec": "nvidia-smi --query-gpu=power.draw --format=csv,noheader,nounits", @@ -176,21 +149,25 @@ "tooltip-format": "GPU Power: {} Watts", "interval": 5 }, + "memory": { - "format": "󰍛 {used:0.1f}G/{total:0.1f}G", + "format": "󰛨 {used:0.1f}G/{total:0.1f}G", "tooltip": true, "tooltip-format": "Memory: {used:0.2f}G/{total:0.2f}G" }, + "cpu": { "format": "󰻠 {usage}%", "tooltip": true }, + "clock": { "interval": 1, "format": "󰃰 {:%d.%m %a %H:%M}", "tooltip": true, "tooltip-format": "{:L%A, %d-%m-%Y}" }, + "tray": { "icon-size": 14, "spacing": 4 @@ -201,7 +178,7 @@ "drawer": { "transition-duration": 300, "children-class": "backlight-hidden", - "transition-left-to-right": false, + "transition-left-to-right": true, "click-to-reveal": true }, "modules": [ @@ -209,12 +186,13 @@ "backlight/slider" ] }, + "backlight": { "device": "intel_backlight", - "format": "{icon} {percent}%", + "format": "{icon}", "tooltip": true, "tooltip-format": "Brightness: {percent}%", - "format-icons": ["󰃞", "󰃝", "󰃟", "󰃠"] + "format-icons": ["󰃞", "󰃟", "󰃝", "󰃠"] }, "backlight/slider": { @@ -223,6 +201,7 @@ "orientation": "horizontal", "device": "intel_backlight" }, + "bluetooth": { "format": "󰂯 {status}", "format-connected": "󰂱 {device_alias}", diff --git a/waybar/style.css b/waybar/style.css index a3c13c3..dc4ab11 100644 --- a/waybar/style.css +++ b/waybar/style.css @@ -1,31 +1,9 @@ /* ============================================================================= WAYBAR STYLESHEET - GRUVBOX THEME - A clean, compact design using the Gruvbox color palette + Clean, compact design using the Gruvbox color palette + Updated for Waybar 0.13.0+ ============================================================================= */ -/* ----------------------------------------------------------------------------- - GRUVBOX COLOR PALETTE (for reference) - ----------------------------------------------------------------------------- */ -/* - Background: #282828 (bg) - Background 1: #3c3836 (bg1) - Background 2: #504945 (bg2) - Foreground: #ebdbb2 (fg) - Gray: #928374 (gray) - Red: #fb4934 (red) - Green: #b8bb26 (green) - Yellow: #fabd2f (yellow) - Blue: #83a598 (blue) - Purple: #d3869b (purple) - Aqua: #8ec07c (aqua) - Orange: #fe8019 (orange) - - Standard padding: 2px 4px - Extended padding: 0px 10px - Standard margin: 4px - Border radius: 6px -*/ - /* ----------------------------------------------------------------------------- GLOBAL SETTINGS ----------------------------------------------------------------------------- */ @@ -33,262 +11,195 @@ font-family: "BlexMono Nerd Font Mono"; font-weight: 500; font-size: 13px; - color: #ebdbb2; /* Gruvbox foreground */ + color: #ebdbb2; } -/* Transparent Waybar background */ #waybar { - background-color: rgba(0, 0, 0, 0); + background-color: transparent; border: none; - box-shadow: none; } /* ----------------------------------------------------------------------------- MODULE LAYOUTS & BACKGROUNDS ----------------------------------------------------------------------------- */ -/* Standard modules with full rounded corners */ +/* Standard modules with rounded corners */ #workspaces, #window, #tray, -#custom-keyboard { - background-color: rgba(60, 56, 54, 0.85); /* bg1 with transparency */ +#custom-keyboard, +#bluetooth, +#custom-bitcoin { + background-color: rgba(60, 56, 54, 0.85); padding: 2px 6px; - margin-top: 2px; - margin-right: 4px; - margin-bottom: 0; - margin-left: 4px; + margin: 2px 4px 0; border-radius: 4px; - border-width: 0px; +} + +#custom-keyboard, +#bluetooth { font-size: 10px; } -/* Clock - right-side rounded */ -#clock { - background-color: rgba(60, 56, 54, 0.85); /* bg1 with transparency */ - margin-top: 2px; - margin-right: 2px; - margin-bottom: 0; - margin-left: 0; - padding: 2px 6px; - border-radius: 0 4px 4px 0; - border-width: 0px; +/* Drawer wrapper modules */ +#backlight-wrapper, +#pulseaudio-wrapper { + background-color: rgba(60, 56, 54, 0.85); + padding: 0 8px 0 10px; + margin: 2px 4px 0 4px; + border-radius: 4px; +} +#backlight, +#pulseaudio{font-size: 1.4rem; padding-right: 4px;} + +#custom-audio-settings, +#custom-audio-mute{ + padding: 0px 12px; } -/* Left-side modules - rounded left edge */ -#network { - background-color: rgba(60, 56, 54, 0.85); /* bg1 with transparency */ - margin-top: 2px; - margin-right: 0; - margin-bottom: 0; - margin-left: 2px; +/* Clock - right edge */ +#custom-power { + background-color: rgba(60, 56, 54, 0.85); + margin: 2px 2px 0 0; + padding: 2px 12px; + border-radius: 0 4px 4px 0; + font-size: 14px; +} + +/* Battery - left edge */ +#battery { + background-color: rgba(60, 56, 54, 0.85); + margin: 2px 0 0 2px; padding: 2px 8px; border-radius: 4px 0 0 4px; - border-width: 0px; } /* Middle modules - no rounded edges */ -#bluetooth, -#battery, -#pulseaudio, -#backlight, #temperature, #memory, #cpu, -#custom-gpu-power, -#custom-audio-settings, -#custom-audio-mute -{ - background-color: rgba(60, 56, 54, 0.85); /* bg1 with transparency */ - margin-top: 2px; - margin-right: 0; - margin-bottom: 0; - margin-left: 0; +#clock, +#custom-gpu-power { + background-color: rgba(60, 56, 54, 0.85); + margin: 2px 0 0; padding: 2px 6px; - border-width: 0px; min-width: 18px; } +/* Audio settings icons */ +#custom-audio-settings, +#custom-audio-mute { + font-size: 16px; +} + +/* ----------------------------------------------------------------------------- + SLIDER STYLING - PROPER CSS NODES + ----------------------------------------------------------------------------- */ + +/* Slider containers */ #pulseaudio-slider, #backlight-slider { - background-color: rgba(60, 56, 54, 0.85); - margin-top: 2px; - margin-right: 0; - margin-bottom: 0; - margin-left: 0; + margin: 2px 0 0; padding: 2px 6px; - border-width: 0px; min-width: 18px; -} +} +/* Hide the actual slider handle */ #pulseaudio-slider slider, -#backlight-slider slider{ - min-height: 0px; - min-width: 0px; +#backlight-slider slider { + min-height: 0; + min-width: 0; opacity: 0; - background-image: none; + background: transparent; border: none; box-shadow: none; } +/* Trough - the unfilled background */ #pulseaudio-slider trough, #backlight-slider trough { min-height: 8px; min-width: 80px; border-radius: 6px; - background-color: #282828; /* bg - darker for contrast */ + background-color: #282828; } -#pulseaudio-slider highlight, +/* Highlight - the filled portion */ #backlight-slider highlight { min-width: 4px; + min-height: 8px; border-radius: 6px; - background-color: #fabd2f; /* yellow - bright and visible */ + background-color: #fabd2f; } -#pulseaudio-slider highlight{ + +#pulseaudio-slider highlight { + min-width: 4px; + min-height: 8px; + border-radius: 6px; background-color: #8ec07c; } -#custom-audio-settings, -#custom-audio-mute{ - font-size: 16px; -} - -/* ----------------------------------------------------------------------------- - POWER MANAGEMENT GROUP - ----------------------------------------------------------------------------- */ - -/* Power management drawer toggle button - standalone with full radius */ -#custom-power-button { - background-color: rgba(60, 56, 54, 0.85); /* bg1 with transparency */ - margin-top: 2px; - margin-right: 2px; - margin-bottom: 0; - margin-left: 2px; - padding: 2px 6px; - border-width: 0px; - border-radius: 4px; - min-width: 18px; -} - -/* Lock button - first in power group, left rounded edge */ -#custom-lock { - background-color: rgba(60, 56, 54, 0.85); /* bg1 with transparency */ - margin-top: 2px; - margin-right: 0; - margin-bottom: 0; - margin-left: 2px; - padding: 2px 6px; - border-radius: 4px 0 0 4px; - border-width: 0px; -} - -/* Reboot button - middle button, no rounded edges */ -#custom-reboot { - background-color: rgba(60, 56, 54, 0.85); /* bg1 with transparency */ - margin-top: 2px; - margin-right: 0; - margin-bottom: 0; - margin-left: 0; - padding: 2px 6px; - border-width: 0px; - border-radius: 0; - min-width: 18px; -} - -/* Power button - last in group, right rounded edge */ -#custom-power { - background-color: rgba(60, 56, 54, 0.85); /* bg1 with transparency */ - margin-top: 2px; - margin-right: 0; - margin-bottom: 0; - margin-left: 0; - padding: 2px 6px; - border-width: 0px; - border-radius: 0 4px 4px 0; - min-width: 18px; -} - -.power-hidden:hover, -#custom-power-button:hover .power-hidden, -#custom-reboot:hover .power-hidden, -#custom-power:hover .power-hidden, -.backlight-hidden:hover, -#backlight:hover .backlight-hidden, -#backlight-slider:hover .power-hidden, -.audio-hidden:hover, -#pulseaudio:hover .audio-hidden, -#pulseaudio-slider:hover .audio-hidden { - opacity: 1; +/* Drawer reveal on hover */ +.backlight-hidden, +.audio-hidden { + transition: opacity 300ms; } /* ----------------------------------------------------------------------------- WORKSPACES (NIRI) ----------------------------------------------------------------------------- */ -/* Default workspace buttons */ +/* Workspace buttons default state */ #workspaces button { background: transparent; border: none; - color: #928374; /* Gruvbox gray */ + color: #928374; padding: 1px 6px; margin: 0 1px; font-weight: 500; border-radius: 0; + transition: all 200ms ease; } /* Hovered workspace */ #workspaces button:hover { - background-color: rgba(131, 165, 152, 0.2); /* Gruvbox aqua with transparency */ + background-color: rgba(131, 165, 152, 0.2); padding: 1px 6px; margin: 0 1px; border-radius: 6px; } -/* Active workspace */ +/* Active workspace - clear distinct styling */ #workspaces button.active { - background-color: #83a598; /* Gruvbox blue */ - color: #282828; /* Gruvbox bg */ + background-color: #83a598; + color: #282828; padding: 1px 6px; margin: 0 1px; border-radius: 6px; + font-weight: 600; } /* Urgent workspace */ #workspaces button.urgent { - background-color: #fb4934; /* Gruvbox red */ - color: #282828; /* Gruvbox bg */ + background-color: #fb4934; + color: #282828; padding: 1px 6px; margin: 0 1px; border-radius: 6px; } +/* Empty workspace styling */ +#workspaces button.empty { + color: #665c54; +} + /* ----------------------------------------------------------------------------- WINDOW TITLE ----------------------------------------------------------------------------- */ #window { font-weight: 400; font-style: italic; - color: #ebdbb2; /* Gruvbox foreground */ -} - -/* ----------------------------------------------------------------------------- - CUSTOM MODULES - COLORS - ----------------------------------------------------------------------------- */ - -/* Lock button */ -#custom-lock { - color: #8ec07c; /* Gruvbox aqua */ -} - -/* Reboot button */ -#custom-reboot { - color: #fabd2f; /* Gruvbox yellow */ -} - -/* Power button */ -#custom-power { - color: #fb4934; /* Gruvbox red */ + color: #ebdbb2; } /* ----------------------------------------------------------------------------- @@ -297,121 +208,117 @@ /* Network */ #network { - color: #8ec07c; /* Gruvbox aqua */ + color: #8ec07c; } -/* Battery */ +/* Battery states */ #battery { - color: #b8bb26; /* Gruvbox green */ + color: #b8bb26; } #battery.warning { - color: #fabd2f; /* Gruvbox yellow */ + color: #fabd2f; } #battery.critical { - color: #fb4934; /* Gruvbox red */ + color: #fb4934; + animation: blink 1s ease-in-out infinite; } -/* Bluetooth */ -#bluetooth { - color: #83a598; /* Gruvbox blue */ +@keyframes blink { + 50% { + opacity: 0.5; + } } /* Audio */ #pulseaudio { - color: #8ec07c; /* Gruvbox aqua */ + /* color: #8ec07c; */ + color: #ebdbb2; } #pulseaudio.muted { - color: #fb4934; /* Gruvbox red */ + color: #fb4934; } /* Backlight */ -#backlight { - color: #fabd2f; /* Gruvbox yellow */ -} +/* #backlight { + color: #fabd2f; +} */ /* GPU Power */ #custom-gpu-power { - color: #83a598; /* Gruvbox blue */ + color: #83a598; } -/* Temperature */ +/* Temperature states */ #temperature { - color: #fe8019; /* Gruvbox orange */ + color: #fe8019; } #temperature.warning { - color: #fabd2f; /* Gruvbox yellow */ + color: #fabd2f; } #temperature.critical { - color: #fb4934; /* Gruvbox red */ + color: #fb4934; } -/* Memory */ +/* Memory states */ #memory { - color: #d3869b; /* Gruvbox purple */ + color: #d3869b; } #memory.warning { - color: #fabd2f; /* Gruvbox yellow */ + color: #fabd2f; } #memory.critical { - color: #fb4934; /* Gruvbox red */ + color: #fb4934; } -/* CPU */ +/* CPU states */ #cpu { - color: #fe8019; /* Gruvbox orange */ + color: #fe8019; } #cpu.warning { - color: #fabd2f; /* Gruvbox yellow */ + color: #fabd2f; } #cpu.critical { - color: #fb4934; /* Gruvbox red */ + color: #fb4934; } /* Clock */ #clock { - color: #d3869b; /* Gruvbox purple */ + color: #8ec07c; } -/* System tray */ +/* ----------------------------------------------------------------------------- + SYSTEM TRAY + ----------------------------------------------------------------------------- */ #tray { - background-color: rgba(60, 56, 54, 0.85); /* bg1 with transparency */ + background-color: rgba(60, 56, 54, 0.85); padding: 2px 6px; } #tray > .passive { padding: 1px 3px; - margin-top: 0; - margin-right: 1px; - margin-bottom: 0; - margin-left: 1px; -} - -#tray > .needs-attention { - padding: 1px 3px; - margin-top: 0; - margin-right: 1px; - margin-bottom: 0; - margin-left: 1px; - background-color: #fb4934; /* Gruvbox red */ - color: #282828; /* Gruvbox bg */ - border-radius: 3px; + margin: 0 1px; } #tray > .active { padding: 1px 3px; - margin-top: 0; - margin-right: 1px; - margin-bottom: 0; - margin-left: 1px; + margin: 0 1px; +} + +#tray > .needs-attention { + padding: 1px 3px; + margin: 0 1px; + background-color: #fb4934; + color: #282828; + border-radius: 3px; } /* ----------------------------------------------------------------------------- @@ -419,21 +326,15 @@ ----------------------------------------------------------------------------- */ #bluetooth:hover, #network:hover, -#backlight:hover, #custom-audio-settings:hover, #custom-audio-mute:hover, #battery:hover, -#pulseaudio:hover, #temperature:hover, #memory:hover, #cpu:hover, #clock:hover, -#custom-lock:hover, -#custom-reboot:hover, #custom-power:hover, -#custom-power-button:hover, #window:hover { - background-color: rgba(80, 73, 69, 0.9); /* Gruvbox bg2 */ + background-color: rgba(80, 73, 69, 0.9); + transition: background-color 200ms ease; } - - diff --git a/waylock b/waylock deleted file mode 100755 index 2d7bb4a..0000000 --- a/waylock +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash - -# Gruvbox Dark Theme Swaylock Command -# Colors based on the Gruvbox color scheme -# Background: dark0_hard (#1d2021) -# Foreground: light1 (#ebdbb2) -# Red: bright_red (#fb4934) -# Green: bright_green (#b8bb26) -# Yellow: bright_yellow (#fabd2f) -# Blue: bright_blue (#83a598) -# Purple: bright_purple (#d3869b) -# Orange: bright_orange (#fe8019) - -swaylock \ - --color 1d2021 \ - --inside-color 282828 \ - --inside-clear-color 458588 \ - --inside-caps-lock-color fabd2f \ - --inside-ver-color d3869b \ - --inside-wrong-color fb4934 \ - --ring-color 3c3836 \ - --ring-clear-color 689d6a \ - --ring-caps-lock-color fe8019 \ - --ring-ver-color b16286 \ - --ring-wrong-color cc241d \ - --line-color 00000000 \ - --line-clear-color 00000000 \ - --line-caps-lock-color 00000000 \ - --line-ver-color 00000000 \ - --line-wrong-color 00000000 \ - --text-color ebdbb2 \ - --text-clear-color 1d2021 \ - --text-caps-lock-color 1d2021 \ - --text-ver-color 1d2021 \ - --text-wrong-color 1d2021 \ - --key-hl-color b8bb26 \ - --bs-hl-color fb4934 \ - --caps-lock-key-hl-color fe8019 \ - --caps-lock-bs-hl-color fb4934 \ - --separator-color 00000000 \ - --indicator-radius 100 \ - --indicator-thickness 7 \ - --indicator-idle-visible \ - --font "BlexMono Nerd Font Mono" \ - --font-size 24 \ - --show-failed-attempts \ - --show-keyboard-layout