diff --git a/eww/.config.json b/eww/.config.json new file mode 100644 index 0000000..531e851 --- /dev/null +++ b/eww/.config.json @@ -0,0 +1,11 @@ +{ + "notify": { + "limit": 50, + "interval": 0.5, + "cache_path": "$XDG_CACHE_HOME/dunst/notifications.txt", + "quote_path": "$XDG_CACHE_HOME/dunst/quotes.txt", + "default_quote": "", + "timestamp": "%I:%M", + "excluded_appnames": ["Volume", "Backlight"] + } +} diff --git a/eww/eww.scss b/eww/eww.scss new file mode 100755 index 0000000..56c4469 --- /dev/null +++ b/eww/eww.scss @@ -0,0 +1,648 @@ +/** Global *******************************************/ +*{ + all : unset; + font-family : feather; + font-family : Iosevka; +} + +/** Vars *********************************************/ + +$bg-color : #282c34; + +/** Generic window ***********************************/ +.genwin, .genwina, .genwinb, .genwinc, .genwind, .genwine, .genwinf, .genwing, .genwinh { + background-color : $bg-color; //#252933; #2E3440; +} + +.genwin { + border-radius : 16px; +} + +.genwina { + border-radius : 16px 16px 0 0; +} + +.genwinb { + border-radius : 0 16px 16px 16px; +} + +.genwinh { + border-radius : 16px 0 0 16px; +} + +.genwinc { + border-radius : 16px 16px 0 16px; +} + +.genwind { + border-radius : 46px 16px 16px 16px; +} + +.genwine { + border-radius : 16px 46px 16px 16px; +} + +.genwinf { + border-radius : 16px 16px 16px 46px; +} + +.genwing { + border-radius : 16px 16px 46px 16px; +} + +/** rxyhn's calendar *********************************/ +.cal-holder { + padding : 1rem 1rem .2rem; + font-size : 1em; + font-weight : bold; +} + +.cal_title_right, .cal_title_left { + font-size : 32px; + color : #EBCB8B; +} + +.cal_title { + margin : 16px 72px 4px 72px; + font-size : 16px; + color : #A3BE8C; +} + +.cal { + padding : .8rem .25rem 0rem; + margin-left : 10px; + + &.highlight { + padding : 10rem; + } +} + +calendar:selected { + color : #88C0D0; +} + +calendar.header { + color : #88C0D0; + font-weight : bold; +} + +calendar.button { + color : #D46389; +} + +calendar.highlight { + color : #D46389; + font-weight : bold; +} + +calendar:indeterminate { + color : $bg-color; +} + +/** Profile ******************************************/ +.face { + background-image : url('images/fox.png'); + background-size : 175px; + background-position : 50% 35%; + min-height : 175px; + min-width : 175px; + margin : 40px 0px 0px 2px; + border-radius : 100%; +} + +.fullname { + color : #D46389; + font-size : 30px; + font-weight : bold; +} + +.username { + color : #8FBCBB; + font-size : 22px; + font-weight : bold; + margin : -15px 0px 0px 0px; +} + +/** System ********************************************/ +.iconcpu, .iconmem, .iconbright, .iconbat { + font-size : 22px; + font-weight : normal; +} +.iconcpu { + color : #BF616A; +} +.iconmem { + color : #A3BE8C; +} +.iconbright { + color : #EBCB8B; +} +.iconbat { + color : #88C0D0; +} + +.cpu_bar, .mem_bar, .bright_bar, .bat_bar, scale trough { + all : unset; + background-color : #3A404C; + border-radius : 16px; + min-height : 12px; + min-width : 170px; +} + +.cpu_bar, .mem_bar, .bright_bar, .bat_bar, scale trough highlight { + all : unset; + border-radius : 16px; +} + +.cpu_bar scale trough highlight { + background-color : #BF616A; +} +.mem_bar scale trough highlight { + background-color : #A3BE8C; +} +.bright_bar scale trough highlight { + background-color : #EBCB8B; +} +.bat_bar scale trough highlight { + background-color : #88C0D0; +} + +/** Clock ********************************************/ +.time_hour, .time_min { + color : #81A1C1; + font-size : 45px; + font-weight : bold; +} +.time_hour { + margin : 10px 0px 0px 10px; +} +.time_min { + margin : 0px 0px 10px 0px; +} + +.time_mer { + color : #A3BE8C; + font-size : 30px; + font-weight : bold; + margin : 10px 0px 0px 0px; +} + +.time_day { + color : #EBCB8B; + font-size : 17px; + font-weight : normal; + margin : 0px 0px -30px 10px; +} + +/** Uptime ********************************************/ +.icontimer { + color : #B48EAD; + font-size : 50px; + font-weight : normal; +} + +/** Music ***************************************/ +.album_art { + background-size : cover; + background-position : center; + min-height : 184px; + min-width : 184px; + margin : 20px; + border-radius : 14px; +} + +.song { + color : #8FBCBB; + font-size : 18px; + font-weight : bold; + margin : 15px 0px 0px 0px; +} + +.artist { + color : #EBCB8B; + font-size : 14px; + font-weight : normal; + margin : 0px 0px 0px 0px; +} + +.btn_prev, .btn_play, .btn_next { + font-family : Iosevka Nerd Font; +} +.btn_prev { + color : #EBCB8B; + font-size : 32px; + font-weight : normal; +} +.btn_play { + color : #A3BE8C; + font-size : 40px; + font-weight : bold; +} +.btn_next { + color : #EBCB8B; + font-size : 32px; + font-weight : normal; +} + +.music_bar scale trough highlight { + all : unset; + background-color : #B48EAD; + border-radius : 8px; +} +.music_bar scale trough { + all : unset; + background-color : #3A404C; + border-radius : 8px; + min-height : 10px; + min-width : 180px; + margin : 0px 10px 0px 0px; +} + +/** Weather ***************************************/ +.iconweather { + font-family : Iosevka Nerd Font; + font-size : 58px; + font-weight : normal; + margin : 10px 0px 0px 30px; +} + +.label_temp { + color : #A6D1DD; + font-size : 54px; + font-weight : bold; + margin : 15px 25px 0px 0px; +} + +.label_stat { + color : #BF616A; + font-size : 30px; + font-weight : bold; + margin : 10px 0px 0px 0px; +} + +.label_quote { + color : #E5E5E5; + font-size : 18px; + font-weight : normal; +} + +/** Applications ***************************************/ +.appbox { + margin : 10px 0px 0px 12px; +} + +.app_chrome, .app_telegram, .app_discord, .app_terminal, .app_sublime, +.app_files, .app_neovim, .app_code, .app_krita, .app_vbox { + background-repeat : no-repeat; + background-size : 42px; + min-height : 42px; + min-width : 42px; + margin : 0 3px 0 3px; +} + +.app_chrome {} +.app_telegram {} +.app_discord {} +.app_terminal {} +.app_files {} +.app_sublime { +} +.app_neovim { + background-size : 36px; + background-position : center; +} +.app_code {} +.app_krita {} +.app_vbox {} + +/** Links ***************************************/ +.iconweb, .iconmail { + color : #FFFFFF; + font-family : Iosevka Nerd Font; + font-size : 60px; + font-weight : normal; +} +.iconmail { + color : #DF584E; +} + +.github { + background-color : #24292E; + border-radius : 16px; +} +.reddit { + background-color : #E46231; + border-radius : 16px; +} +.twitter { + background-color : #61AAD6; + border-radius : 16px; +} +.youtube { + background-color : #DF584E; + border-radius : 16px; +} +.mail { + background-color : #FFFFFF; + border-radius : 16px; +} + +.mailbox { + background-color : #E5E5E5; + border-radius : 10px; + margin : 24px 0px 24px 0px; +} +.label_mails { + color : #404040; + font-size : 24px; + font-weight : bold; + margin : 0px 6px 0px 6px; +} + +/** Power buttons ***************************************/ +.btn_logout, .btn_sleep, .btn_reboot, .btn_poweroff { + font-size : 48px; + font-weight : bold; +} + +.btn_logout { + color : #BF616A; +} +.btn_sleep { + color : #A3BE8C; +} +.btn_reboot { + color : #EBCB8B; +} +.btn_poweroff { + color : #88C0D0; +} + +/** Home folders ***************************************/ +.hddbox { + background-color : #3A404C; + border-radius : 10px; + margin : 15px; +} +.hddicon { + color : #81A1C1; + font-family : Iosevka Nerd Font; + font-size : 70px; + font-weight : normal; + margin : 5px 10px 5px 15px; +} +.hdd_label { + color : #E5E9F0; + font-size : 32px; + font-weight : bold; + margin : 0px 0px 0px 10px; +} +.fs_sep { + color : #2E3440; + font-size : 34px; + font-weight : bold; +} + +.iconfolder1, .iconfolder2, .iconfolder3, .iconfolder4, .iconfolder5, .iconfolder6 { + font-family : Iosevka Nerd Font; + font-size : 24px; + font-weight : normal; + margin : 0px 0px 0px 25px; +} +.iconfolder1 { + color : #BF616A; +} +.iconfolder2 { + color : #A3BE8C; +} +.iconfolder3 { + color : #EBCB8B; +} +.iconfolder4 { + color : #81A1C1; +} +.iconfolder5 { + color : #B48EAD; +} +.iconfolder6 { + color : #88C0D0; +} + +.label_folder1, .label_folder2, .label_folder3, .label_folder4, .label_folder5, .label_folder6 { + font-size : 22px; + font-weight : normal; + margin : 0px 0px 0px 30px; +} +.label_folder1 { + color : #BF616A; +} +.label_folder2 { + color : #A3BE8C; +} +.label_folder3 { + color : #EBCB8B; +} +.label_folder4 { + color : #81A1C1; +} +.label_folder5 { + color : #B48EAD; +} +.label_folder6 { + color : #88C0D0; +} + +/*--|> NeoMusic <|--*/ + +.art { + background-size : 62%; + background-repeat : no-repeat; + background-position : 100% 50%; + border-radius : 18px 16px 16px 18px; +} + +.music_main, .art { + min-width : 396px; +} + +.music_main { + border-radius : 16px; +} + +.music_main .song { + font-size : 24px; +} + +.music_main .artist { + margin : 0 0 32px 0; + font-size : 16px; +} + +.music_main .artist { + color : #E5C07b; +} + +.music_main .song { + color : #61AFEF; +} + +.music_main .btn_next, .music_main .btn_prev { + font-size : 30px; + color : #E5C07b; +} + +.music_main .btn_play { + font-size : 40px; + color : #98c379; +} + +.music_main .control, .music_main .song, .music_main .artist { + padding : 4px 0 4px 10px; + letter-spacing : 2px; +} + +.music_main { + background : linear-gradient(120deg, $bg-color /*#282c34*/ 55%, transparent); + padding : 4px 10px 4px 10px; +} + +/*--> NeoProfile <--*/ + +.avatar{ + border-radius : 18px; + background-size : cover; + background-position : 50% 100%; +} + +.profile_main { + border-radius : 16px; +} + +.profile_main { + background : linear-gradient(-5deg, $bg-color 10%, transparent, $bg-color 75%); + min-width : 246px; +} + +.fullname { + margin : 10px 0 0 0; + color : #D46389; + font-size : 28px; + font-weight : bold; +} + +.username { + color : #8FBCBB; + font-size : 22px; + font-weight : bold; + margin : -15px 0px 0px 0px; +} + +/*--|> NeoClock <|--*/ + +.uptitle { + color : #EBCB8B; + font-size : 22px; +} + +.updays { + color : #81A1C1; + font-size : 28px; + font-weight : bold; +} + +.uptime { + color : #61AFEF; + //color : #E5E9F0; + font-size : 24px; + font-weight : bold; +} + +.nclock { + margin : 0px 0px 0px 26px; +} + +.ntime_hour, .ntime_min { + color : #81A1C1; + font-size : 45px; + font-weight : bold; +} +.ntime_hour { + margin : 0px; +} +.ntime_min { + margin : 0px 0px 0px 2px; +} + +.ntime_date { + margin : 6px 0px -6px 0px; + color : #61AFEF; + font-size : 17px; + font-weight : bold; +} + +.ntime_mers *:nth-child(1) { + margin : 8px 0px 0px 0px; +} + +.ntime_mer { + color : #A3BE8C; + font-size : 16px; + font-weight : bold; +} + +.ntime_day { + margin : -8px 0px 0px 0px; + color : #EBCB8B; + font-size : 18px; + font-weight : normal; +} + +/*--> Workspaces <--*/ +.ws1, .ws2, .ws3, .ws4, .ws5, .ws6, .ws7, .ws8, .ws9, .ws10 { + font-size : 24px; +} +.ws1 { + color : #52E080; +} + +.ws2 { + color : #D70040; +} + +.ws3 { + color : #117dca; +} + +.ws4 { + color : #32a7d9; +} + +.ws5 { + color : #23cf5f; +} + +.ws6 { + color : #80461b; +} + +.ws7 { + color : #f4c430; +} + +.ws8 { + color : #DA70D6; +} + +.ws9 { + color : #800020; +} + +.ws10 { + color : #F5F5F5; +} + +.ws_holder { + margin : 24px 0 0 0; +} + +/** EOF *************************************************/ diff --git a/eww/eww.yuck b/eww/eww.yuck new file mode 100755 index 0000000..0a0b8c8 --- /dev/null +++ b/eww/eww.yuck @@ -0,0 +1,422 @@ +;; ** +;; ** Widgets config for EWW +;; ** Created by : @adi1090x +;; ** BY : Dustyn +;; ** Modified by : S4NKALP + + + + + ;; Profile vars + (defvar IMAGE "images/pp.png") + (defvar NAME "Har Har Mahadev") + (defpoll UNAME :interval "5m" "whoami") + + ;; System vars + (defpoll HOST :interval "5s" "hostname") + (defpoll CPU_USAGE :interval "1s" "~/.config/eww/scripts/sys_info --cpu") + (defpoll MEM_USAGE :interval "1s" "~/.config/eww/scripts/sys_info --mem") + (defpoll BLIGHT :interval "1s" "~/.config/eww/scripts/sys_info --blight") + (defpoll BATTERY :interval "5s" "~/.config/eww/scripts/sys_info --bat") + + ;; Time vars + (defpoll HOUR :interval "5s" "date +\"%I\"") + (defpoll MIN :interval "5s" "date +\"%M\"") + (defpoll MERA :interval "5s" "date +\"%p\" | cut -b 1") + (defpoll MERB :interval "5s" "date +\"%p\" | cut -b 2") + (defpoll DAY :interval "5s" "date +\"%A\"") + (defpoll DATE :interval "5s" "date +\"%d.%m.%Y\"") + + ;; Uptime vars + (defpoll UPDAYS :interval "5s" "echo $(($(cat /proc/uptime | cut -d \".\" -f 1) / 60 / 60 / 24))d") + (defpoll UPTIME :interval "5s" "printf %02d:%02d $(($(cat /proc/uptime | cut -d \".\" -f 1) / 60 / 60 % 24)) $(($(cat /proc/uptime | cut -d \".\" -f 1) / 60 % 60))") + ;; (defpoll UPHOUR :interval "5s" "uptime -p | awk '{print $2 \" \" $3}' | sed 's/,//g'") + ;; (defpoll UPMIN :interval "5s" "uptime -p | awk '{print $4 \" \" $5}'") + + ;; Music vars + (defpoll SONG :interval "1s" "~/.config/eww/scripts/music_info --song") + (defpoll ARTIST :interval "1s" "~/.config/eww/scripts/music_info --artist") + (defpoll STATUS :interval "1s" "~/.config/eww/scripts/music_info --status") + (defpoll COVER :interval "1s" "~/.config/eww/scripts/music_info --cover") + + ;; Weather vars + (defpoll ICON :interval "15m" "~/.config/eww/scripts/weather_info --icon") + (defpoll STAT :interval "15m" "~/.config/eww/scripts/weather_info --stat") + (defpoll TEMP :interval "15m" "~/.config/eww/scripts/weather_info --temp") + (defpoll HEX :interval "15m" "~/.config/eww/scripts/weather_info --hex") + (defpoll QUOTE :interval "15m" "~/.config/eww/scripts/weather_info --quote") + (defpoll QUOTE2 :interval "15m" "~/.config/eww/scripts/weather_info --quote2") + + ;; Apps vars + (defpoll MAILS :interval "5m" "~/.config/eww/scripts/mails") + + ;; Files vars + (defpoll FREE :interval "5s" "df -h / | awk '{print $4}' | tail -n 1 | sed 's/G/GB/'") + + ;; Calendar + (defpoll CALDAY :interval "10h" "date '+%d'") + (defpoll CALMONTH :interval "10h" "$(($(date +%m)-1))") + (defpoll CALYEAR :interval "10h" "date '+%Y'") + + + + +;; ******************************************************************************** + + + + ;; profile + (defwidget user [] + (box :class "genwin" :orientation "v" :spacing 20 :space-evenly "false" :vexpand "false" :hexpand "false" + (box :class "face" :halign "center" :style "background-image: url('${IMAGE}');") + (label :class "fullname" :halign "center" :wrap "true" :limit-width 25 :text NAME) + (label :class "username" :halign "center" :wrap "true" :limit-width 25 :text UNAME))) + + ;; NeoProfile + (defwidget nuser [] + (box :class "genwina" :space-evenly "false" :vexpand "false" :hexpand "false" + (box :class "avatar" :vexpand "false" :hexpand "false" :style "background-image: url('${IMAGE}');" + (box :class "profile_main" :orientation "v" :spacing 15 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "fullname" :halign "center" :wrap "true" :limit-width 25 :text NAME) + (label :class "username" :halign "center" :wrap "true" :limit-width 25 :text UNAME))))) + + ;; system + (defwidget system [] + (box :class "genwin" :vexpand "false" :hexpand "false" + (box :orientation "v" :spacing 25 :halign "center" :valign "center" :space-evenly "false" :vexpand "false" :hexpand "false" + (box :class "cpu_bar" :orientation "h" :spacing 20 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "iconcpu" :text "") + (scale :min 0 :max 100 :value CPU_USAGE :active "false")) + (box :class "mem_bar" :orientation "h" :spacing 20 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "iconmem" :text "") + (scale :min 0 :max 100 :value MEM_USAGE :active "false")) + (box :class "bright_bar" :orientation "h" :spacing 20 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "iconbright" :text "") + (scale :min 0 :max 100 :value BLIGHT :active "false")) + (box :class "bat_bar" :orientation "h" :spacing 20 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "iconbat" :text "") + (scale :min 0 :max 100 :value BATTERY :active "false"))))) + + ;; clock + (defwidget clock [] + (box :class "genwin" :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "time_hour" :valign "start" :wrap "true" :limit-width 25 :text HOUR) + (label :class "time_min" :valign "end" :wrap "true" :limit-width 25 :text MIN) + (box :orientation "v" :spacing 10 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "time_mer" :halign "end" :wrap "true" :limit-width 25 :text "${MERA}${MERB}") + (label :class "time_day" :halign "end" :wrap "true" :limit-width 25 :text DAY)))) + + ;; NeoClock + (defwidget nclock [] + (box :class "genwinc" :orientation "h" :spacing 20 :space-evenly "false" :vexpand "false" :hexpand "false" + (box :orientation "v" :class "nclock" :valign "center" :spacing 0 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "uptitle" :halign "center" :wrap "true" :limit-width 25 :text "Uptime") + (label :class "updays" :halign "center" :wrap "true" :limit-width 25 :text "祥 ${UPDAYS}") + (label :class "uptime" :halign "center" :wrap "true" :limit-width 25 :text UPTIME)) + (box :orientation "v" :valign "center" :spacing 0 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "ntime_date" :halign "center" :wrap "true" :limit-width 25 :text DATE) + (box :orientation "h" :halign "center" :spacing 8 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "ntime_hour" :valign "start" :wrap "true" :limit-width 25 :text HOUR) + (box :orientation "v" :class "ntime_mers" :spacing 0 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "ntime_mer" :halign "end" :wrap "true" :limit-width 25 :text MERA) + (label :class "ntime_mer" :halign "end" :wrap "true" :limit-width 25 :text MERB)) + (label :class "ntime_min" :valign "end" :wrap "true" :limit-width 25 :text MIN)) + (label :class "ntime_day" :halign "center" :wrap "true" :limit-width 25 :text DAY)))) + + ;; NeoCalendar (rxyhn's calendar) + (defwidget cal [] + (box :class "genwin" :orientation "v" :space-evenly "false" :vexpand "false" :hexpand "false" + (box :class "cal_holder" :orientation "v" :space-evenly "false" :vexpand "false" :hexpand "false" + (box :class "title_holder" :orientation "h" :space-evenly "false" :halign "center" :vexpand "false" :hexpand "false" + (label :class "cal_title_left" :halign "start" :wrap "true" :text "·") + (label :class "cal_title" :halign "center" :wrap "true" :text "Calendar") + (label :class "cal_title_right" :halign "end" :wrap "true" :text "·")) + (calendar :class "cal" :day CALDAY :month CALMONTH :year CALYEAR)))) + + ;; uptime + (defwidget uptime [] + (box :class "genwin" + (box :orientation "h" :halign "center" :spacing 30 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "icontimer" :valign "center" :text "祥") + (box :orientation "v" :valign "center" :spacing 0 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "updays" :halign "center" :wrap "true" :limit-width 25 :text UPDAYS) + (label :class "uptime" :halign "center" :wrap "true" :limit-width 25 :text UPTIME))))) + + ;; Music + (defwidget music [] + (box :class "genwin" :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" + (box :class "album_art" :vexpand "false" :hexpand "false" :style "background-image: url('${COVER}');") + (box :orientation "v" :spacing 29 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :halign "center" :class "song" :wrap "true" :limit-width 15 :text SONG) + (label :halign "center" :class "artist" :wrap "true" :limit-width 18 :text ARTIST) + (box :orientation "h" :spacing 15 :halign "center" :space-evenly "true" :vexpand "false" :hexpand "false" + (button :class "btn_prev" :onclick "~/.config/eww/scripts/music_info --prev" "玲") + (button :class "btn_play" :onclick "~/.config/eww/scripts/music_info --toggle" STATUS) + (button :class "btn_next" :onclick "~/.config/eww/scripts/music_info --next" "怜")) + (box :class "music_bar" :halign "center" :vexpand "false" :hexpand "false" + ;; (scale :onscroll "mpc -q seek +1" :min 0 :active "true" :max 100 :value CURRENT) + )))) + + ;; Spotify + (defwidget spotify [] + (box :class "genwinb" :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" + (box :class "art" :vexpand "false" :hexpand "false" :style "background-image: url('${COVER}');" + (box :class "music_main" :orientation "v" :spacing 15 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :halign "start" :class "song" :wrap "false" :limit-width 15 :text SONG) + (label :halign "start" :class "artist" :wrap "false" :limit-width 15 :text ARTIST) + (box :orientation "h" :class "control" :spacing 20 :halign "start" :valign "end" :space-evenly "true" :vexpand "false" :hexpand "false" + (button :class "btn_prev" :onclick "~/.config/eww/scripts/music_info --prev" "玲") + (button :class "btn_play" :onclick "~/.config/eww/scripts/music_info --toggle" STATUS) + (button :class "btn_next" :onclick "~/.config/eww/scripts/music_info --next" "怜")))))) + + ;; github + (defwidget github [] + (box :class "github" :vexpand "false" :hexpand "false" + (button :class "iconweb" :onclick "~/.config/eww/scripts/open_links --gh" ""))) + + ;; reddit + (defwidget reddit [] + (box :class "reddit" :vexpand "false" :hexpand "false" + (button :class "iconweb" :onclick "~/.config/eww/scripts/open_links --rd" "樓"))) + + ;; twitter + (defwidget twitter [] + (box :class "twitter" :vexpand "false" :hexpand "false" + (button :class "iconweb" :onclick "~/.config/eww/scripts/open_links --tw" ""))) + + ;; youtube + (defwidget youtube [] + (box :class "youtube" :vexpand "false" :hexpand "false" + (button :class "iconweb" :onclick "~/.config/eww/scripts/open_links --yt" ""))) + + ;; mail + (defwidget mail [] + (box :class "mail" + (box :orientation "h" :halign "center" :spacing 20 :space-evenly "false" :vexpand "false" :hexpand "false" + (button :class "iconmail" :onclick "~/.config/eww/scripts/open_links --mail" "") + (box :class "mailbox" :space-evenly "false" :vexpand "false" :hexpand "false" + (button :class "label_mails" :onclick "~/.config/eww/scripts/open_links --mail" MAILS))))) + + ;; weather + (defwidget weather_info [] + (box :orientation "h" :vexpand "false" :hexpand "false" + (label :class "iconweather" :halign "start" :style "color: ${HEX};" :text ICON) + (label :class "label_stat" :valign "center" :text STAT) + (label :class "label_temp" :halign "end" :text TEMP))) + (defwidget weather_quotes [] + (box :orientation "v" :spacing 15 :halign "center" :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "label_quote" :text QUOTE) + (label :class "label_quote" :text QUOTE2))) + + ;; apps + (defwidget apps [] + (box :class "genwinh" :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" + (box :class "appbox" :orientation "v" :spacing 18 :space-evenly "false" :vexpand "false" :hexpand "false" + (button :style "background-image: url('images/icons/chrome.svg');" :class "app_chrome" :onclick "~/.config/eww/scripts/open_apps --gc") + (button :style "background-image: url('images/icons/telegram.svg');" :class "app_telegram" :onclick "~/.config/eww/scripts/open_apps --tg") + (button :style "background-image: url('images/icons/discord.svg');" :class "app_discord" :onclick "~/.config/eww/scripts/open_apps --dc") + (button :style "background-image: url('images/icons/terminal.svg');" :class "app_terminal" :onclick "~/.config/eww/scripts/open_apps --tr") + (button :style "background-image: url('images/icons/files.svg');" :class "app_files" :onclick "~/.config/eww/scripts/open_apps --fm")) + (box :class "appbox" :orientation "v" :spacing 18 :space-evenly "false" :vexpand "false" :hexpand "false" + (button :style "background-image: url('images/icons/code.svg');" :class "app_code" :onclick "~/.config/eww/scripts/open_apps --cd") + (button :style "background-image: url('images/icons/krita.svg');" :class "app_krita" :onclick "~/.config/eww/scripts/open_apps --kr") + (button :style "background-image: url('images/icons/virtualbox.svg');" :class "app_vbox" :onclick "~/.config/eww/scripts/open_apps --vb") + (button :style "background-image: url('images/icons/sublime.svg');" :class "app_sublime" :onclick "~/.config/eww/scripts/open_apps --st") + (button :style "background-image: url('images/icons/neovim.svg');" :class "app_neovim" :onclick "~/.config/eww/scripts/open_apps --nv")))) + + ;; power buttons + (defwidget logout [] + (box :class "genwinb" :vexpand "false" :hexpand "false" + (button :class "btn_logout" :onclick "openbox --exit" ""))) + (defwidget sleep [] + (box :class "genwinb" :vexpand "false" :hexpand "false" + (button :class "btn_sleep" :onclick "systemctl suspend" ""))) + (defwidget reboot [] + (box :class "genwinb" :vexpand "false" :hexpand "false" + (button :class "btn_reboot" :onclick "systemctl reboot" ""))) + (defwidget poweroff [] + (box :class "genwinb" :vexpand "false" :hexpand "false" + (button :class "btn_poweroff" :onclick "systemctl poweroff" ""))) + + ;; folders + (defwidget folders [] + (box :class "genwinb" :orientation "v" :spacing 4 :space-evenly "false" :vexpand "false" :hexpand "false" + (box :class "hddbox" :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" + (box :space-evenly "false" :vexpand "false" :hexpand "false" + (button :class "hddicon" :onclick "~/.config/eww/scripts/open_apps --fm" "")) + (label :class "fs_sep" :text "|") + (box :space-evenly "false" :vexpand "false" :hexpand "false" + (label :class "hdd_label" :wrap "true" :limit-width 25 :text FREE))) + (box :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" + (button :class "iconfolder1" :onclick "~/.config/eww/scripts/open_folders --docs" "") + (button :class "label_folder1" :onclick "~/.config/eww/scripts/open_folders --docs" "Documents")) + (box :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" + (button :class "iconfolder2" :onclick "~/.config/eww/scripts/open_folders --dl" "") + (button :class "label_folder2" :onclick "~/.config/eww/scripts/open_folders --dl" "Downloads")) + (box :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" + (button :class "iconfolder3" :onclick "~/.config/eww/scripts/open_folders --music" "") + (button :class "label_folder3" :onclick "~/.config/eww/scripts/open_folders --music" "Music")) + (box :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" + (button :class "iconfolder4" :onclick "~/.config/eww/scripts/open_folders --pics" "") + (button :class "label_folder4" :onclick "~/.config/eww/scripts/open_folders --pics" "Pictures")) + (box :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" + (button :class "iconfolder5" :onclick "~/.config/eww/scripts/open_folders --cfg" "ﮛ") + (button :class "label_folder5" :onclick "~/.config/eww/scripts/open_folders --cfg" "~/.config")) + (box :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" + (button :class "iconfolder6" :onclick "~/.config/eww/scripts/open_folders --local" "ﮛ") + (button :class "label_folder6" :onclick "~/.config/eww/scripts/open_folders --local" "~/.local")))) + + ;; workspaces + (defwidget workspaces[] + (box :class "genwinb" :orientation "v" :spacing 25 :space-evenly "false" :vexpand "false" :hexpand "false" + (box :orientation "h" :class "ws_holder" :spacing 18 :halign "center" :space-evenly "false" :vexpand "false" :hexpand "false" + (button :class "ws1" :halign "center" :wrap "true" :onclick "./launch_eww && i3-msg workspace \"1 : \"" "") + (button :class "ws2" :halign "center" :wrap "true" :onclick "./launch_eww && i3-msg workspace \"2 : \"" "") + (button :class "ws3" :halign "center" :wrap "true" :onclick "./launch_eww && i3-msg workspace \"3 : \"" "") + (button :class "ws4" :halign "center" :wrap "true" :onclick "./launch_eww && i3-msg workspace \"4 : 切\"" "切") + (button :class "ws5" :halign "center" :wrap "true" :onclick "./launch_eww && i3-msg workspace \"5 : \"" "")) + (box :orientation "h" :spacing 15 :halign "center" :space-evenly "false" :vexpand "false" :hexpand "false" + (button :class "ws6" :halign "center" :wrap "true" :onclick "./launch_eww && i3-msg workspace \"6 : \"" "") + (button :class "ws7" :halign "center" :wrap "true" :onclick "./launch_eww && i3-msg workspace \"7 : \"" "") + (button :class "ws8" :halign "center" :wrap "true" :onclick "./launch_eww && i3-msg workspace \"8 : \"" "") + (button :class "ws9" :halign "center" :wrap "true" :onclick "./launch_eww && i3-msg workspace \"9 : \"" "") + (button :class "ws10" :halign "center" :wrap "true" :onclick "./launch_eww && i3-msg workspace \"10 : \"" "")))) + + + ;; outers + (defwidget outera[] + (box :style "background:transparent;border-radius:20px;box-shadow:16px 16px 0 0 #282c34;")) + + (defwidget outerb[] + (box :style "background:transparent;border-radius:20px;box-shadow:-16px 16px 0 0 #282c34;")) + + (defwidget outerc[] + (box :style "background:transparent;border-radius:20px;box-shadow:16px -16px 0 0 #282c34;")) + + +;; ******************************************************************************** + + + + ;; profile + (defwindow profile :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 520 :y 235 :width 246 :height 320) + (nuser)) + + ;; system + (defwindow system :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 775 :y 235 :width 246 :height 204) + (system)) + + ;; clock + (defwindow clock :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 775:y 445 :width 246 :height 102) + (nclock)) + + ;; music + (defwindow music :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 520:y 560 :width 416 :height 224) + (spotify)) + + ;; github + (defwindow github :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 973 :y 690 :width 96 :height 92) + (github)) + + ;; reddit + (defwindow reddit :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 1073 :y 690 :width 96 :height 92) + (reddit)) + + ;; twitter + (defwindow twitter :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 1173 :y 690 :width 96 :height 92) + (twitter)) + + ;; youtube + (defwindow youtube :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 1275 :y 690 :width 96 :height 92) + (youtube)) + + ;; weather + (defwindow weather_info :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 750 :y 145 :width 438 :height 59) + (weather_info)) + (defwindow weather_quotes :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 745 :y 795 :width 438 :height 99) + (weather_quotes)) + + ;; apps + (defwindow apps :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 1035 :y 235 :width 126 :height 306) + (apps)) + + ;; mail + (defwindow mail :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 1170 :y 585 :width 202 :height 92) + (mail)) + + ;; logout + (defwindow logout :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 1400 :y 240 :width 104 :height 102) + (logout)) + + ;; sleep + (defwindow sleep :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 1400:y 360 :width 104 :height 102) + (sleep)) + + ;; reboot + (defwindow reboot :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 1400 :y 480 :width 104 :height 102) + (reboot)) + + ;; poweroff + (defwindow poweroff :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 1400 :y 600 :width 104 :height 102) + (poweroff)) + + ;; folders + (defwindow folders :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 1165:y 235 :width 202 :height 346) + (folders)) + + ;; workspaces + (defwindow workspaces :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 950 :y 550 :width 202 :height 132) + (workspaces)) + + ;; calendar + (defwindow calendar :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 1645:y 45 :width 270 :height 55) + (cal)) + + ;; calendar outer 1 + (defwindow roundeda :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x "630px" :y "7px" :width "32px" :height "32px") + (outera)) + + ;; calendar outer 2 + (defwindow roundedb :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x "662px" :y "7px" :width "32px" :height "32px") + (outerb)) + + ;; profile outer 1 + (defwindow roundedc :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x "355px" :y "403px" :width "32px" :height "32px") + (outerb)) + + ;; clock outer 1 + (defwindow roundedd :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 611 :y 393 :width 32 :height 32) + (outerb)) + + ;; clock outer 2 + (defwindow roundede :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 503 :y 425 :width 32 :height 32) + (outerc)) + + ;; apps outer 1 + (defwindow roundedf :stacking "fg" :focusable "false" :screen 1 + :geometry (geometry :x 715 :y 415 :width 32 :height 32) + (outerc)) diff --git a/eww/images/icons/chrome.svg b/eww/images/icons/chrome.svg new file mode 100644 index 0000000..717db57 --- /dev/null +++ b/eww/images/icons/chrome.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eww/images/icons/code.svg b/eww/images/icons/code.svg new file mode 100755 index 0000000..b340b75 --- /dev/null +++ b/eww/images/icons/code.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/eww/images/icons/discord.svg b/eww/images/icons/discord.svg new file mode 100755 index 0000000..a02373d --- /dev/null +++ b/eww/images/icons/discord.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/eww/images/icons/files.svg b/eww/images/icons/files.svg new file mode 100755 index 0000000..8a2c892 --- /dev/null +++ b/eww/images/icons/files.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/eww/images/icons/firefox.svg b/eww/images/icons/firefox.svg new file mode 100755 index 0000000..e14fafa --- /dev/null +++ b/eww/images/icons/firefox.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/eww/images/icons/geany.svg b/eww/images/icons/geany.svg new file mode 100755 index 0000000..94029ba --- /dev/null +++ b/eww/images/icons/geany.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/eww/images/icons/gimp.svg b/eww/images/icons/gimp.svg new file mode 100755 index 0000000..3a96df9 --- /dev/null +++ b/eww/images/icons/gimp.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/eww/images/icons/krita.svg b/eww/images/icons/krita.svg new file mode 100644 index 0000000..ab06e45 --- /dev/null +++ b/eww/images/icons/krita.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/eww/images/icons/neovim.svg b/eww/images/icons/neovim.svg new file mode 100644 index 0000000..17106f0 --- /dev/null +++ b/eww/images/icons/neovim.svg @@ -0,0 +1 @@ +Created with Sketch (http://www.bohemiancoding.com/sketch) \ No newline at end of file diff --git a/eww/images/icons/sublime.svg b/eww/images/icons/sublime.svg new file mode 100644 index 0000000..bb06513 --- /dev/null +++ b/eww/images/icons/sublime.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/eww/images/icons/telegram.svg b/eww/images/icons/telegram.svg new file mode 100755 index 0000000..37c62cf --- /dev/null +++ b/eww/images/icons/telegram.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/eww/images/icons/terminal.svg b/eww/images/icons/terminal.svg new file mode 100755 index 0000000..ea2f949 --- /dev/null +++ b/eww/images/icons/terminal.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/eww/images/icons/virtualbox.svg b/eww/images/icons/virtualbox.svg new file mode 100755 index 0000000..3883061 --- /dev/null +++ b/eww/images/icons/virtualbox.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/eww/images/mus.png b/eww/images/mus.png new file mode 100644 index 0000000..2f87951 Binary files /dev/null and b/eww/images/mus.png differ diff --git a/eww/images/music.jpg b/eww/images/music.jpg new file mode 100644 index 0000000..b21f8e2 Binary files /dev/null and b/eww/images/music.jpg differ diff --git a/eww/images/music.png b/eww/images/music.png new file mode 100644 index 0000000..17b1ba3 Binary files /dev/null and b/eww/images/music.png differ diff --git a/eww/images/pp.png b/eww/images/pp.png new file mode 100644 index 0000000..55ddf7c Binary files /dev/null and b/eww/images/pp.png differ diff --git a/eww/images/spotify.jpg b/eww/images/spotify.jpg new file mode 100644 index 0000000..61f88d0 Binary files /dev/null and b/eww/images/spotify.jpg differ diff --git a/eww/scripts/mails b/eww/scripts/mails new file mode 100755 index 0000000..a49a15d --- /dev/null +++ b/eww/scripts/mails @@ -0,0 +1,7 @@ +#!/bin/python + +import imaplib +obj = imaplib.IMAP4_SSL('imap.gmail.com',993) +obj.login('email','password') # write your email and password +obj.select() +print(len(obj.search(None, 'UnSeen')[1][0].split())) diff --git a/eww/scripts/music_info b/eww/scripts/music_info new file mode 100755 index 0000000..f567a29 --- /dev/null +++ b/eww/scripts/music_info @@ -0,0 +1,111 @@ +#!/bin/bash + +## Get data +# STATUS="$(mpc status)" +# COVER="/tmp/.music_cover.jpg" +# MUSIC_DIR="$HOME/Music" + +# DATA = `dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:org.mpris.MediaPlayer2.Player string:Metadata` + +# Playing, Paused or Stopped +# album = `dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:org.mpris.MediaPlayer2.Player string:Metadata | sed -n '/album/{n;p}' | cut -d '"' -f 2 | head -n 1` + +## Get status +get_status() { + status=`dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:'org.mpris.MediaPlayer2.Player' string:'PlaybackStatus'|egrep -A 1 "string"|cut -b 26-|cut -d '"' -f 1|egrep -v ^$` + if [[ "$(pidof spotify || echo 1)" != "1" ]]; then + if [[ "$status" == "Playing" ]]; then + echo "" + else + echo "喇" + fi + else + echo "" + fi +} + +## Get song +get_song() { + title=`dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:org.mpris.MediaPlayer2.Player string:Metadata | sed -n '/title/{n;p}' | cut -d '"' -f 2` + if [[ "$(pidof spotify || echo 1)" != "1" ]]; then + echo "$title" + else + echo "Offline" + fi +} + +## Get artist +get_artist() { + artist=`dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:org.mpris.MediaPlayer2.Player string:Metadata | sed -n '/artist/{n;n;p}' | cut -d '"' -f 2` + if [[ "$(pidof spotify || echo 1)" != "1" ]]; then + echo "$artist" + else + echo "Offline" + fi +} + +## Get time +get_time() { + time=`mpc status | grep "%)" | awk '{print $4}' | tr -d '(%)'` + if [[ -z "$time" ]]; then + echo "0" + else + echo "$time" + fi +} +get_ctime() { + ctime=`mpc status | grep "#" | awk '{print $3}' | sed 's|/.*||g'` + if [[ -z "$ctime" ]]; then + echo "0:00" + else + echo "$ctime" + fi +} +get_ttime() { + ttime=`mpc -f %time% current` + if [[ -z "$ttime" ]]; then + echo "0:00" + else + echo "$ttime" + fi +} + +## Get cover +get_cover() { + art=`dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:org.mpris.MediaPlayer2.Player string:Metadata | sed -n '/artUrl/{n;p}' | cut -d '"' -f 2` + if [[ "$(pidof spotify || echo 1)" != "1" ]]; then + echo "$art" + else + echo "images/spotify.jpg" + fi +} + +# Toggle +toggle() { + if [[ "$(pidof spotify || echo 1)" != "1" ]]; then + dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.PlayPause + fi +} + +## Execute accordingly +if [[ "$1" == "--song" ]]; then + get_song +elif [[ "$1" == "--artist" ]]; then + get_artist +elif [[ "$1" == "--status" ]]; then + get_status +elif [[ "$1" == "--time" ]]; then + get_time +elif [[ "$1" == "--ctime" ]]; then + get_ctime +elif [[ "$1" == "--ttime" ]]; then + get_ttime +elif [[ "$1" == "--cover" ]]; then + get_cover +elif [[ "$1" == "--toggle" ]]; then + toggle +elif [[ "$1" == "--next" ]]; then + { dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Next; get_cover; } +elif [[ "$1" == "--prev" ]]; then + { dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Previous; get_cover; } +fi diff --git a/eww/scripts/open_apps b/eww/scripts/open_apps new file mode 100755 index 0000000..3372fcd --- /dev/null +++ b/eww/scripts/open_apps @@ -0,0 +1,37 @@ +#!/bin/bash + +## Open Applications +FILE="$HOME/.cache/eww_launch.xyz" +EWW="/usr/bin/eww" + +if [[ "$1" == "--gc" ]]; then + ${EWW} close-all && rm -rf "$FILE" && google-chrome-stable & + +elif [[ "$1" == "--tg" ]]; then + ${EWW} close-all && rm -rf "$FILE" && telegram-desktop & + +elif [[ "$1" == "--dc" ]]; then + ${EWW} close-all && rm -rf "$FILE" && discord & + +elif [[ "$1" == "--tr" ]]; then + ${EWW} close-all && rm -rf "$FILE" && urxvt & + +elif [[ "$1" == "--fm" ]]; then + ${EWW} close-all && rm -rf "$FILE" && thunar ~ & + +elif [[ "$1" == "--nv" ]]; then + ${EWW} close-all && rm -rf "$FILE" && urxvt -e sh -c "nvim" & + +elif [[ "$1" == "--cd" ]]; then + ${EWW} close-all && rm -rf "$FILE" && code & + +elif [[ "$1" == "--kr" ]]; then + ${EWW} close-all && rm -rf "$FILE" && krita & + +elif [[ "$1" == "--vb" ]]; then + ${EWW} close-all && rm -rf "$FILE" && vit-manager & + +elif [[ "$1" == "--st" ]]; then + ${EWW} close-all && rm -rf "$FILE" && subl & + +fi diff --git a/eww/scripts/open_folders b/eww/scripts/open_folders new file mode 100755 index 0000000..4a4d73e --- /dev/null +++ b/eww/scripts/open_folders @@ -0,0 +1,25 @@ +#!/bin/bash + +## Open folders in thunar +FILE="$HOME/.cache/eww_launch.xyz" +EWW="/usr/bin/eww" + +if [[ "$1" == "--dl" ]]; then + ${EWW} close-all && rm -rf "$FILE" && thunar ~/Downloads & + +elif [[ "$1" == "--docs" ]]; then + ${EWW} close-all && rm -rf "$FILE" && thunar ~/Documents & + +elif [[ "$1" == "--music" ]]; then + ${EWW} close-all && rm -rf "$FILE" && thunar ~/Music & + +elif [[ "$1" == "--pics" ]]; then + ${EWW} close-all && rm -rf "$FILE" && thunar ~/Pictures & + +elif [[ "$1" == "--cfg" ]]; then + ${EWW} close-all && rm -rf "$FILE" && thunar ~/.config & + +elif [[ "$1" == "--local" ]]; then + ${EWW} close-all && rm -rf "$FILE" && thunar ~/.local/share & + +fi diff --git a/eww/scripts/open_links b/eww/scripts/open_links new file mode 100755 index 0000000..a7d332f --- /dev/null +++ b/eww/scripts/open_links @@ -0,0 +1,23 @@ +#!/bin/bash + +## Open links in firefox +FILE="$HOME/.cache/eww_launch.xyz" +EWW="/usr/bin/eww" +cmd="xdg-open" + +if [[ "$1" == "--mail" ]]; then + ${EWW} close-all && rm -rf "$FILE" && $cmd "https://mail.google.com" + +elif [[ "$1" == "--gh" ]]; then + ${EWW} close-all && rm -rf "$FILE" && $cmd "https://github.com/S4NKALP" + +elif [[ "$1" == "--rd" ]]; then + ${EWW} close-all && rm -rf "$FILE" && $cmd "https://reddit.com/S4NKALP" + +elif [[ "$1" == "--tw" ]]; then + ${EWW} close-all && rm -rf "$FILE" && $cmd "https://twitter.com" + +elif [[ "$1" == "--yt" ]]; then + ${EWW} close-all && rm -rf "$FILE" && $cmd "https://youtube.com" + +fi diff --git a/eww/scripts/sys_info b/eww/scripts/sys_info new file mode 100755 index 0000000..fefc7f4 --- /dev/null +++ b/eww/scripts/sys_info @@ -0,0 +1,78 @@ +#!/bin/bash + +## Files and Data +PREV_TOTAL=0 +PREV_IDLE=0 +cpuFile="/tmp/.cpu_usage" + +## Get CPU usage +get_cpu() { + if [[ -f "${cpuFile}" ]]; then + fileCont=$(cat "${cpuFile}") + PREV_TOTAL=$(echo "${fileCont}" | head -n 1) + PREV_IDLE=$(echo "${fileCont}" | tail -n 1) + fi + + CPU=(`cat /proc/stat | grep '^cpu '`) # Get the total CPU statistics. + unset CPU[0] # Discard the "cpu" prefix. + IDLE=${CPU[4]} # Get the idle CPU time. + + # Calculate the total CPU time. + TOTAL=0 + + for VALUE in "${CPU[@]:0:4}"; do + let "TOTAL=$TOTAL+$VALUE" + done + + if [[ "${PREV_TOTAL}" != "" ]] && [[ "${PREV_IDLE}" != "" ]]; then + # Calculate the CPU usage since we last checked. + let "DIFF_IDLE=$IDLE-$PREV_IDLE" + let "DIFF_TOTAL=$TOTAL-$PREV_TOTAL" + let "DIFF_USAGE=(1000*($DIFF_TOTAL-$DIFF_IDLE)/$DIFF_TOTAL+5)/10" + echo "${DIFF_USAGE}" + else + echo "?" + fi + + # Remember the total and idle CPU times for the next check. + echo "${TOTAL}" > "${cpuFile}" + echo "${IDLE}" >> "${cpuFile}" +} + +## Get Used memory +get_mem() { + printf "%.0f\n" $(free -m | grep Mem | awk '{print ($3/$2)*100}') +} + +## Get Brightness +get_blight() { + CARD=`ls /sys/class/backlight | head -n 1` + + if [[ "$CARD" == *"intel_"* ]]; then + BNESS=`xbacklight -get` + LIGHT=${BNESS%.*} + else + BNESS=`blight -d $CARD get brightness` + PERC="$(($BNESS*100/255))" + LIGHT=${PERC%.*} + fi + + echo "$LIGHT" +} + +## Get Battery +get_battery() { + BAT=`ls /sys/class/power_supply | grep BAT | head -n 1` + cat /sys/class/power_supply/${BAT}/capacity +} + +## Execute accordingly +if [[ "$1" == "--cpu" ]]; then + get_cpu +elif [[ "$1" == "--mem" ]]; then + get_mem +elif [[ "$1" == "--blight" ]]; then + get_blight +elif [[ "$1" == "--bat" ]]; then + get_battery +fi diff --git a/eww/scripts/weather_info b/eww/scripts/weather_info new file mode 100755 index 0000000..17715af --- /dev/null +++ b/eww/scripts/weather_info @@ -0,0 +1,147 @@ +#!/bin/bash + +## Collect data +cache_dir="$HOME/.cache/eww/weather" +cache_weather_stat=${cache_dir}/weather-stat +cache_weather_degree=${cache_dir}/weather-degree +cache_weather_quote=${cache_dir}/weather-quote +cache_weather_hex=${cache_dir}/weather-hex +cache_weather_icon=${cache_dir}/weather-icon + +## Weather data +KEY="ab662f7f800fc75b3e7bc6d00e84193e" +ID="6941099" +UNIT="metric" # Available options : 'metric' or 'imperial' + +## Make cache dir +if [[ ! -d "$cache_dir" ]]; then + mkdir -p ${cache_dir} +fi + +## Get data +get_weather_data() { + weather=`curl -sf "http://api.openweathermap.org/data/2.5/weather?APPID=$KEY&id=$ID&units=$UNIT"` + echo ${weather} + + if [ ! -z "$weather" ]; then + weather_temp=`echo "$weather" | jq ".main.temp" | cut -d "." -f 1` + weather_icon_code=`echo "$weather" | jq -r ".weather[].icon" | head -1` + weather_description=`echo "$weather" | jq -r ".weather[].description" | head -1 | sed -e "s/\b\(.\)/\u\1/g"` + + #Big long if statement of doom + if [ "$weather_icon_code" == "50d" ]; then + weather_icon=" " + weather_quote="Forecast says it's misty \nMake sure you don't get lost on your way..." + weather_hex="#84afdb" + elif [ "$weather_icon_code" == "50n" ]; then + weather_icon=" " + weather_quote="Forecast says it's a misty night \nDon't go anywhere tonight or you might get lost..." + weather_hex="#84afdb" + elif [ "$weather_icon_code" == "01d" ]; then + weather_icon=" " + weather_quote="It's a sunny day, gonna be fun! \nDon't go wandering all by yourself though..." + weather_hex="#ffd86b" + elif [ "$weather_icon_code" == "01n" ]; then + weather_icon=" " + weather_quote="It's a clear night \nYou might want to take a evening stroll to relax..." + weather_hex="#fcdcf6" + elif [ "$weather_icon_code" == "02d" ]; then + weather_icon=" " + weather_quote="It's cloudy, sort of gloomy \nYou'd better get a book to read..." + weather_hex="#adadff" + elif [ "$weather_icon_code" == "02n" ]; then + weather_icon=" " + weather_quote="It's a cloudy night \nHow about some hot chocolate and a warm bed?" + weather_hex="#adadff" + elif [ "$weather_icon_code" == "03d" ]; then + weather_icon=" " + weather_quote="It's cloudy, sort of gloomy \nYou'd better get a book to read..." + weather_hex="#adadff" + elif [ "$weather_icon_code" == "03n" ]; then + weather_icon=" " + weather_quote="It's a cloudy night \nHow about some hot chocolate and a warm bed?" + weather_hex="#adadff" + elif [ "$weather_icon_code" == "04d" ]; then + weather_icon=" " + weather_quote="It's cloudy, sort of gloomy \nYou'd better get a book to read..." + weather_hex="#adadff" + elif [ "$weather_icon_code" == "04n" ]; then + weather_icon=" " + weather_quote="It's a cloudy night \nHow about some hot chocolate and a warm bed?" + weather_hex="#adadff" + elif [ "$weather_icon_code" == "09d" ]; then + weather_icon=" " + weather_quote="It's rainy, it's a great day! \nGet some ramen and watch as the rain falls..." + weather_hex="#6b95ff" + elif [ "$weather_icon_code" == "09n" ]; then + weather_icon=" " + weather_quote=" It's gonna rain tonight it seems \nMake sure your clothes aren't still outside..." + weather_hex="#6b95ff" + elif [ "$weather_icon_code" == "10d" ]; then + weather_icon=" " + weather_quote="It's rainy, it's a great day! \nGet some ramen and watch as the rain falls..." + weather_hex="#6b95ff" + elif [ "$weather_icon_code" == "10n" ]; then + weather_icon=" " + weather_quote=" It's gonna rain tonight it seems \nMake sure your clothes aren't still outside..." + weather_hex="#6b95ff" + elif [ "$weather_icon_code" == "11d" ]; then + weather_icon="" + weather_quote="There's storm for forecast today \nMake sure you don't get blown away..." + weather_hex="#ffeb57" + elif [ "$weather_icon_code" == "11n" ]; then + weather_icon="" + weather_quote="There's gonna be storms tonight \nMake sure you're warm in bed and the windows are shut..." + weather_hex="#ffeb57" + elif [ "$weather_icon_code" == "13d" ]; then + weather_icon=" " + weather_quote="It's gonna snow today \nYou'd better wear thick clothes and make a snowman as well!" + weather_hex="#e3e6fc" + elif [ "$weather_icon_code" == "13n" ]; then + weather_icon=" " + weather_quote="It's gonna snow tonight \nMake sure you get up early tomorrow to see the sights..." + weather_hex="#e3e6fc" + elif [ "$weather_icon_code" == "40d" ]; then + weather_icon=" " + weather_quote="Forecast says it's misty \nMake sure you don't get lost on your way..." + weather_hex="#84afdb" + elif [ "$weather_icon_code" == "40n" ]; then + weather_icon=" " + weather_quote="Forecast says it's a misty night \nDon't go anywhere tonight or you might get lost..." + weather_hex="#84afdb" + else + weather_icon=" " + weather_quote="Sort of odd, I don't know what to forecast \nMake sure you have a good time!" + weather_hex="#adadff" + fi + echo "$weather_icon" > ${cache_weather_icon} + echo "$weather_description" > ${cache_weather_stat} + echo "$weather_temp""°C" > ${cache_weather_degree} + echo -e "$weather_quote" > ${cache_weather_quote} + echo "$weather_hex" > ${cache_weather_hex} + else + echo "Weather Unavailable" > ${cache_weather_stat} + echo " " > ${cache_weather_icon} + echo -e "Ah well, no weather huh? \nEven if there's no weather, it's gonna be a great day!" > ${cache_weather_quote} + echo "-" > ${cache_weather_degree} + echo "#adadff" > ${tcache_weather_hex} + fi +} + +## Execute +if [[ "$1" == "--getdata" ]]; then + get_weather_data +elif [[ "$1" == "--icon" ]]; then + cat ${cache_weather_icon} +elif [[ "$1" == "--temp" ]]; then + cat ${cache_weather_degree} +elif [[ "$1" == "--hex" ]]; then + cat ${cache_weather_hex} +elif [[ "$1" == "--stat" ]]; then + cat ${cache_weather_stat} +elif [[ "$1" == "--quote" ]]; then + cat ${cache_weather_quote} | head -n1 +elif [[ "$1" == "--quote2" ]]; then + cat ${cache_weather_quote} | tail -n1 +fi + diff --git a/eww/sidebar/eww.scss b/eww/sidebar/eww.scss new file mode 100755 index 0000000..f4a2543 --- /dev/null +++ b/eww/sidebar/eww.scss @@ -0,0 +1,242 @@ +* { + all : unset; + +} + +$bg: #0d0f18; +$bg-alt: #151720; +$fg: #c0caf5; +$black: #151720; +$gray: #1c1e27; +$red: #dd6777; +$green: #90ceaa; +$yellow: #ecd3a0; +$blue: #86aaec; +$magenta: #c296eb; +$cyan: #93cee9; +$white: $fg; +$archlogo: #0f94d2; + +.profilebox { + background-color: $bg-alt; + border-radius: 12px; + margin-top: .7rem; +} + +.pfpface { + margin: 1rem 0rem 1rem 1rem; + +} + +.welcometxt { + font-size: 1.3rem; + font-style: italic; + font-family: Cartograph CF; + color: $fg; + margin: 1.3rem 0rem 0rem 1.3rem; +} + +.pfpapps { + margin: 1.3rem 0rem 0rem 1.3rem; + +} + +.launcher-btn { + color: $fg; + font-family: Material Design Icons Desktop; + font-size: 1.3rem; +} + +.launcher-btn:hover { + color: $blue; + font-family: Material Design Icons Desktop; + font-size: 1.3rem; + +} + +.weatherbox { + background-color: $bg-alt; + border-radius: 12px; + padding: .9rem; + margin-top: .7rem; +} + +.weathericon { + padding: 2rem 1rem; + margin-top: .7rem; + border-radius: 12px; + margin-left: .7rem; + background-color: $bg-alt; + font-size: 2rem; + font-family: "Material Design Icon Desktop"; +} + +.weatherdesc { + font-size: 1.75rem; + font-family: "Iosevka Nerd Font"; +} + +.weathertemp { + font-size: 1.5rem; + font-weight: bold; + color: $fg; + font-family: "Iosevka Nerd Font"; +} + +.weathercity { + font-size: .9rem; + margin-top: 1rem; + color: $fg; + font-family: "Iosevka Nerd Font"; +} + +.playerbox { + background-color: $bg-alt; + border-radius: 12px; + padding: .9rem; + margin-top: .7rem; +} + +.album_art { + background-repeat: no-repeat; + background-size: cover; + background-position: center; + border-radius: 7px; + padding: 3rem; +} + +.song { + font-family: JetBrainsMono Nerd Font; + font-weight: bold; + font-style: italic; + font-size: 1rem; + color: $fg; +} + +.artist { + font-family: JetBrainsMono Nerd Font; + font-weight: bold; + color: $fg; + font-size: .8rem; +} + +.btn_prev, +.btn_play, +.btn_next { + font-family : Iosevka; + color: $fg; + font-size: 20px; +} + +.sysbox { + background-color: $bg-alt; + border-radius: 12px; + padding: .9rem; + margin-top: .7rem; +} + +.calouterbox { + padding: 1.75rem 1.35rem; + background-color: $bg; + border-radius: 12px; +} + +.caltime { + font-family: "Iosevka Nerd Font"; + margin-bottom: 1rem; + margin-top: .5rem; +} + +.caltext { + font-weight: bold; + font-size: 3rem; + color: $fg; +} + +.infodate { + font-weight: bold; + font-size: 1.5rem; + font-family: "Iosevka Nerd Font"; + margin-bottom: 1rem; + color: $fg; +} + +.caltimeboxes { + margin: 0 2rem; + font-family: "Material Design Icon Desktop"; + font-size: .6rem; +} + +.red { + color: $red; +} +.yellow { + color: $yellow; +} + +.green { + color: $green; +} +.magenta { + color: $magenta; +} + +.blue { + color: $blue; +} +.cyan { + color: $cyan; +} + +.white { + color: $red; +} + +.stats-icon-cpu, +.stats-icon-ram, +.stats-icon-fs { + font-size: 1.5rem; +} + +.stats-icon-cpu { + color: $red; +} + +.stats-icon-ram { + color: $green; +} + +.stats-icon-fs { + color: $yellow; +} + +.scale-cpu, +.scale-ram, +.scale-fs, +scale trough { + all: unset; + background-color: $gray; + min-height: 10px; + border-radius: 15px; + margin-left: 25px; +} + +.scale-cpu, +.scale-ram, +.scale-fs, +scale trough highlight { + all: unset; + border-radius: 15px; +} + +.scale-cpu trough highlight { + background: linear-gradient(to right, $magenta, $red); +} + +.scale-ram trough highlight { + background: linear-gradient(to right, $green, $yellow); +} + +.scale-fs trough highlight { + background: linear-gradient(to right, $yellow, $fg); +} diff --git a/eww/sidebar/eww.yuck b/eww/sidebar/eww.yuck new file mode 100755 index 0000000..8deebb1 --- /dev/null +++ b/eww/sidebar/eww.yuck @@ -0,0 +1,155 @@ + + +;; ** Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defpoll hour :interval "1s" `date +%I`) +(defpoll minute :interval "1m" `date +%M\%p`) +(defpoll fulldate :interval "24h" "date '+%A, %d %B'") + +(defvar pfpimage "../images/pp.png") +(defpoll iam :interval "6h" `whoami`) + +(defpoll weathericon :interval "2h" "~/.config/eww/sidebar/scripts/Weather icon") +(defpoll weathercity :interval "2h" "~/.config/eww/sidebar/scripts/Weather city") +(defpoll weathertemp :interval "2h" "~/.config/eww/sidebar/scripts/Weather current_temp") +(defpoll weatherfeel :interval "2h" "~/.config/eww/sidebar/scripts/Weather feels_like") +(defpoll weatherdesc :interval "2h" "~/.config/eww/sidebar/scripts/Weather weather_desc") +(defpoll weatherhex :interval "2h" "~/.config/eww/sidebar/scripts/Weather hex") + +(defpoll title :interval "0s" "~/.config/eww/sidebar/scripts/music title") +(defpoll artist :interval "0s" "~/.config/eww/sidebar/scripts/music artist") +(defpoll album :interval "0s" "~/.config/eww/sidebar/scripts/music album") +(defpoll playpause :interval "0s" "~/.config/eww/sidebar/scripts/music statusicon") +(defpoll art :interval "0s" "~/.config/eww/sidebar/scripts/music coverloc") + +;; ** Widgets ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; User info Widget ;; +(defwidget pfp[] + (box :orientation "h" :class "pfpbox" :space-evenly false + (box :orientation "v" :height 100 :class "profilebox" :hexpand true :space-evenly false + (box :orientation "h" :class "pfpboxtop" :space-evenly false + (box :class "pfpface" :halign "start" :hexpand false + (image :path "${pfpimage}" :image-width 75 :image-height 75)) + + (box :orientation "v" :class "pfpboxbot" :space-evenly false + (box :class "welcometxt" :halign "start" :valign "center" "Welcome ${iam}") + (box :class "pfpapps" :halign "center" :valign "center" :hexpand true :spacing 20 + (button :class "launcher-btn" + :onclick "google-chrome-stable & disown" + :tooltip "Google-Chrome" + "󰊯") + (button :class "launcher-btn" + :onclick "github-desktop" + :tooltip "Sys Fetch" + "󰊤") + (button :class "launcher-btn" + :onclick "thunar ~ & disown" + :tooltip "Thunar" + "󰉋") + (button :class "launcher-btn" + :onclick "Term --ot" + :tooltip "Terminal" + "󰞷") + (button :class "launcher-btn" + :onclick "subl & disown" + :tooltip "Editor" + "󰅴")) + )) + ) + + ) +) + +;; Weather widget ;; +(defwidget weather[] + (box :orientation "h" :class "weatherfullbox" :space-evenly false + (box :orientation "v" :height 100 :class "weatherbox" :hexpand true :space-evenly false + (box :orientation "h" :class "weathertop" :space-evenly false + (box :class "weatherdesc" :style "color: ${weatherhex};" :halign "start" :hexpand true weatherdesc) + (box :class "weathertemp" :halign "end" "${weathertemp}°C") + ) + (box :orientation "h" :class "weatherbot" :space-evenly false + (box :class "weathercity" :halign "start" :hexpand true weathercity) + (box :class "weathercity" :halign "end" "Feels like ${weatherfeel}°C") + ) + ) + (box :class "weathericon" :style "color: ${weatherhex};" :halign "center" weathericon) + ) +) + +;; Music Player Widget ;; +(defwidget musicplayer[] + (box :orientation "h" :class "playerfullbox" :space-evenly false + (box :orientation "v" :height 100 :class "playerbox" :hexpand true :space-evenly false + (box :orientation "h" :class "playertop" :space-evenly false :spacing 15 + (box :class "album_art" :style "background-image: url('${art}');") + (box :class "playerinfo":orientation "v" :halign "center" :hexpand true + (label :class "song" :limit-width 18 :text title) + (label :class "artist" :limit-width 18 :text artist) + (box :orientation "h" :class "playerbot" :space-evenly false :valign "center" :halign "center" :spacing 30 + (button :class "btn_prev" :onclick "~/.config/eww/scripts/sidebar/music previous" "玲") + (button :class "btn_play" :onclick "~/.config/eww/sidebar/scripts/music toggle" playpause) + (button :class "btn_next" :onclick "~/.config/eww/sidebar/scripts/music next" "怜") + ) + ) + ) + ) + ) +) + +;; System info Widget ;; +(defwidget sysinfo[] + (box :orientation "h" :class "sysfullbox" :space-evenly false + (box :orientation "v" :height 100 :class "sysbox" :hexpand true :space-evenly false + (box :orientation "v" :class "systop" :space-evenly false :spacing 15 + (box :class "stats" :hexpand "false" :vexpand "false" :space-evenly "false" + (label :tooltip "${round(EWW_CPU.avg, 0)}%" :class "stats-icon-cpu" :text "") + (scale :min 0 :max 101 :active false :value {EWW_CPU.avg} :class "scale-cpu" :width 265) + ) + (box :class "stats" :hexpand "false" :vexpand "false" :space-evenly "false" + (label :tooltip "${round(EWW_RAM.used_mem_perc, 0)}%" :class "stats-icon-ram" :text "﬙") + (scale :min 0 :max 101 :active false :value {EWW_RAM.used_mem_perc} :class "scale-ram" :width 265) + ) + (box :class "stats" :hexpand "false" :vexpand "false" :space-evenly "false" + (label :tooltip "${round(EWW_DISK["/"].used_perc, 0)}%" :class "stats-icon-fs" :text "") + (scale :min 0 :max 101 :active false :value {(EWW_DISK["/"].used / EWW_DISK["/"].total) * 100} :class "scale-fs" :width 265) + ) + ) + ) + ) +) + +;; The Time Widget ;; +(defwidget infotime[] + (box :halign "center" :class "caltime" :orientation "h" :space-evenly false + (box :class "caltext" "${hour}") + (box :class "caltimeboxes" :valign "center" :spacing 5 :space-evenly false :orientation 'v' + (box :class "caltimebox magenta" "󰝤") + (box :class "caltimebox green" "󰝤") + (box :class "caltimebox blue" "󰝤") + ) + (box :class "caltext" "${minute}") + ) +) + +;; Panel Widget ;; +(defwidget ipanel[] + (box :orientation "v" :width 360 :space-evenly false :class "calouterbox" + (infotime) + (box :class "infodate" "${fulldate}") + (pfp) + (weather) + (musicplayer) + (sysinfo) + ) +) + +;; ** Windows ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + (defwindow dashboard + :geometry (geometry :x "0.2%" + :y "49.5px" + :anchor "left top") + :stacking "fg" +(ipanel)) diff --git a/eww/sidebar/scripts/Weather b/eww/sidebar/scripts/Weather new file mode 100755 index 0000000..b343cdb --- /dev/null +++ b/eww/sidebar/scripts/Weather @@ -0,0 +1,133 @@ +#!/usr/bin/env bash + +encode_to_url_format() { + echo "$1" | sed 's/ /%20/g' +} + +check_if_empty() { + [[ -z "$1" ]] && echo "0" || echo "$1" +} + +KEY="1f6e449790b8cf73a3cab8d5715483f8" +CITY="Kohalpur" +CITYN=$(encode_to_url_format "$CITY") +WEATHER=$(curl -sf "api.openweathermap.org/data/2.5/weather?q=$CITYN&appid=$KEY&units=metric") + +WEATHER_DESC=$(echo $WEATHER | jq -r ".weather[0].main") +WEATHER_TEMP=$(echo $WEATHER | jq ".main.temp" | cut -d "." -f 1) +WEATHER_ICON_CODE=$(echo "$WEATHER" | jq -r ".weather[].icon" | head -1) +WEATHER_FEELS_LIKE=$(echo $WEATHER | jq ".main.feels_like" | cut -d "." -f 1) +WEATHER_HUMIDITY=$(echo $WEATHER | jq ".main.humidity" | cut -d "." -f 1) +WEATHER_ICON="" +WEATHER_HEX="" + +case $WEATHER_ICON_CODE in + "01d") + WEATHER_ICON="󰖙" + WEATHER_HEX="#ffd86b" + ;; + "01n") + WEATHER_ICON="󰖔" + WEATHER_HEX="#fcdcf6" + ;; + "02d") + WEATHER_ICON="󰖕" + WEATHER_HEX="#adadff" + ;; + "02n") + WEATHER_ICON="󰼱" + WEATHER_HEX="#adadff" + ;; + "03d") + WEATHER_ICON="󰖐" + WEATHER_HEX="#adadff" + ;; + "03n") + WEATHER_ICON="󰖐" + WEATHER_HEX="#adadff" + ;; + "04d") + WEATHER_ICON="󰖐" + WEATHER_HEX="#adadff" + ;; + "04n") + WEATHER_ICON="󰖐" + WEATHER_HEX="#acb0d0" + ;; + "09d") + WEATHER_ICON="󱐋" + WEATHER_HEX="#6b95ff" + ;; + "09n") + WEATHER_ICON="󱐋" + WEATHER_HEX="#6b95ff" + ;; + "10d") + WEATHER_ICON="󱐋" + WEATHER_HEX="#6b95ff" + ;; + "10n") + WEATHER_ICON="󱐋" + WEATHER_HEX="#6b95ff" + ;; + "11d") + WEATHER_ICON="󱐋" + WEATHER_HEX="#ffeb57" + ;; + "11n") + WEATHER_ICON="󱐋" + WEATHER_HEX="#ffeb57" + ;; + "13d") + WEATHER_ICON="󰜗" + WEATHER_HEX="#e3e6fc" + ;; + "13n") + WEATHER_ICON="󰜗" + WEATHER_HEX="#e3e6fc" + ;; + "40d") + WEATHER_ICON="󰖝" + WEATHER_HEX="#84afdb" + ;; + "40n") + WEATHER_ICON="󰖝" + WEATHER_HEX="#84afdb" + ;; + *) + WEATHER_ICON="󰖐" + WEATHER_HEX="#adadff" + ;; +esac + +case $1 in + "current_temp") + check_if_empty $WEATHER_TEMP + ;; + "current_temp_fahrenheit") + WEATHER_TEMP=$((($WEATHER_TEMP * 9 / 5) + 32)) + check_if_empty $WEATHER_TEMP + ;; + "feels_like") + check_if_empty $WEATHER_FEELS_LIKE + ;; + "humidity") + check_if_empty $WEATHER_HUMIDITY + ;; + "weather_desc") + [[ -z $WEATHER_DESC ]] && echo "Not Available." || echo "$WEATHER_DESC" + ;; + "icon") + echo $WEATHER_ICON + ;; + "hex") + echo $WEATHER_HEX + ;; + "full") + echo $WEATHER + ;; + "city") + echo $CITY + ;; +esac + diff --git a/eww/sidebar/scripts/WeatherMini b/eww/sidebar/scripts/WeatherMini new file mode 100755 index 0000000..f8545d0 --- /dev/null +++ b/eww/sidebar/scripts/WeatherMini @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +weather=$(curl -s wttr.in/Kohalpur?format=3) + +if [ $(echo "$weather" | grep -E "(Unknown|curl|HTML)" | wc -l) -gt 0 ]; then + echo "WEATHER UNAVAILABLE" +else + echo "$weather" | awk '{print $2" "$3}' +fi + diff --git a/eww/sidebar/scripts/getSongDuration b/eww/sidebar/scripts/getSongDuration new file mode 100755 index 0000000..7a47a7d --- /dev/null +++ b/eww/sidebar/scripts/getSongDuration @@ -0,0 +1,3 @@ +#/usr/bin/env sh + +mpc status | grep "%)" | awk '{ print $4 }' | awk -F% '{ print $1 }' | awk 'sub(/^.{1}/,"")' diff --git a/eww/sidebar/scripts/gettitle b/eww/sidebar/scripts/gettitle new file mode 100755 index 0000000..0f16ed6 --- /dev/null +++ b/eww/sidebar/scripts/gettitle @@ -0,0 +1,13 @@ +#!/bin/bash +title=$(playerctl metadata --format '{{ title }}') +count=$(echo -n "$title" | wc -c) +if [ -z "$title" ]; then + echo "Nothing Playing..."; +else + if [ "$count" -le 18 ]; then + echo $title + else + echo $(playerctl metadata --format '{{ title }}' | cut -c-18)... + fi +fi + diff --git a/eww/sidebar/scripts/music b/eww/sidebar/scripts/music new file mode 100755 index 0000000..21ab77e --- /dev/null +++ b/eww/sidebar/scripts/music @@ -0,0 +1,220 @@ +#!/bin/bash + + +# Controls spotify if it's running, (kill spotify process to switch) + + +# add different players here (also at the end) + +if [[ -n $(pgrep spotify) ]]; then +Control="spotify" +else +Control="MPD" +fi + +# Covers will be saved here (for both the players) +Cover=/tmp/cover.png +# mpd music directory +mpddir=~/Music + +############# Spotify ############## + +spotcover() { + +albumart="$(playerctl --player=spotify metadata mpris:artUrl | sed -e 's/open.spotify.com/i.scdn.co/g')" + +if [ $(playerctl --player=spotify metadata mpris:artUrl) ]; then + curl -s "$albumart" --output $Cover; +else cp ~/.config/eww/images/music.png $Cover; +fi +echo "$Cover" +} + +spotstatusicon() { +if [[ $(playerctl --player=spotify status 2>&1) == "Playing" ]];then + echo "" +elif [[ $(playerctl --player=spotify status 2>&1) == "Paused" ]];then + echo "喇" +else + echo "ﭥ" +fi +} + +############ MPD ################# + +### + +mpctitle() { +song=`mpc -f %title% current` + + if [[ -n "$song" ]]; then + echo "$song" + else + echo "Play Someting" + fi +} + +mpcartist() { +artist=`mpc -f %artist% current` + if [[ -n "$artist" ]]; then + echo "$artist" + else + echo "Artist" + fi +} + +mpcalbum() { +album=`mpc -f %album% current` + if [[ -n "$album" ]]; then + echo "$album" + else + echo "Album" + fi +} + +mpccover() { + ffmpeg -i "$mpddir"/"$(mpc current -f %file%)" "${Cover}" -y &> /dev/null + STATUS=$? + + if [ "$STATUS" -eq 0 ];then + echo "$Cover" + else + cp ~/.config/eww/images/music.png $Cover; + echo "$Cover" + fi +} + +mpctime() { + time=`mpc status %currenttime%` + if [[ -n "$time" ]]; then + echo "$time" + else + echo "0:00" + fi +} + +mpclength() { + length=`mpc status %totaltime%` + if [[ -n "$length" ]]; then + echo "$length" + else + echo "0:00" + fi +} + + +mpcicon() { + if [[ $(mpc status) == *"[playing]"* ]]; then + echo "" + else + echo "喇" + fi +} + + +mpcstatus() { + if [[ $(mpc status) == *"[playing]"* ]]; then + echo "Playing" + elif [[ $(mpc status) == *"[paused]"* ]]; then + echo "Paused" + else + echo "Stopped" + fi +} + +mpcperc() { + perc=`mpc status %percenttime%` + if [[ -n "$perc" ]]; then + echo "$perc" + else + echo "0" + fi +} + + +mpcsongpos() { + pos=`mpc status %songpos%` + length=`mpc status %length%` + if [[ -n "$pos" ]]; then + echo "$pos/$length" + else + echo "0/0" + fi +} + + + +case $Control in + + spotify) + + if [[ $1 == "next" ]];then + playerctl --player=spotify next + elif [[ $1 == "previous" ]];then + playerctl --player=spotify previous + elif [[ $1 == "toggle" ]];then + playerctl --player=spotify play-pause + elif [[ $1 == "title" ]];then + playerctl --player=spotify metadata --format {{title}} + elif [[ $1 == "artist" ]];then + playerctl --player=spotify metadata --format {{artist}} + elif [[ $1 == "album" ]];then + playerctl --player=spotify metadata --format {{album}} + elif [[ $1 == "status" ]];then + playerctl --player=spotify status + elif [[ $1 == "statusicon" ]];then + spotstatusicon + elif [[ $1 == "player" ]];then + echo " Spotify" + elif [[ $1 == "coverloc" ]];then + spotcover + elif [[ $1 == "showcover" ]];then + spotcover | xargs feh + elif [[ $1 == "time" ]];then + echo "0:00" + elif [[ $1 == "length" ]];then + echo "0:00" + elif [[ $1 == "percent" ]];then + echo "0" + elif [[ $1 == "playlist" ]];then + playerctl metadata xesam:trackNumber + fi + + ;; + + MPD) + if [[ $1 == "next" ]];then + mpc -q next + elif [[ $1 == "previous" ]];then + mpc -q prev + elif [[ $1 == "toggle" ]];then + mpc -q toggle + elif [[ $1 == "title" ]];then + mpctitle + elif [[ $1 == "artist" ]];then + mpcartist + elif [[ $1 == "album" ]];then + mpcalbum + elif [[ $1 == "status" ]];then + mpcstatus + elif [[ $1 == "statusicon" ]];then + mpcicon + elif [[ $1 == "player" ]];then + echo " MPD" + elif [[ $1 == "coverloc" ]];then + mpccover + elif [[ $1 == "showcover" ]];then + mpccover | xargs feh + elif [[ $1 == "time" ]];then + mpctime + elif [[ $1 == "length" ]];then + mpclength + elif [[ $1 == "percent" ]];then + mpcperc + elif [[ $1 == "playlist" ]];then + mpcsongpos + fi + +esac 2>/dev/null + + diff --git a/widgets/MusicPlayer/MediaControl b/widgets/MusicPlayer/MediaControl new file mode 100755 index 0000000..6cd7826 --- /dev/null +++ b/widgets/MusicPlayer/MediaControl @@ -0,0 +1,281 @@ +#!/usr/bin/env bash + +# ╔╦╗ ╦ ╦ ╔═╗ ╦ ╔═╗ +# ║║║ ║ ║ ╚═╗ ║ ║ +# ╩ ╩ ╚═╝ ╚═╝ ╩ ╚═╝ + +# author Niraj +# github niraj998 + +# ┌─┐┌─┐┌┐┌┌─┐┬┌─┐┬ ┬┬─┐┌─┐┌┬┐┬┌─┐┌┐┌┌─┐ +# │ │ ││││├┤ ││ ┬│ │├┬┘├─┤ │ ││ ││││└─┐ +# └─┘└─┘┘└┘└ ┴└─┘└─┘┴└─┴ ┴ ┴ ┴└─┘┘└┘└─┘ + +# uncomment your music players below. +# + +Control="MPD" +[ -n "$(pgrep spotify)" ] && Control="spotify" +# [ -n "$(pgrep brave-bin)" ] && Control="brave-bin" +# [ -n "$(pgrep rhythmbox)" ] && Control="rhythmbox" +# [ -n "$(pgrep audacious)" ] && Control="audacious" +# [ -n "$(pgrep clementine)" ] && Control="clementine" +# [ -n "$(pgrep strawberry)" ] && Control="strawberry" + + + +# saves covers here +Cover=/tmp/cover.png +# if cover not found in metadata use this instead +bkpCover=~/.config/eww/images/music.jpg +# mpd music directory +mpddir=~/Music + +# ┌─┐┬ ┌─┐┬ ┬┌─┐┬─┐┌─┐┌┬┐┬ ┌─┐┌─┐┬─┐┬┌─┐┌┬┐┌─┐ +# ├─┘│ ├─┤└┬┘├┤ ├┬┘│ │ │ └─┐│ ├┬┘│├─┘ │ └─┐ +# ┴ ┴─┘┴ ┴ ┴ └─┘┴└─└─┘ ┴ ┴─┘ └─┘└─┘┴└─┴┴ ┴ └─┘ + +########################## Title ########################## +title() { +title=$(playerctl --player=$Control metadata --format {{title}}) +[ -z "$title" ] && title="Play Something" +echo "$title" +} + +########################## Artist ########################## +artist() { +artist=$(playerctl --player=$Control metadata --format {{artist}}) +[ -z "$artist" ] && artist="Artist" +echo "$artist" +} + +########################## Album ########################## +album() { +album=$(playerctl --player=$Control metadata --format {{album}}) +[ -z "$album" ] && album="Album" +echo "$album" +} + +########################## Status ########################## +status() { +status=$(playerctl --player=$Control status) +[ -z "$status" ] && status="Stopped" +echo "$status" +} + +########################## Time ########################## +ctime() { +time=$(playerctl --player=$Control position --format "{{ duration(position) }}") +[ -z "$time" ] && time="0:00" +echo "$time" +} + +########################## Length ########################## +length() { +length=$(playerctl --player=$Control metadata --format "{{ duration(mpris:length) }}") +[ -z "$length" ] && length="0:00" +echo "$length" +} + +########################## trackNumber ########################## +playlist() { +playlist=$(playerctl --player=$Control metadata xesam:trackNumber) +[ -z "$playlist" ] && playlist="0" +echo "$playlist" +} + +########################## Cover ########################## +cover() { +albumart="$(playerctl --player=$Control metadata mpris:artUrl | sed -e 's/open.spotify.com/i.scdn.co/g')" +[ $(playerctl --player=$Control metadata mpris:artUrl) ] && curl -s "$albumart" --output $Cover || cp $bkpCover $Cover +echo "$Cover" +} + +########################## Statusicon ########################## +statusicon() { +icon="" +[ $(playerctl --player=$Control status) = "Playing" ] && icon="" +[ $(playerctl --player=$Control status) = "Paused" ] && icon="" +echo "$icon" +} + +########################## Lyrics ########################## +lyrics() { +Title=$(playerctl --player=$Control metadata --format {{title}}) +Artist=$(playerctl --player=$Control metadata --format {{artist}}) +URL="https://api.lyrics.ovh/v1/$Artist/$Title" +lyrics=$(curl -s "$( echo "$URL" | sed s/" "/%20/g | sed s/"&"/%26/g | sed s/,/%2C/g | sed s/-/%2D/g)" | jq '.lyrics' ) +[ "$lyrics" = "null" ] && lyrics=$( curl -s --get "https://makeitpersonal.co/lyrics" --data-urlencode "artist=$Artist" --data-urlencode "title=$Title") +printf "$lyrics" | less +} + +# ┌┬┐┌─┐┌┬┐ ┌─┐┌─┐┬─┐┬┌─┐┌┬┐┌─┐ +# │││├─┘ ││ └─┐│ ├┬┘│├─┘ │ └─┐ +# ┴ ┴┴ ─┴┘ └─┘└─┘┴└─┴┴ ┴ └─┘ + +########################## Title ########################## +mpctitle() { +title=$(mpc -f %title% current) +[ -z "$title" ] && title="Play Something" +echo "$title" +} + +########################## Artist ########################## +mpcartist() { +artist=$(mpc -f %artist% current) +[ -z "$artist" ] && artist="Artist" +echo "$artist" +} + +########################## Album ########################## +mpcalbum() { +album=$(mpc -f %album% current) +[ -z "$album" ] && album="Album" +echo "$album" +} + +########################## Cover ########################## +mpccover() { +ffmpeg -i "$mpddir"/"$(mpc current -f %file%)" -vf scale=500:500 "${Cover}" -y || cp $bkpCover $Cover +echo "$Cover" && exit +} + +## Cover for ncmpcpp ## +ncmpcppcover() { +ffmpeg -i "$mpddir"/"$(mpc current -f %file%)" -vf scale=500:500 "${Cover}" -y || cp $bkpCover $Cover +exit +} + +########################## Time ########################## +mpctime() { +time=$(mpc status %currenttime%) +[ -z "$time" ] && time="0:00" +echo "$time" +} + +########################## Length ########################## +mpclength() { +length=$(mpc status %totaltime%) +[ -z "$length" ] && length="0:00" +echo "$length" +} + +########################## Icon ########################## +mpcicon() { +status=$(mpc status | head -2 | tail -1 | cut -c2-7 ) +icon="" +[ "$status" = "playin" ] && icon="" +[ "$status" = "paused" ] && icon="" +echo "$icon" +} + +########################## Status ########################## +mpcstatus() { +stat=$(mpc status | head -2 | tail -1 | cut -c2-7 ) +status="Stopped" +[ "$stat" = "playin" ] && status="Playing" +[ "$stat" = "paused" ] && status="Paused" +echo "$status" +} + +########################## Percent ########################## +mpcperc() { +perc=$(mpc status | grep "%)" | awk '{ print $4 }' | awk -F% '{ print $1 }' | awk 'sub(/^.{1}/,"")') +[ -z "$perc" ] && perc="0" +echo "$perc" +} + +########################## Playlist ########################## +mpcsongpos() { +playlist=$(mpc status %songpos%/%length%) +[ -z "$playlist" ] && playlist="0/0" +echo "$playlist" +} + +########################## Lyrics ########################## +mpclyrics() { +Title=$(mpc -f %title% current) +Artist=$(mpc -f %artist% current) +URL="https://api.lyrics.ovh/v1/$Artist/$Title" +lyrics=$(curl -s "$( echo "$URL" | sed s/" "/%20/g | sed s/"&"/%26/g | sed s/,/%2C/g | sed s/-/%2D/g)" | jq '.lyrics' ) +[ "$lyrics" = "null" ] && lyrics=$( curl -s --get "https://makeitpersonal.co/lyrics" --data-urlencode "artist=$Artist" --data-urlencode "title=$Title") +printf "$lyrics" | less +} + +# ┬ ┬┌─┐┬ ┌─┐ +# ├─┤├┤ │ ├─┘ +# ┴ ┴└─┘┴─┘┴ + +doc() { +echo "Usage: + MediaControl [Options] + +Options: + --previous previous song + --next next song + --toggle toggle between play-pause song + --stop stop song + title shows title of current song + album shows album of current song + artist shows artist of current song + status music status (playing/paused/stopped) + statusicon music status icons (playing/paused/stopped) + coverloc saves cover and shows location to cover of current song + showcover opens cover using feh + time shows current time of song + length shows length of song + percent shows percent song + playlist shows playlist position of current song + lyrics shows lyrics" +} + +# ┌─┐┌─┐┌┬┐┬┌─┐┌┐┌┌─┐ +# │ │├─┘ │ ││ ││││└─┐ +# └─┘┴ ┴ ┴└─┘┘└┘└─┘ + +case $Control in + MPD) + case $1 in + --next) mpc -q next ;; + --previous) mpc -q prev ;; + --toggle) mpc -q toggle ;; + --stop) mpc -q stop ;; + title) mpctitle ;; + artist) mpcartist ;; + album) mpcalbum ;; + status) mpcstatus ;; + statusicon) mpcicon ;; + player) echo "$Control" ;; + coverloc) mpccover ;; + nccover) ncmpcppcover ;; + showcover) mpccover | xargs feh ;; + time) mpctime ;; + length) mpclength ;; + percent) mpcperc ;; + playlist) mpcsongpos ;; + lyrics) mpclyrics ;; + *) doc ;; + esac + ;; + *) + case $1 in + --next) playerctl --player=$Control next ;; + --previous) playerctl --player=$Control previous ;; + --toggle) playerctl --player=$Control play-pause ;; + --stop) playerctl --player=$Control stop ;; + title) title ;; + artist) artist ;; + album) album ;; + status) status ;; + statusicon) statusicon ;; + player) echo "$Control" ;; + coverloc) cover ;; + showcover) cover | xargs feh ;; + time) ctime ;; + length) length ;; + percent) echo "0" ;; + playlist) playlist ;; + lyrics) lyrics ;; + *) doc ;; + esac +esac 2>/dev/null diff --git a/widgets/MusicPlayer/eww.scss b/widgets/MusicPlayer/eww.scss new file mode 100644 index 0000000..bcf9eb5 --- /dev/null +++ b/widgets/MusicPlayer/eww.scss @@ -0,0 +1,69 @@ +* +{ + all: unset; +} + +/* Music player */ + +.music-player{ + background-color: #1f1d29; + padding: 0 1rem 1rem 1rem; + border-radius: 12px; + margin: .5rem; +} +.album_art { + background-size: cover; + background-position: center; + border-radius: 12px; + margin: 2rem 1rem 1.5rem 1rem; + padding: 7rem; +} + +.artist { + font-family: Cartograph CF; + font-style: italic; + font-weight: bold; + color: #FDFDFD; + font-size : 13px; +} +.song{ + font-family: Cartograph CF; + font-style: italic; + font-size: 15px; + color: #FDFDFD; + margin-top: -5px; +} +.btn_prev, .btn_play, .btn_next { + font-family: JetBrainsMono Nerd Font; +} +.btn_prev { + color: #FDFDFD; + font-size : 2.6em; +} +.btn_play { + color: #FDFDFD; + font-size : 2.6em; + font-weight : bold; +} +.btn_next { + color: #FDFDFD; + font-size : 2.6em; +} +.metric-player scale trough highlight { + all: unset; + border-radius: 10px; + background-color: #c3a5e6; +} + +.metric-player scale trough { + all: unset; + background-color: #1D1F28; + border:2px solid #454545; + border-radius: 50px; + } + +.metric-player scale trough { + min-width: 200px; + min-height: 10px; + margin: .5rem 1rem 0 1rem; +} diff --git a/widgets/MusicPlayer/eww.yuck b/widgets/MusicPlayer/eww.yuck new file mode 100644 index 0000000..f86e29b --- /dev/null +++ b/widgets/MusicPlayer/eww.yuck @@ -0,0 +1,56 @@ +;; Music Variables +(defpoll art :interval "0s" "~/.config/polybar/widgets/MusicPlayer/MediaControl coverloc") +(defpoll title :interval "0s" "~/.config/polybar/widgets/MusicPlayer/MediaControl title") +(defpoll artist :interval "0s" "~/.config/polybar/widgets/MusicPlayer/MediaControl artist") +(defpoll album :interval "0s" "~/.config/polybar/widgets/MusicPlayer/MediaControl album") +(defpoll playpause :interval "0s" "~/.config/polybar/widgets/MusicPlayer/MediaControl statusicon") +(defpoll songtime :interval "0s" "~/.config/polybar/widgets/MusicPlayer/MediaControl percent") + +;; Music Widgets +(defwidget music [] + (box :class "music-player" + :orientation "v" + :space-evenly "false" + :halign "center" + (box :class "album_art" + :style "background-image: url('${art}');") + (box :orientation "v" + :space-evenly "false" + :halign "center" + :spacing 10 + (label :class "artist" + :limit-width 15 + :text artist) + (label :class "song" + :limit-width 20 + :text title) + (box :orientation "h" + :space-evenly "false" + :halign "center" + :spacing 30 + (button :class "btn_prev" :onclick "~/.config/polybar/widgets/MusicPlayer/MediaControl --previous" "ﭣ") + (button :class "btn_play" :onclick "~/.config/polybar/widgets/MusicPlayer/MediaControl --toggle" playpause) + (button :class "btn_next" :onclick "~/.config/polybar/widgets/MusicPlayer/MediaControl --next" "ﭡ")) + (box :orientation "h" + :class "metric-player" + :space-evenly "false" + (scale :min 0 + :max 101 + :active "false" + :value {songtime != "" ? "${songtime}" : "0"} + ) + ) + ) + ) +) + + +;windows + +(defwindow music + :geometry (geometry :x "-470px" + :y "-45px" + :width "18%" + :height "30%" + :anchor "bottom right") + (music)) diff --git a/widgets/MusicPlayer/get-music-info b/widgets/MusicPlayer/get-music-info new file mode 100755 index 0000000..e4936ea --- /dev/null +++ b/widgets/MusicPlayer/get-music-info @@ -0,0 +1,43 @@ +#!/bin/bash + +# Get the metadata of the currently playing track +metadata=$(playerctl metadata) + +# Extract the required information from the metadata +artist=$(echo "$metadata" | grep "xesam:artist" | sed 's/xesam:artist: //') +title=$(echo "$metadata" | grep "xesam:title" | sed 's/xesam:title: //') +album=$(echo "$metadata" | grep "xesam:album" | sed 's/xesam:album: //') +art=$(echo "$metadata" | grep "xesam:artUrl" | sed 's/xesam:artUrl: //') +status=$(playerctl status) + +# Determine the playback status +if [ "$status" = "Playing" ]; then + playback_status="" +elif [ "$status" = "Paused" ]; then + playback_status="" +else + playback_status="" +fi + +# Calculate the progress of the current track +position=$(playerctl position) +duration=$(echo "$metadata" | grep "mpris:length" | sed 's/mpris:length: //') +progress_bar=$(echo "$position / $duration * 100" | bc -l | awk -F. '{print $1}') + +# Calculate the time elapsed and remaining time +time_elapsed=$(printf "%.0f:%.2d" $(echo "$position/60" | bc -l) $(echo "$position%60" | bc -l)) +time_remaining=$(printf "%.0f:%.2d" $(echo "($duration - $position)/60" | bc -l) $(echo "($duration - $position)%60" | bc -l)) + +# Output the music information in JSON format +cat <<-EOF +{ + "artist": "$artist", + "title": "$title", + "album": "$album", + "album_art": "$art", + "playback_status": "$playback_status", + "progress_bar": "$progress_bar", + "time_elapsed": "$time_elapsed", + "time_remaining": "$time_remaining" +} +EOF diff --git a/widgets/MusicPlayer/mplayer-launcher b/widgets/MusicPlayer/mplayer-launcher new file mode 100755 index 0000000..5d05489 --- /dev/null +++ b/widgets/MusicPlayer/mplayer-launcher @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +mplayer() { + LOCKFILE="$HOME/.cache/eww-cris-mplayer.lock" + +if [[ ! -f "$LOCKFILE" ]]; then + touch "$LOCKFILE" + eww open music -c ~/.config/polybar/widgets/MusicPlayer/ +elif [[ -f "$LOCKFILE" ]]; then + eww close music -c ~/.config/polybar/widgets/MusicPlayer/ + rm "$LOCKFILE" +fi +} + +mplayer diff --git a/widgets/actions/actions.scss b/widgets/actions/actions.scss new file mode 100644 index 0000000..9dd8d5d --- /dev/null +++ b/widgets/actions/actions.scss @@ -0,0 +1,153 @@ +.actions { + background-color: $bg; + color: $fg; + font-family: Product Sans; + padding: 10px 18px; +} + +.controlsbox { + background-color: $contrastbg; + border-radius: 7px; + padding: 7px 12px; + + .vol-slider { + .vol-label { + font-size: 20px; + font-family: Iosevka Nerd Font; + margin-right: 15px; + color: $blue; + } + & scale trough { + all: unset; + border-radius: 10px; + background-color: $black; + border: none; + min-height: 8px; + min-width: 250px; + margin-left: 3px; + & highlight { + all: unset; + background: linear-gradient(to right, $blue, $cyan); + color: #000000; + border-radius: 10px; + } + } + } + + .br-slider { + .br-label { + font-size: 20px; + font-family: Iosevka Nerd Font; + margin-right: 15px; + color: $yellow; + } + & scale trough { + all: unset; + border-radius: 10px; + background-color: $black; + border: none; + min-height: 8px; + min-width: 250px; + margin-left: 3px; + & highlight { + all: unset; + background: linear-gradient(to right, $red, $yellow); + color: #000000; + border-radius: 10px; + } + } + } +} + +.userinfobox { + .time { + font-size: 42px; + } + .pfp { + background-position: center; + background-size: cover; + background-repeat: no-repeat; + border-radius: 50px; + min-width: 30px; + min-height: 30px; + } + .daybox { + margin-top: 8px; + font-size: 14px; + } + .username { + font-family: Product Sans; + } + .useractions { + .logout { + background-color: $black; + font-family: Iosevka Nerd Font; + font-size: 18px; + padding: 2px 12px 2px 7px; + border-radius: 4px; + transition: all ease .2s; + &:hover { background-color: $dimblack; } + &:active { background-color: $black; } + } + } +} + +.sysbuttonsbox { + .block-container { + .block { + background-color: $contrastbg; + padding: 0px 6px; + border-radius: 50px; + transition: all ease .2s; + color: $fg; + &:hover { background-color: $bgSecondary; } + &:active { background-color: $contrastbg; } + &.active { + background-color: $black; + &:hover { background-color: $dimblack; } + &:active { background-color: $black; } + .sepbox .sep { + background-color: $lightblack; + } + } + .iconbox .icon { + font-family: Iosevka Nerd Font; + padding: 7px 17px 7px 8px; + } + .sepbox .sep { + background-color: $black; + margin: 0px 4px; + padding: 0px 1px; + } + .chevronbox { + font-family: Iosevka Nerd Font; + padding: 0px 12px; + } + } + .label { + font-family: Product Sans; + font-size: 14px; + } + } +} + +.poweractionsbox { + .info { + font-size: 14px; + } + .buttons { + font-family: Iosevka Nerd Font; + font-size: 16px; + button { + padding: 0px 9px 0px 5px; + background-color: $bgSecondary; + border-radius: 50px; + transition: all ease .2s; + &:hover { background-color: $black; } + &:active { background-color: $bg; } + &.do-poweroff { color: $red; } + &.do-reboot { color: $magenta; } + &.do-logout { color: $yellow; } + } + } +} diff --git a/widgets/actions/actions.yuck b/widgets/actions/actions.yuck new file mode 100644 index 0000000..1716e1f --- /dev/null +++ b/widgets/actions/actions.yuck @@ -0,0 +1,211 @@ +(defwidget sysblock [icon onclick active label] + (box :class "block-container" + :orientation "v" + :space-evenly false + :spacing 5 + (button :class "block ${active ? 'active' : ''}" :onclick "${onclick}" + (box :orientation "h" + :space-evenly false + (box :class "iconbox" + :halign "start" + :space-evenly true + :orientation "h" + (label :text "${icon}" :class "icon") + ) + (box :class "sepbox" + :halign "center" + :space-evenly true + :orientation "h" + (box :class "sep") + ) + (box :class "chevronbox" + :halign "end" + :space-evenly true + :orientation "h" + (label :text "") + ) + ) + ) + (label :halign "center" :text "${label}" :class "label") + ) +) + +(defwidget sysbuttons [] + (box :class "sysbuttonsbox" + :orientation "v" + :space-evenly false + :spacing 20 + (box :class "firstrow" + :orientation "h" + :space-evenly true + (box :halign "start" + :orientation "h" + :space-evenly true + (sysblock :icon "${network_icon}" + :onclick "~/.config/polybar/widgets/scripts/network.sh toggle" + :active "${network_status == 'Connected' ? true : false}" + :label "${network_ssid}") + ) + (box :halign "center" + :orientation "h" + :space-evenly true + (sysblock :icon "${bluetooth_state == 'on' ? '' : ''}" + :onclick "~/.config/polybar/widgets/scripts/bluetooth toggle" + :active "${bluetooth_state == 'on' ? true : false}" + :label "Bluetooth") + ) + (box :halign "end" + :orientation "h" + :space-evenly true + (sysblock :icon "${network_radio == 'on' ? '' : ''}" + :onclick "~/.config/polybar/widgets/scripts/network.sh toggle-radio" + :active "${network_radio == 'on' ? false : true}" + :label "Airplane") + ) + ) + (box :class "lastrow" + :orientation "h" + :space-evenly true + (box :halign "start" + :orientation "h" + :space-evenly true + (sysblock :icon "${redshift_state == 'on' ? '' : ''}" + :onclick "~/.config/polybar/widgets/scripts/redshift toggle" + :active "${redshift_state == 'on' ? true : false}" + :label "Night Light") + ) + (box :halign "center" + :orientation "h" + :space-evenly true + (sysblock :icon "${volume_icon}" + :onclick "~/.config/polybar/widgets/scripts/volume.sh toggle-muted" + :active "${is_muted == 'yes' ? false : true}" + :label "Volume") + ) + (box :halign "end" + :orientation "h" + :space-evenly true + (sysblock :icon "${mic_status == 'yes' ? '' : ''}" + :onclick "~/.config/polybar/widgets/scripts/mic toggle" + :active "${mic_status == 'yes' ? true : false}" + :label "MicroPhone") + ) + ) + ) +) + +(defwidget controls [] + (box :class "controlsbox" + :orientation "v" + :space-evenly false + :spacing 10 + (box :orientation "h" + :space-evenly "false" + :class "vol-slider" + (box :class "vol-label" + volume_icon + ) + (scale :value volume_value + :onchange "~/.config/polybar/widgets/scripts/volume.sh set {}" + :min 0 + :max 101) + ) + (box :orientation "h" + :space-evenly "false" + :class "br-slider" + (box :class "br-label" + br_icon + ) + (scale :value br + :onchange "~/.config/polybar/widgets/scripts/brightness.sh set {}" + :min 0 + :max 101) + ) + ) +) + +(defwidget user [] + (box :class "userinfobox" + :orientation "h" + :space-evenly false + (box :class "info" + :space-evenly false + :orientation "h" + :halign "start" + :valign "center" + :spacing 10 + (label :text "${lithour}:${litmin}" :class "time") + (box :class "daybox" + :orientation "v" + :space-evenly false + (label :text "${shortday}" :halign "start") + (label :text "${weekday}" :halign "start") + ) + ) + (box :orientation "h" + :space-evenly false + :halign "end" + :valign "center" + :hexpand true + (box :class "pfp" + :style "background-image: url('${pfp}');") + ) + ) +) + +(defwidget poweractions [] + (box :class "poweractionsbox" + :orientation "h" + :space-evenly true + (box :class "buttons-container" + :space-evenly true + :halign "end" + :orientation "h" + (box :class "buttons" + :orientation "h" + :space-evenly true + :spacing 10 + (button :class "do-poweroff" + :onclick "systemctl poweroff" + :tooltip "Shutdown Now" + :halign "start" + "襤" + ) + (button :class "do-reboot" + :onclick "systemctl reboot" + :tooltip "Reboot" + :halign "center" + "勒" + ) + (button :class "do-logout" + :onclick "bsp quit" + :tooltip "Logout" + :halign "end" + "" + ) + ) + ) + ) +) + +(defwidget actions [] + (box :class "actions" + :orientation "v" + :space-evenly false + :spacing 20 + (user) + (sysbuttons) + (controls) + (poweractions) + ) +) + +(defwindow actions + :geometry (geometry :x "-11px" + :y "48px" + :width "320px" ; this isn't respected btw, so it's auto-adjusted by eww lol. + :height "10px" ; automatically generated by eww. + :anchor "top right") + :type "dock" + :wm-ignore true +(actions)) diff --git a/widgets/colors.scss b/widgets/colors.scss new file mode 100644 index 0000000..eca60fa --- /dev/null +++ b/widgets/colors.scss @@ -0,0 +1,31 @@ +$darkbg: #0b0d16; +$bg: #0d0f18; +$contrastbg: #0f111a; +$bgSecondary: #11131c; +$bgSecondaryAlt: #a5b6cf; +$fg: #a5b6cf; +$fgDim: #a5b6cf; +$watermelon: #dd6777; + +// Aliases +$background: $bg; +$backgroundSecondary: $bgSecondary; +$backgroundSecondaryAlt: $bgSecondaryAlt; +$foreground: $fg; +$foregroundDim: $fgDim; + +$black: #151720; +$dimblack: #1a1c25; +$lightblack: #262831; +$red: #dd6777; +$blue: #86aaec; +$cyan: #93cee9; +$blue-desaturated: #93cee9; +$magenta: #c296eb; +$purple: #c296eb; +$green: #90ceaa; +$aquamarine: #90ceaa; +$yellow: #ecd3a0; +$accent: $blue; +$javacafeMagenta: #c296eb; +$javacafeBlue: #86aaec; diff --git a/widgets/control/assets/fallback.png b/widgets/control/assets/fallback.png new file mode 100644 index 0000000..9dd2c7f Binary files /dev/null and b/widgets/control/assets/fallback.png differ diff --git a/widgets/control/assets/notifications/airplane-disable.svg b/widgets/control/assets/notifications/airplane-disable.svg new file mode 100644 index 0000000..353ae5a --- /dev/null +++ b/widgets/control/assets/notifications/airplane-disable.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/widgets/control/assets/notifications/airplane-enable.svg b/widgets/control/assets/notifications/airplane-enable.svg new file mode 100644 index 0000000..092938a --- /dev/null +++ b/widgets/control/assets/notifications/airplane-enable.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/widgets/control/assets/notifications/bluetooth-disable.svg b/widgets/control/assets/notifications/bluetooth-disable.svg new file mode 100644 index 0000000..f46007e --- /dev/null +++ b/widgets/control/assets/notifications/bluetooth-disable.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/widgets/control/assets/notifications/bluetooth-enable.svg b/widgets/control/assets/notifications/bluetooth-enable.svg new file mode 100644 index 0000000..e7fb0de --- /dev/null +++ b/widgets/control/assets/notifications/bluetooth-enable.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/widgets/control/assets/notifications/dnd-disable.svg b/widgets/control/assets/notifications/dnd-disable.svg new file mode 100644 index 0000000..e8a673d --- /dev/null +++ b/widgets/control/assets/notifications/dnd-disable.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/widgets/control/assets/notifications/dnd-enable.svg b/widgets/control/assets/notifications/dnd-enable.svg new file mode 100644 index 0000000..41a9aa7 --- /dev/null +++ b/widgets/control/assets/notifications/dnd-enable.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/widgets/control/assets/notifications/nightlight-disable.svg b/widgets/control/assets/notifications/nightlight-disable.svg new file mode 100644 index 0000000..c985e42 --- /dev/null +++ b/widgets/control/assets/notifications/nightlight-disable.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/widgets/control/assets/notifications/nightlight-enable.svg b/widgets/control/assets/notifications/nightlight-enable.svg new file mode 100644 index 0000000..3e61ee6 --- /dev/null +++ b/widgets/control/assets/notifications/nightlight-enable.svg @@ -0,0 +1,39 @@ + + + + + + diff --git a/widgets/control/assets/notifications/notifications-off.svg b/widgets/control/assets/notifications/notifications-off.svg new file mode 100755 index 0000000..3817ac0 --- /dev/null +++ b/widgets/control/assets/notifications/notifications-off.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/widgets/control/assets/profile.png b/widgets/control/assets/profile.png new file mode 100644 index 0000000..ac8dc6c Binary files /dev/null and b/widgets/control/assets/profile.png differ diff --git a/widgets/control/control-panel b/widgets/control/control-panel new file mode 100755 index 0000000..322dd86 --- /dev/null +++ b/widgets/control/control-panel @@ -0,0 +1,29 @@ +#!/bin/bash + +[ ! -d $cache_dir ] && mkdir $cache_dir + +cache_dir="$HOME/.cache/eww" +cache="$cache_dir/control-center.eww" +config="$HOME/.config/polybar/widgets/control" +active_players=$(playerctl -l | head -n 1) + +run () { + eww -c $config open control-panel + eww -c $config update control-panel=true + [[ ! -z $active_players ]] && eww -c $config update music-panel=true +} + +if [[ ! `pidof eww` ]]; then + eww -c $config daemon + +else + if [ ! -f $cache ]; then + touch $cache + run + else + [[ ! -z $active_players ]] && eww -c $config update music-panel=false + eww -c $config update control-panel=false + eww -c $config close control-panel + rm $cache + fi +fi \ No newline at end of file diff --git a/widgets/control/eww.scss b/widgets/control/eww.scss new file mode 100644 index 0000000..7e07bca --- /dev/null +++ b/widgets/control/eww.scss @@ -0,0 +1,25 @@ +@import "src/scss/colors"; +@import "src/scss/style"; + +@import "src/scss/widgets/control-panel"; +@import "src/scss/widgets/info-panel"; +@import "src/scss/widgets/top-panel"; + +@import "src/scss/widgets/control-panel/controls"; +@import "src/scss/widgets/control-panel/music"; +@import "src/scss/widgets/control-panel/profile"; +@import "src/scss/widgets/control-panel/sliders"; + +@import "src/scss/widgets/info-panel/calendar"; +@import "src/scss/widgets/info-panel/date"; +@import "src/scss/widgets/info-panel/notification-stats"; +@import "src/scss/widgets/info-panel/weather"; + +@import "src/scss/widgets/notification-panel/notification-box"; +@import "src/scss/widgets/notification-panel/notification-card"; + +@import "src/scss/widgets/top-panel/actions"; +@import "src/scss/widgets/top-panel/date"; +@import "src/scss/widgets/top-panel/music"; +@import "src/scss/widgets/top-panel/sysmenu"; +@import "src/scss/widgets/top-panel/workspaces"; \ No newline at end of file diff --git a/widgets/control/eww.yuck b/widgets/control/eww.yuck new file mode 100644 index 0000000..76129f4 --- /dev/null +++ b/widgets/control/eww.yuck @@ -0,0 +1,7 @@ +(include "src/yuck/variables.yuck") +(include "src/yuck/windows.yuck") + +(include "src/yuck/widgets/control-panel.yuck") +(include "src/yuck/widgets/info-panel.yuck") +(include "src/yuck/widgets/notification-panel.yuck") +(include "src/yuck/widgets/top-panel.yuck") \ No newline at end of file diff --git a/widgets/control/info-panel b/widgets/control/info-panel new file mode 100755 index 0000000..bbf76c9 --- /dev/null +++ b/widgets/control/info-panel @@ -0,0 +1,28 @@ +#!/bin/bash + +[ ! -d $cache_dir ] && mkdir $cache_dir + +cache_dir="$HOME/.cache/eww" +cache="$cache_dir/info-center.eww" +config="$HOME/.config/polybar/widgets/control" + +run () { + eww -c $config open info-panel + sleep 0.6 + eww -c $config update info-panel=true +} + +if [[ ! `pidof eww` ]]; then + eww -c $config daemon + sleep 1 +else + if [ ! -f $cache ]; then + touch $cache + run + else + eww -c $config update info-panel=false + sleep 0.6 + eww -c $config close info-panel + rm $cache + fi +fi \ No newline at end of file diff --git a/widgets/control/scripts/controls b/widgets/control/scripts/controls new file mode 100755 index 0000000..0fe667a --- /dev/null +++ b/widgets/control/scripts/controls @@ -0,0 +1,83 @@ +#!/bin/bash + +cache="$HOME/.cache/eww" + +night_light="$cache/night-light.eww" +do_not_disturb="$cache/do-not-disturb.eww" +airplane_mode=$(nmcli radio all | awk 'FNR == 2 {print $2}') +icon_dir="$HOME/.config/polybar/widgets/control/assets/notifications" + +[ ! -d $cache ] && mkdir $cache + +night_light_toggle () { + if [ ! -f $night_light ]; then + touch $night_light + redshift -P -O 4500 & + notify-send -a "Night Light" -i $icon_dir/nightlight-enable.svg "Night Night" "Blue light filter is on" + else + rm $night_light + redshift -x & + notify-send -a "Night Light" -i $icon_dir/nightlight-disable.svg "Night Night" "Blue light filter is off" + fi +} + + +do_not_disturb_toggle () { + if [ ! -f "$do_not_disturb" ]; then + touch "$do_not_disturb" + dunstctl set-paused true + else + rm "$do_not_disturb" + dunstctl set-paused false & + notify-send -a "Notifications" -i $icon_dir/dnd-enable.svg "Do Not Disturb" "Notifications are Unpaused" -u Normal + fi +} + + +airplane_mode_toggle () { + if [ $airplane_mode = enabled ]; then + nmcli radio all off & + notify-send -a "Wireless" -i $icon_dir/airplane-disable.svg "Airplane Mode" "Radios are turn off" + else + nmcli radio all on & + notify-send -a "Wireless" -i $icon_dir/airplane-enable.svg "Airplane Mode" "Radios are back on" + fi +} + + +bluetooth_toggle () { + if rfkill list bluetooth | grep -q 'blocked: yes'; then + rfkill unblock bluetooth & + notify-send -a "Bluetooth" -i $icon_dir/bluetooth-disable.svg "Bluetooth" "Bluetooth is back on" + else + rfkill block bluetooth & + notify-send -a "Bluetooth" -i $icon_dir/bluetooth-enable.svg "Bluetooth" "Bluetooth is turn off" + fi +} + +case $1 in + nl-toggle) + night_light_toggle + ;; + dnd-toggle) + do_not_disturb_toggle + ;; + air-toggle) + airplane_mode_toggle + ;; + blue-toggle) + bluetooth_toggle + ;; + nl-check) + [[ ! -f "$night_light" ]] && echo "Off" || echo "On" + ;; + air-check) + [[ $airplane_mode = enabled ]] && echo "Off" || echo "On" + ;; + dnd-check) + [[ ! -f "$do_not_disturb" ]] && echo "Off" || echo "On" + ;; + blue-check) + rfkill list bluetooth | grep -q 'blocked: yes' && echo "Off" || echo "On" + ;; +esac \ No newline at end of file diff --git a/widgets/control/scripts/logger b/widgets/control/scripts/logger new file mode 100755 index 0000000..4a256d5 --- /dev/null +++ b/widgets/control/scripts/logger @@ -0,0 +1,130 @@ +#!/bin/bash + +cache_dir="$HOME/.cache/eww/notify-logs" +cache="$cache_dir/notifications.txt" + +[ ! -d $cache_dir ] && mkdir $cache_dir +[ ! -f $cache ] && touch $cache + +create_cache () { + case $DUNST_URGENCY in + LOW|NORMAL|CRITICAL) + urgency=$DUNST_URGENCY + ;; + *) + urgency="OTHER" + ;; + esac + + TIMESTAMP=$(date +%H:%M) + + [ "$DUNST_SUMMARY" = "" ] && summary="Summary unavailable." || summary="$DUNST_SUMMARY" + [ "$DUNST_BODY" = "" ] && body="Body unavailable." || body="$(printf "$DUNST_BODY" | perl -MHTML::Entities -pe 'decode_entities($_);')" + + # TODO: Getting and caching web notification icons (dunstctl history won't show the icon path :() + if [[ $DUNST_ICON_PATH == "" ]]; then + APP_NAME=$(echo $DUNST_APP_NAME | awk '{print tolower($0)}') + ICON_PATH=/usr/share/icons/Papirus/128x128/apps/$APP_NAME.svg + else + FIXED_ICON_PATH=$(echo ${DUNST_ICON_PATH} | sed 's/32x32/48x48/g') + ICON_PATH=$FIXED_ICON_PATH + fi + + # If you have a mpris player that send notification you can add one here just follow the layout + if [[ $DUNST_APP_NAME == "Spotify" ]]; then + ICON_PATH=$(./scripts/music cover-notification spotify) + fi + if [[ $DUNST_APP_NAME == "Music Player Daemon" ]]; then + if [[ $DUNST_BODY =~ "by" ]]; then + ICON_PATH=$(./scripts/music cover-notification mpd) + else + ICON_PATH=/usr/share/icons/Papirus/128x128/apps/mpd.svg + fi + fi + + if [[ $DUNST_APP_NAME == "Screenshot" ]]; then + echo '(notification-card-screenshot :class "notification-card $notification-card-screenshot" :U "LOW" :SL "'$DUNST_ID'" :summary "'$summary'" :image "'$ICON_PATH'" :application "'$DUNST_APP_NAME'" :timestamp "'$TIMESTAMP'")' \ + | cat - "$cache" \ + | sponge "$cache" + elif [[ $DUNST_APP_NAME == "Volume" ]]; then + echo 'Null' # This is still a work in progress, for now this will be ignored by the notification-panel (Also you can follow the layout if you want to add ignore notifications) + elif [[ $DUNST_APP_NAME == "Brightness" ]]; then + echo 'Null' + else + echo '(notification-card :class "notification-card notification-card-'$DUNST_APP_NAME'" :U "'$DUNST_URGENCY'" :SL "'$DUNST_ID'" :body "'$body'" :summary "'$summary'" :image "'$ICON_PATH'" :application "'$DUNST_APP_NAME'" :timestamp "'$TIMESTAMP'")' \ + | cat - "$cache" \ + | sponge "$cache" + fi +} + +make_literal () { + caches="$(tr '\n' ' ' < "$cache")" + + if [[ "$caches" == "" ]]; then + echo '(box :class "notifications-empty-box" :height 550 :orientation "v" :space-evenly "false" (image :class "notifications-empty-banner" :valign "end" :vexpand true :path "assets/notifications/notifications-off.svg" :image-width 300 :image-height 200) (label :vexpand true :valign "start" :wrap true :class "notifications-empty-label" :text "No Notifications"))' + else + echo "(scroll :height 550 :vscroll true (box :orientation 'v' :class 'notification-scroll-box' :spacing 10 :space-evenly 'false' $caches))" + fi +} + +clear_logs () { + killall dunst 2>/dev/null + $HOME/.config/polybar/widgets/control/scripts/notification & disown + echo > "$cache" + rm -rf $HOME/.cache/eww/music/notification-cover/*.png +} + +subscribe () { + make_literal + + lines=$(cat $cache | wc -l) + + while sleep 0.1; do + + new=$(cat $cache | wc -l) + + [[ $lines -ne $new ]] && lines=$new && echo + done | read_literal +} + +read_literal () { + while read -r _; do + make_literal + done +} + +remove_line () { + sed -i '/SL "'$1'"/d' "$cache" +} + +case $1 in + pop) + sed -i '1d' "$cache" + ;; + drop) + sed -i '$d' "$cache" + ;; + remove) + remove_line $2 + ;; + clear) + clear_logs + ;; + subscribe) + subscribe + ;; + criticals) + printf '%03d\n' $(cat $cache | grep CRITICAL | wc --lines) + ;; + lows) + printf '%03d\n' $(cat $cache | grep LOW | wc --lines) + ;; + normals) + printf '%03d\n' $(cat $cache | grep NORMAL | wc --lines) + ;; + *) + create_cache + ;; +esac + +sed -i '/^$/d' "$cache" diff --git a/widgets/control/scripts/music b/widgets/control/scripts/music new file mode 100755 index 0000000..080d956 --- /dev/null +++ b/widgets/control/scripts/music @@ -0,0 +1,107 @@ +#!/bin/bash + +players="spotify,%any,firefox,chromium,brave,mpd" +player_status=$(playerctl -p $players status) +artist=$(playerctl -p $players metadata --format '{{ artist }}') +title=$(playerctl -p $players metadata --format '{{ title }}') +albumart="$(playerctl -p $players metadata mpris:artUrl | sed -e 's/open.spotify.com/i.scdn.co/g')" +cache="$HOME/.cache/eww/music" + +[ ! -d $cache ] && mkdir $cache + +status_icon () { + if [ "$player_status" = "Playing" ]; then + echo "󰏥" + elif [ "$player_status" = "Paused" ]; then + echo "󰐌" + else + echo "󰐌" + fi +} + +status_value () { + player_name=$(playerctl -p $players -l | head -n 1) + + [ "$player_status" = "Playing" ] && echo "Now Playing - via ${player_name^}" || echo "Music" +} + +title () { + [ -z "$title" ] && echo "Nothing Playing" || echo $title +} + +artist () { + if [ "$title" = "Advertisement" ]; then + echo "Spotify Free" + else + if [ -z "$artist" ]; then + echo "Unknown Artist"; + else + echo "by $artist" + fi + fi +} + +cover_notification () { + cover_dir="$cache/notification-cover" + [ ! -d $cover_dir ] && mkdir $cover_dir + cover="$cover_dir/$RANDOM-cover.png" + fallback="$HOME/.config/eww/images/fallback.png" + + players_pass=$1 + + if [[ $players_pass == "mpd" ]]; then + albumart="$(playerctl -p mpd metadata mpris:artUrl)" + else + albumart="$(playerctl -p $players_pass metadata mpris:artUrl | sed -e 's/open.spotify.com/i.scdn.co/g')" + fi + + [ $(playerctl -p $players_pass metadata mpris:artUrl) ] && curl -s "$albumart" --output $cover || cp $fallback $cover + + echo $cover | tail -n 1 +} + +cover () { + cover="$cache/cover.png" + fallback="$HOME/.config/eww/images/fallback.png" + + [ $(playerctl -p $players metadata mpris:artUrl) ] && curl -s "$albumart" --output $cover || cp $fallback $cover + echo $cover +} + +position () { + position=$(playerctl -p $players position | sed 's/..\{6\}$//') + duration=$(playerctl -p $players metadata mpris:length | sed 's/.\{6\}$//') + + if [[ $position -gt 0 ]]; then + printf "%0d:%02d" $((position%3600/60)) $((position%60)) + printf " / " + printf "%0d:%02d" $((duration%3600/60)) $((duration%60)) + fi +} + +case $1 in + status) + status_value + ;; + status-icon) + status_icon + ;; + title) + title + ;; + artist) + artist + ;; + title-artist) + echo $(title) - $(artist) + ;; + cover) + cover + ;; + cover-notification) + cover_notification $2 + ;; + position) + position || echo "--:-- / --:-- " + ;; +esac diff --git a/widgets/control/scripts/notification b/widgets/control/scripts/notification new file mode 100755 index 0000000..a18b6ae --- /dev/null +++ b/widgets/control/scripts/notification @@ -0,0 +1,7 @@ +#!/bin/bash + +if [[ `pidof dunst` ]]; then + pkill dunst +fi + +dunst -config $HOME/.config/dunst/dunstrc \ No newline at end of file diff --git a/widgets/control/scripts/scroll b/widgets/control/scripts/scroll new file mode 100755 index 0000000..77c8fa1 --- /dev/null +++ b/widgets/control/scripts/scroll @@ -0,0 +1,45 @@ +#!/bin/bash + +players="spotify,%any,firefox,chromium,brave,mpd" +player_status=$(playerctl -p $players status) +script_dir=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) +exit=$? + +scroll () { + zscroll -l 20 \ + --delay 1 \ + --scroll-padding " | " \ + --match-command "$script_dir/scroll scroll-music" \ + --match-text "Playing" "--scroll 1" \ + --match-text "Paused" "--scroll 0" \ + --update-check true "$script_dir/scroll" & + wait +} + + +if [ $exit -eq 0 ]; then + status=$player_status +else + status="Offline" +fi + +if [ "$1" == "scroll-music" ]; then + echo $status +else + if [ $status = "Stopped" ]; then + echo "Offline" + elif [ $status = "Paused" ]; then + playerctl -p $players metadata --format '{{ artist }} - {{ title }}' + elif [ $status = "Offline" ]; then + echo $status + else + playerctl -p $players metadata --format '{{ artist }} - {{ title }}' + fi +fi + + +case $1 in + scroll-toggle) + scroll + ;; +esac diff --git a/widgets/control/scripts/system b/widgets/control/scripts/system new file mode 100755 index 0000000..3522e35 --- /dev/null +++ b/widgets/control/scripts/system @@ -0,0 +1,219 @@ +#!/bin/bash + +brightness=$(brightnessctl i | grep -oP '\(\K[^%\)]+') +volume=$(pamixer --get-volume) +empty_battery="1" + +volume_icon () { + if [ $volume -lt "35" ]; then + icon="󰖀" + elif [ $volume -lt "70" ]; then + icon="󰕾" + else + icon="󰕾" + fi + + [ $(pamixer --get-mute) = "true" ] && icon="󰝟" + + echo $icon +} + +brightness_icon () { + if [ $brightness -lt "15" ]; then + echo "󰃞" + elif [ $brightness -lt "30" ]; then + echo "󰃞" + elif [ $brightness -lt "45" ]; then + echo "󰃟" + elif [ $brightness -lt "60" ]; then + echo "󰃟" + elif [ $brightness -lt "75" ]; then + echo "󰃟" + elif [ $brightness -lt "90" ]; then + echo "󰃠" + else + echo "󰃠" + fi +} + +network_icon () { + wifi_check=$(cat /sys/class/net/w*/operstate) + ethernet_check=$(cat /sys/class/net/e*/operstate) + wifi_strength=$(nmcli dev wifi list | awk '/\*/{if (NR!=1) {print $9}}') + + + if [ $wifi_check = up ]; then + if [ $wifi_strength -lt "10" ]; then + echo "" + elif [ $wifi_strength -lt "25" ]; then + echo "" + elif [ $wifi_strength -lt "45" ]; then + echo "" + elif [ $wifi_strength -lt "65" ]; then + echo "" + elif [ $wifi_strength -lt "85" ]; then + echo "" + else + echo "" + fi + elif [ $ethernet_check = up ]; then + echo "" + else + echo "" + fi +} + +network_speed () { + interface="$(ip link | awk '/state UP/ {print $2}' | tr -d :)" + + received_1=$(cat /sys/class/net/$interface/statistics/rx_bytes) + transmitted_1=$(cat /sys/class/net/$interface/statistics/tx_bytes) + + received_2=$(cat /sys/class/net/$interface/statistics/rx_bytes) + transmitted_2=$(cat /sys/class/net/$interface/statistics/tx_bytes) + + bandwidth_up=`expr $transmitted_2 - $transmitted_1` + bandwidth_down=`expr $received_2 - $received_1` + + upspeed=$(expr $bandwidth_up / 1024) + downspeed=$(expr $bandwidth_down / 1024) + + echo "$downspeed kB/s $upspeed kB/s" +} + +network_name () { + ssid=$(iwgetid -r) + + [ -z $ssid ] && echo "Not Connected" || echo "Connected to $ssid" +} + + +battery_empty () { + if [ "$empty_battery" -gt 0 ]; then + if [ ! -f "$HOME/.cache/empty_battery_capacity" ]; then + echo $(($RANDOM % 100)) > "$HOME/.cache/empty_battery_capacity" + fi + fi +} + +battery_icon () { + if [[ -d /sys/class/power_supply/ACAD ]]; then + echo "" + elif [[ -d /sys/class/power_supply/BAT ]]; then + if [[ $(cat /sys/class/power_supply/BAT/status) == "Discharging" ]]; then + echo "" + else + echo "" + fi + else + echo "" + fi +} + +battery_capacity () { + if [ "$empty_battery" -gt 0 ]; then + if [ -f "$HOME/.cache/empty_battery_capacity" ]; then + echo $(cat "$HOME/.cache/empty_battery_capacity") + fi + elif [ -f /sys/class/power_supply/BAT0/capacity ]; then + echo $(cat /sys/class/power_supply/BAT0/capacity) + fi +} + +cpu_value () { + preview_total=0 + preview_idle=0 + cache="/tmp/cpu_value.cache.eww" + + + if [[ -f "${cache}" ]]; then + file=$(cat "${cache}") + preview_total=$(echo "${file}" | head -n 1) + preview_idle=$(echo "${file}" | tail -n 1) + fi + + cpu=(`cat /proc/stat | grep '^cpu '`) + unset cpu[0] + idle=${cpu[4]} + + total=0 + + for value in "${cpu[@]:0:4}"; do + let "total=$total+$value" + done + + if [[ "${preview_total}" != "" ]] && [[ "${preview_idle}" != "" ]]; then + let "diff_idle=$idle-$preview_idle" + let "diff_total=$total-$preview_total" + let "diff_usage=(1000*($diff_total-$diff_idle)/$diff_total+5)/10" + echo "${diff_usage}" + else + echo "0" + fi + + echo "${total}" > "${cache}" + echo "${idle}" >> "${cache}" +} + +temperature_value () { + IFS=')' read -ra core_temp_arr <<< $(sensors | grep '^Core\s[[:digit:]]\+:') + + total_cpu_temp=0 + index=0 + + for i in "${core_temp_arr[@]}"; do : + temp=$(echo $i | sed -n 's/°C.*//; s/.*[+-]//; p; q') + let index++ + total_cpu_temp=$(echo "$total_cpu_temp + $temp" | bc) + done + + avg_cpu_temp=$(echo "scale=2; $total_cpu_temp / $index" | bc) + + temp_status="$avg_cpu_temp" + echo $temp_status +} + +case $1 in + volume-icon) + volume_icon + ;; + brightness-icon) + brightness_icon + ;; + volume-value) + echo $volume + ;; + brightness-value) + echo $brightness + ;; + network-icon) + network_icon + ;; + network-speed) + network_speed + ;; + network-name) + network_name + ;; + empty-battery) + battery_empty + ;; + battery-icon) + battery_icon + ;; + battery-capacity) + battery_capacity + ;; + cpu-value) + cpu_value + ;; + memory-value) + printf "%.0f\n" $(free -m | grep Mem | awk '{print ($3/$2)*100}') + ;; + temperature-value) + temperature_value + ;; + disk-value) + df -h / | awk '{print $5}' | tail -n1 | sed 's/%//g' + ;; +esac diff --git a/widgets/control/scripts/weather b/widgets/control/scripts/weather new file mode 100755 index 0000000..fc57a4b --- /dev/null +++ b/widgets/control/scripts/weather @@ -0,0 +1,164 @@ +#!/bin/bash + +cache="$HOME/.cache/eww/weather" +cache_status="$cache/status" +cache_temperature="$cache/temperature" +cache_gradient_1="$cache/gradient_1" +cache_gradient_2="$cache/gradient_2" +cache_icon="$cache/icon" +cache_city="$cache/city" +cache_country="$cache/country" + +[ ! -d $cache ] && mkdir $cache + +key="1f6e449790b8cf73a3cab8d5715483f8" +id="6941099" +unit="metric" # Available options : 'metric' or 'imperial' + +data () { + weather=`curl -sf "http://api.openweathermap.org/data/2.5/weather?APPID=$key&id=$id&units=$unit"` + + if [ ! -z "$weather" ]; then + temperature=$(echo "$weather" | jq ".main.temp" | cut -d "." -f 1) + city=$(echo "$weather" | jq -r ".name" | head -1) + country=$(echo "$weather" | jq -r ".sys.country" | head -1) + icon_code=$(echo "$weather" | jq -r ".weather[].icon" | head -1) + description=$(echo "$weather" | jq -r ".weather[].description" | head -1 | sed -e "s/\b\(.\)/\u\1/g") + + if [ "$icon_code" == "50d" ]; then + icon="󰖐" + gradient_1="#ece3b1" + gradient_2="#c9d4ff" + elif [ "$icon_code" == "50n" ]; then + icon="󰖐" + gradient_1="#ceace8" + gradient_2="#c9d4ff" + elif [ "$icon_code" == "01d" ]; then + icon="󰖙" + gradient_1="#ece3b1" + gradient_2="#ece3b1" + elif [ "$icon_code" == "01n" ]; then + icon="" + gradient_1="#ceace8" + gradient_2="#ceace8" + elif [ "$icon_code" == "02d" ]; then + icon="" + gradient_1="#ece3b1" + gradient_2="#a3cbe7" + elif [ "$icon_code" == "02n" ]; then + icon="" + gradient_1="#ceace8" + gradient_2="#a3cbe7" + elif [ "$icon_code" == "03d" ]; then + icon="" + gradient_1="#ece3b1" + gradient_2="#a3cbe7" + elif [ "$icon_code" == "03n" ]; then + icon="" + gradient_1="#ceace8" + gradient_2="#a3cbe7" + elif [ "$icon_code" == "04d" ]; then + icon="" + gradient_1="#a3cbe7" + gradient_2="#ece3b1" + elif [ "$icon_code" == "04n" ]; then + icon="" + gradient_1="#a3cbe7" + gradient_2="#ceace8" + elif [ "$icon_code" == "09d" ]; then + icon="" + gradient_1="#b4e3ad" + gradient_2="#ece3b1" + elif [ "$icon_code" == "09n" ]; then + icon="" + gradient_1="#b4e3ad" + gradient_2="#ceace8" + elif [ "$icon_code" == "10d" ]; then + icon="" + gradient_1="#ffdede" + gradient_2="#ece3b1" + elif [ "$icon_code" == "10n" ]; then + icon="" + gradient_1="#ffdede" + gradient_2="#ceace8" + elif [ "$icon_code" == "11d" ]; then + icon="" + gradient_1="#e5a3a1" + gradient_2="#ece3b1" + elif [ "$icon_code" == "11n" ]; then + icon="" + gradient_1="#e5a3a1" + gradient_2="#ceace8" + elif [ "$icon_code" == "13d" ]; then + icon="" + gradient_1="#c9d4ff" + gradient_2="#c9d4ff" + elif [ "$icon_code" == "13n" ]; then + icon="" + gradient_1="#c9d4ff" + gradient_2="#c9d4ff" + elif [ "$icon_code" == "40d" ]; then + icon="煮" + gradient_1="#a3cbe7" + gradient_2="#a3cbe7" + elif [ "$icon_code" == "40n" ]; then + icon="煮" + gradient_1="#a3cbe7" + gradient_2="#a3cbe7" + else + icon="" + gradient_1="#b4e3ad" + gradient_2="#a3cbe7" + fi + + echo "$icon" > $cache_icon + echo "$city" > $cache_city + echo "$country" > $cache_country + echo "$description" > $cache_status + echo "$temperature""°C" > $cache_temperature + echo "$gradient_1" > $cache_gradient_1 + echo "$gradient_2" > $cache_gradient_2 + else + echo "-" > $cache_city + echo "-" > $cache_country + echo "Weather Unavailable" > $cache_status + echo "" > $cache_icon + echo "-" > $cache_temperature + echo "#b4e3ad" > $cache_gradient_1 + echo "#a3cbe7" > $cache_gradient_2 + fi +} + +case $1 in + data) + data + ;; + icon) + data & + cat $cache_icon + ;; + color-1) + data & + cat $cache_gradient_1 + ;; + color-2) + data & + cat $cache_gradient_2 + ;; + city) + data & + cat $cache_city + ;; + country) + data & + cat $cache_country + ;; + temperature) + data & + cat $cache_temperature + ;; + status) + data & + cat $cache_status + ;; +esac \ No newline at end of file diff --git a/widgets/control/scripts/workspaces b/widgets/control/scripts/workspaces new file mode 100755 index 0000000..2079a59 --- /dev/null +++ b/widgets/control/scripts/workspaces @@ -0,0 +1,37 @@ +#!/bin/bash + +nicknames=(一 二 三 四 五 六 七 八 九 十) +start=0 + +workspaces () { + wmctrl -d | awk '{ print $1 " " $2 " " $9 }' | grep -v NSP | while read -r id active name; do + + name="${name#*_}" + + if [ "$active" == '*' ]; then + button_status="active" + else + button_status="inactive" + fi + + if wmctrl -l | grep --regexp '.*\s\+'"$id"'\s\+.*' >/dev/null; then + button_fill="occupied" + button_name="${nicknames[$start]}" + else + button_fill="empty" + button_name="${nicknames[$start]}" + fi + + load+="(button :class '$button_status-$button_fill' :onclick 'wmctrl -s $id' '$button_name')" + + echo -n $load + load="" + ((start=start+1)) + done +} + +xprop -spy -root _NET_CURRENT_DESKTOP | while read -r; do + echo '(box :orientation "h" :class "workspaces" :space-evenly true :halign "start" :vexpand true '"$(workspaces)"')' +done + +# スパゲッティはもうありませんが、出力は長くなります :( \ No newline at end of file diff --git a/widgets/control/src/scss/colors.scss b/widgets/control/src/scss/colors.scss new file mode 100644 index 0000000..65e0379 --- /dev/null +++ b/widgets/control/src/scss/colors.scss @@ -0,0 +1,12 @@ +$black: #1b1f23; +$black-darker: #161a1e; +$black-lighter: #22262a; +$white: #f8f9fa; + +$pink: #ffdede; +$red: #e5a3a1; +$green: #b4e3ad; +$yellow: #ece3b1; +$purple: #ceace8; +$blue: #a3cbe7; +$cyan: #c9d4ff; \ No newline at end of file diff --git a/widgets/control/src/scss/style.scss b/widgets/control/src/scss/style.scss new file mode 100644 index 0000000..8088ad5 --- /dev/null +++ b/widgets/control/src/scss/style.scss @@ -0,0 +1,16 @@ +* { + all: unset; +} + +window { + color: $white; + font-family: "Roboto", "Sarasa Mono CL", "Material Icons Round"; + font-weight: bold; +} + +tooltip { + background-color: $black; + color: $pink; + font-family: "Roboto", "Sarasa Mono CL", "Material Icons Round"; + font-weight: bold; +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/control-panel.scss b/widgets/control/src/scss/widgets/control-panel.scss new file mode 100644 index 0000000..cf110eb --- /dev/null +++ b/widgets/control/src/scss/widgets/control-panel.scss @@ -0,0 +1,12 @@ +.control-panel-window { + .control-panel-spacer { + transition: all 0.8s cubic-bezier(0.455, 0.03, 0.515, 0.955); + } + + .control-panel { + border: 2px $pink solid; + background-color: $black; + padding: 10px; + border-radius: 20px; + } +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/control-panel/controls.scss b/widgets/control/src/scss/widgets/control-panel/controls.scss new file mode 100644 index 0000000..5d923c6 --- /dev/null +++ b/widgets/control/src/scss/widgets/control-panel/controls.scss @@ -0,0 +1,27 @@ +.control-panel { + .control-box { + background-color: $black-darker; + border-radius: 10px; + margin: 1em 0 1em; + padding: 10px; + box-shadow: 0 0 10px rgba(0, 0, 0, 1); + + .controls { + button { + background-color: $black-lighter; + font-family: Material Design Icons Desktop; + font-size: 1.5em; + font-weight: bold; + border-radius: 20px; + margin: 0.5em; + padding: 20px; + box-shadow: 0 0 10px rgba(0, 0, 0, 1); + transition: 0.3s; + + &:hover { + border-radius: 100%; + } + } + } + } +} diff --git a/widgets/control/src/scss/widgets/control-panel/music.scss b/widgets/control/src/scss/widgets/control-panel/music.scss new file mode 100644 index 0000000..8896ab4 --- /dev/null +++ b/widgets/control/src/scss/widgets/control-panel/music.scss @@ -0,0 +1,72 @@ +.control-panel-window { + .music-box { + margin-top: 1em; + + .song-cover { + background-size: cover; + background-repeat: no-repeat; + background-position: center; + border-radius: 20px; + transition: 1s; + } + + .song { + border-radius: 20px; + background: linear-gradient( + to right, + rgba(16, 20, 21, 0.8), + rgba(16, 20, 21, 0.1) + ); + + padding-top: 10px; + padding-left: 1em; + + .now-playing { + margin-bottom: 6px; + color: $pink; + } + .song-title { + font-size: 16px; + font-weight: bolder; + color: $blue; + } + .song-artist, + .song-position { + font-weight: bold; + color: $pink; + } + + .song-controls { + button { + font-family: Material Design Icons Desktop; + font-size: 2.5em; + font-weight: bold; + margin: 0 0.4em 0.2em; + padding: 5px; + transition: 0.5s; + text-shadow: 5px 5px 5px rgba(0, 0, 0, 1); + + &:hover { + color: $blue; + background-color: $black-darker; + border-radius: 10px; + box-shadow: 0 0 10px rgba(0, 0, 0, 1); + } + } + + .music-play { + color: $pink; + text-shadow: 5px 5px 5px rgba(0, 0, 0, 1); + } + + .music-controls { + color: $blue; + + &:hover { + color: $pink; + } + } + } + } + } +} diff --git a/widgets/control/src/scss/widgets/control-panel/profile.scss b/widgets/control/src/scss/widgets/control-panel/profile.scss new file mode 100644 index 0000000..187b02c --- /dev/null +++ b/widgets/control/src/scss/widgets/control-panel/profile.scss @@ -0,0 +1,23 @@ +.control-panel { + .profile-box { + margin: 1em 1em 0; + + .profile-picture { + background-image: url("assets/profile.png"); + background-repeat: no-repeat; + background-size: 50px; + background-position: center; + min-height: 50px; + min-width: 50px; + border: 2px solid $pink; + border-radius: 100%; + } + + .profile-info { + margin: 1em; + color: $blue; + font-size: 15px; + + } + } +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/control-panel/sliders.scss b/widgets/control/src/scss/widgets/control-panel/sliders.scss new file mode 100644 index 0000000..41a316e --- /dev/null +++ b/widgets/control/src/scss/widgets/control-panel/sliders.scss @@ -0,0 +1,44 @@ +.control-panel { + .slider { + margin: .5em 1em 1em; + } + + scale trough { + all: unset; + background-color: $black-lighter; + border-radius: 20px; + min-height: 20px; + min-width: 250px; + box-shadow: 0 0 10px rgba(0, 0, 0, 1); + + highlight { + margin: .35em; + border-radius: inherit; + } + } + + .volume-bar trough highlight { + background-color: $green; + border-radius: 5px; + } + + .brightness-bar trough highlight { + background-color: $yellow; + border-radius: 5px; + } + + .volume-icon, + .brightness-icon { + font-family: Material Design Icons Desktop; + font-size: 2em; + margin-right: .5em; + } + + .volume-icon { + color: $green; + } + + .brightness-icon { + color: $yellow; + } +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/info-panel.scss b/widgets/control/src/scss/widgets/info-panel.scss new file mode 100644 index 0000000..2e9b870 --- /dev/null +++ b/widgets/control/src/scss/widgets/info-panel.scss @@ -0,0 +1,12 @@ +.info-panel-window { + .info-panel-spacer { + transition: all 0.8s cubic-bezier(0.455, 0.03, 0.515, 1); + } + + .info-panel { + border: 2px $pink solid; + background-color: $black; + padding: 10px; + border-radius: 20px; + } +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/info-panel/calendar.scss b/widgets/control/src/scss/widgets/info-panel/calendar.scss new file mode 100644 index 0000000..ec55309 --- /dev/null +++ b/widgets/control/src/scss/widgets/info-panel/calendar.scss @@ -0,0 +1,34 @@ +.info-panel { + .cal-box { + background-color: $black-darker; + border-radius: 10px; + padding: 1.5em; + margin: 1em 0em .5em; + box-shadow: 0 0 10px rgba(0, 0, 0, 1); + + .cal { + padding: 1em .5em 0em; + color: $pink; + + calendar:selected { + color: $green; + } + + calendar.header { + color: $pink; + } + + calendar.button { + color: $blue; + } + + calendar.highlight { + color: $blue; + } + + calendar:indeterminate { + color: $black-lighter; + } + } + } +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/info-panel/date.scss b/widgets/control/src/scss/widgets/info-panel/date.scss new file mode 100644 index 0000000..e48ae08 --- /dev/null +++ b/widgets/control/src/scss/widgets/info-panel/date.scss @@ -0,0 +1,26 @@ +.info-panel { + .date-box { + background-color: $black-darker; + padding: 1em; + border-radius: 10px; + margin: .5em .5em 0em; + box-shadow: 0 0 10px rgba(0, 0, 0, 1); + + .time-circle { + color: $green; + } + + .time-circle-text { + padding: 2em; + color: $green; + font-weight: bold; + + .time { + font-size: 2.5em; + } + .date { + font-size: 1em; + } + } + } +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/info-panel/notification-stats.scss b/widgets/control/src/scss/widgets/info-panel/notification-stats.scss new file mode 100644 index 0000000..92c8b7a --- /dev/null +++ b/widgets/control/src/scss/widgets/info-panel/notification-stats.scss @@ -0,0 +1,64 @@ +.info-panel { + .stat-box { + background-color: $black-darker; + border-radius: 10px; + margin: .5em; + box-shadow: 0 0 10px rgba(0, 0, 0, 1); + + .stats { + box-shadow: 0 0 10px rgba(0, 0, 0, 1); + border-radius: 10%; + padding: 5px; + margin: 1em 0em; + + .low-circle, + .normal-circle, + .critical-circle { + background-color: $black-darker; + } + + .low-circle { + color: $blue; + } + .normal-circle { + color: $pink; + } + .critical-circle { + color: $red; + } + + .low-circle-text, + .normal-circle-text, + .critical-circle-text { + padding: 1.5em; + font-size: 1.5em; + } + + .low-circle-text { + color: $blue; + } + .normal-circle-text { + color: $pink; + } + .critical-circle-text { + color: $red; + } + + .low-text, + .normal-text, + .critical-text { + margin-top: .5em; + } + + .low-text { + color: $blue; + } + .normal-text { + color: $pink; + } + .critical-text { + color: $red; + } + } + } +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/info-panel/weather.scss b/widgets/control/src/scss/widgets/info-panel/weather.scss new file mode 100644 index 0000000..e78bdde --- /dev/null +++ b/widgets/control/src/scss/widgets/info-panel/weather.scss @@ -0,0 +1,21 @@ +.info-panel { + .weather-box { + border-radius: 10px; + padding: 20px 30px 0px 20px; + margin: .5em .5em 0em; + min-width: 100px; + box-shadow: 0 0 10px rgba(0, 0, 0, 1); + + .weather-info { + color: $black; + } + + .weather-icon { + color: $black; + font-family: 'JetBrainsMono Nerd Font'; + font-weight: bold; + padding-right: 15px; + font-size: 4.5em; + } + } +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/notification-panel/notification-box.scss b/widgets/control/src/scss/widgets/notification-panel/notification-box.scss new file mode 100644 index 0000000..482c819 --- /dev/null +++ b/widgets/control/src/scss/widgets/notification-panel/notification-box.scss @@ -0,0 +1,40 @@ +.notification-panel-window { + .notification-panel-spacer { + transition: all 0.8s cubic-bezier(0.455, 0.03, 0.515, 1); + } + + .notification-box-label { + font-size: 2em; + margin: .5em 1em; + color: $blue; + } + + .notification-box-clear { + background-color: $red; + margin: 0em 1em; + color: $black; + border-radius: 10px; + padding: 10px; + font-size: 1em; + box-shadow: 0px 0px 10px rgba(0, 0, 0, 1); + transition: 0.5s; + + &:hover { + border-radius: 20px; + background-color: $black-darker; + color: $blue; + } + } + + .notifications-empty-label { + color: $red; + padding-top: 10px; + font-size: 2em; + padding-left: 80px; + padding-right: 80px; + } + + .notification-scroll-box { + margin: 0px 5px 0px 5px; + } +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/notification-panel/notification-card.scss b/widgets/control/src/scss/widgets/notification-panel/notification-card.scss new file mode 100644 index 0000000..94a847a --- /dev/null +++ b/widgets/control/src/scss/widgets/notification-panel/notification-card.scss @@ -0,0 +1,91 @@ +.notification-card-eventbox { + background-color: $black-lighter; + border-radius: 15px; +} + +.notification-card-box { + padding: 1em; +} + +.notification-card-header { + background-color: $black-darker; + border-radius: 15px 15px 0 0; + padding: 5px 20px 5px 20px; +} + +.notification-app-name { + font-size: 1em; + color: $pink; +} + +.notification-timestamp { + font-size: .8em; + color: $blue; +} + +.notification-close { + color: $red; + font-size: 1em; + transition: 0.5s; + + &:hover { + color: $blue; + } +} + +.notification-summary { + font-size: 1.2em; + padding-bottom: 10px; + color: $pink; +} + +.notification-body { + font-size: 1em; + color: $pink; +} + +.notification-image { + background-repeat: no-repeat; + background-size: cover; + background-position: center; + min-width: 50px; + min-height: 50px; + border-radius: 10px; + margin: 0 0 10px 20px; +} + +.notification-image-screenshot { + background-repeat: no-repeat; + background-size: cover; + background-position: center; + min-width: 300px; + min-height: 200px; + border-radius: 10px; + margin: 1em; +} + +.notification-screenshot-open, +.notification-screenshot-delete { + padding: 10px; + border-radius: 10px; + margin-left: 2em; + margin-right: 2em; + transition: 0.5s; + box-shadow: 0 0 10px rgba(0, 0, 0, 1); +} + +.notification-screenshot-open:hover, +.notification-screenshot-delete:hover { + border-radius: 20px; + background-color: $black; + color: $blue; +} + +.notification-screenshot-open { + background-color: $blue; + color: $black; +} +.notification-screenshot-delete { + background-color: $red; + color: $black; +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/top-panel.scss b/widgets/control/src/scss/widgets/top-panel.scss new file mode 100644 index 0000000..08fbcf4 --- /dev/null +++ b/widgets/control/src/scss/widgets/top-panel.scss @@ -0,0 +1,3 @@ +.top-panel-window { + background-color: $black; +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/top-panel/actions.scss b/widgets/control/src/scss/widgets/top-panel/actions.scss new file mode 100644 index 0000000..f4e2070 --- /dev/null +++ b/widgets/control/src/scss/widgets/top-panel/actions.scss @@ -0,0 +1,64 @@ +.top-panel-window { + .actions { + font-size: 1.3em; + border-radius: 10px; + background-color: $black-darker; + padding: .5em; + margin-right: 1em; + transition: all 0.3s cubic-bezier(0.455, 0.03, 0.515, 1); + box-shadow: 0px 0px 10px rgba(0, 0, 0, 1); + } + + .brightness-icon, + .volume-icon, + .network-icon { + margin: 0 .5em; + transition: 0.5s; + + &:hover { + color: $blue; + } + } + + .brightness-icon { + color: $yellow; + } + .volume-icon { + color: $green; + } + .network-icon { + color: $purple; + } + + scale trough { + all: unset; + background-color: $black-lighter; + border-radius: 5px; + min-height: 10px; + min-width: 80px; + } + + .brightness-bar trough highlight { + background-color: $yellow; + border-radius: 5px; + } + .volume-bar trough highlight { + background-color: $green; + border-radius: 5px; + } + .network-speed { + font-size: 0.8em; + color: $purple; + } + + .tray { + font-size: 1.5em; + margin: 0 .5em 0 0; + color: $green; + transition: 0.5s; + + &:hover { + color: $blue; + } + } +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/top-panel/date.scss b/widgets/control/src/scss/widgets/top-panel/date.scss new file mode 100644 index 0000000..64ecac7 --- /dev/null +++ b/widgets/control/src/scss/widgets/top-panel/date.scss @@ -0,0 +1,21 @@ +.top-panel-window { + .time { + font-weight: bold; + color: $blue; + + .time-text { + font-size: 1.1em; + margin: 0em 1em 0em 0em; + transition: 0.5s; + } + + .time-icon { + font-size: 1.3em; + margin: 0em 0.5em 0em 0em; + } + + .time-text:hover { + color: $pink; + } + } +} diff --git a/widgets/control/src/scss/widgets/top-panel/music.scss b/widgets/control/src/scss/widgets/top-panel/music.scss new file mode 100644 index 0000000..305fdf5 --- /dev/null +++ b/widgets/control/src/scss/widgets/top-panel/music.scss @@ -0,0 +1,43 @@ +.top-panel-window { + .music-bar { + .song-cover-bar { + background-size: cover; + background-position: center; + min-height: 25px; + min-width: 25px; + margin: 10px; + border-radius: 100%; + } + + .song-bar { + color: $blue; + font-size: 1.1em; + margin-right: 1em; + transition: 0.5s; + } + + .music-play-bar { + color: $pink; + font-size: 1.5em; + transition: 0.5s; + + &:hover { + color: $blue; + } + } + + .music-previous-bar, + .music-next-bar { + color: $blue; + font-size: 1.5em; + margin: 0 0.5em; + transition: 0.5s; + } + + .music-previous-bar:hover, + .music-next-bar:hover, + .song-bar:hover { + color: $pink; + } + } +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/top-panel/sysmenu.scss b/widgets/control/src/scss/widgets/top-panel/sysmenu.scss new file mode 100644 index 0000000..10d0e3f --- /dev/null +++ b/widgets/control/src/scss/widgets/top-panel/sysmenu.scss @@ -0,0 +1,17 @@ +.top-panel-window { + .sysmenu { + margin: 0em 1.5em 0em 0.5em; + + button { + border-radius: 6px; + font-size: 1.3em; + font-weight: bold; + color: $red; + transition: 0.5s; + + &:hover { + color: $blue; + } + } + } +} \ No newline at end of file diff --git a/widgets/control/src/scss/widgets/top-panel/workspaces.scss b/widgets/control/src/scss/widgets/top-panel/workspaces.scss new file mode 100644 index 0000000..04932d1 --- /dev/null +++ b/widgets/control/src/scss/widgets/top-panel/workspaces.scss @@ -0,0 +1,41 @@ +.top-panel-window { + .workspaces { + background-color: $black-darker; + border-radius: 0 20px 20px 0; + font-size: 1em; + font-weight: bold; + padding: 0 1.5em 0 1.5em; + box-shadow: 0px 0px 10px rgba(0, 0, 0, 1); + + button { + margin: .8em; + padding-left: 1px; + transition: 0.5s; + + &.active-occupied, + &.active-empty { + color: $pink; + + &:hover { + color: $blue; + } + } + + &.inactive-occupied { + color: $blue; + + &:hover { + color: $pink; + } + } + + &.inactive-empty { + color: $black-lighter; + + &:hover { + color: $blue; + } + } + } + } +} \ No newline at end of file diff --git a/widgets/control/src/yuck/variables.yuck b/widgets/control/src/yuck/variables.yuck new file mode 100644 index 0000000..de9b316 --- /dev/null +++ b/widgets/control/src/yuck/variables.yuck @@ -0,0 +1,186 @@ +;; Variables +(defvar info-panel false) +(defvar control-panel false) +(defvar music-panel false) +(defvar control-panel-hover false) +(defvar lockscreen false) + +(defvar top-panel-music false) + +(defvar players + "spotify,%any,firefox,chromium,brave,mpd" +) + +;; Date and Time +(defpoll time + :interval "1s" + "date +%I:%M" +) +(defpoll seconds + :interval "250ms" + "echo $(($(date +%S)* 100 / 60))" +) +(defpoll date + :interval "24h" + "date +'%B, %a %d'" +) + +;; Weather +(defpoll weather-col-one + :interval "250ms" + "scripts/weather color-1" +) +(defpoll weather-col-two + :interval "250ms" + "scripts/weather color-2" +) +(defpoll weather-icon + :interval "5s" + "scripts/weather icon" +) +(defpoll weather-temperature + :interval "5s" + "scripts/weather temperature" +) +(defpoll weather-status + :interval "5s" + "scripts/weather status" +) +(defpoll city + :interval "24h" + "scripts/weather city" +) +(defpoll country + :interval "24h" + "scripts/weather country" +) + +;; Calendar +(defpoll calendar-day + :interval "10h" + "date '+%d'" +) +(defpoll calendar-month + :interval "10h" + "echo $(($(date +%m) - 1))" +) +(defpoll calendar-year + :interval "10h" + "date '+%Y'" +) + +;; Notifications +(deflisten notifications-cards + "scripts/logger subscribe" +) +(defpoll lows + :interval "5s" + "scripts/logger lows" +) +(defpoll normals + :interval "5s" + "scripts/logger normals" +) +(defpoll criticals + :interval "5s" + "scripts/logger criticals" +) + +;; Profile +(defpoll name + :interval "24h" + "getent passwd `whoami` | cut -d : -f 5" +) + +;; Controls +(defpoll nl-check + :interval "250ms" + "scripts/controls nl-check" +) +(defpoll air-check + :interval "250ms" + "scripts/controls air-check" +) +(defpoll dnd-check + :interval "250ms" + "scripts/controls dnd-check" +) +(defpoll blue-check + :interval "250ms" + "scripts/controls blue-check" +) + +;; Sliders +(defpoll volume-icon + :interval "250ms" + "scripts/system volume-icon" +) +(defpoll volume-value + :interval "250ms" + "scripts/system volume-value" +) +(defpoll brightness-icon + :interval "250ms" + "scripts/system brightness-icon" +) +(defpoll brightness-value + :interval "250ms" + "scripts/system brightness-value" +) +(defpoll network-icon + :interval "250ms" + "scripts/system network-icon" +) +(defpoll network-speed + :interval "250ms" + "scripts/system network-speed" +) +(defpoll network-name + :interval "250ms" + "scripts/system network-name" +) + +;; Music +(defpoll music-cover + :interval "1s" + "scripts/music cover" +) +(defpoll song-status + :interval "1s" + "scripts/music status" +) +(defpoll song-artist + :interval "1s" + "scripts/music artist" +) +(defpoll song-title + :interval "1s" + "scripts/music title" +) +(defpoll song-position + :interval "250ms" + "scripts/music position" +) +(defpoll play-pause + :interval "250ms" + "scripts/music status-icon" +) +(defpoll title-artist + :interval "250ms" + "scripts/music title-artist" +) +(deflisten music-scroll + "scripts/scroll scroll-toggle" +) + +;; Top Panel +(deflisten workspaces + "scripts/workspaces" +) +(deflisten scroll + "scripts/scroll" +) +(defpoll tray + :interval "250ms" + "$HOME/.config/bin/tray eww-check" +) \ No newline at end of file diff --git a/widgets/control/src/yuck/widgets/control-panel.yuck b/widgets/control/src/yuck/widgets/control-panel.yuck new file mode 100644 index 0000000..4b477f6 --- /dev/null +++ b/widgets/control/src/yuck/widgets/control-panel.yuck @@ -0,0 +1,228 @@ +(defwidget control-panel [] + (box + :orientation "h" + :space-evenly false + :class "control-panel-window" + + (box + :style {control-panel ? "padding-right: 0em;" : "padding-right: 55em;"} + :class "control-panel-spacer" + ) + + (box + :orientation "v" + :space-evenly false + + (box + :orientation "v" + :space-evenly false + :class "control-panel" + + (box + :orientation "h" + :space-evenly false + :class "profile-box" + + (box + :class "profile-picture" + ) + + (label + :valign "center" + :class "profile-info" + :text {name} + ) + ) + + (box + :orientation "h" + :class "control-box" + + (eventbox + :cursor "pointer" + + (box + :class "controls" + + (button + :tooltip "Night Light" + :style {nl-check == "On" ? "background-color: #ece3b1; color: #1b1f23;" : "background-color: #1b1f23; color: #ece3b1"} + :onclick "scripts/controls nl-toggle" + {nl-check == "On" ? "󰖔" : "󰃞"} + ) + + (button + :tooltip "Do Not Disturb" + :style {dnd-check == "On" ? "background-color: #b4e3ad; color: #1b1f23;" : "background-color: #1b1f23; color: #b4e3ad"} + :onclick "scripts/controls dnd-toggle" + {dnd-check == "On" ? "󰂚" : "󰂛"} + ) + + (button + :tooltip "Airplane Mode" + :style {air-check == "On" ? "background-color: #ceace8; color: #1b1f23;" : "background-color: #1b1f23; color: #ceace8"} + :onclick "scripts/controls air-toggle" + {air-check == "On" ? "󰀝" : "󰀞"} + ) + + (button + :tooltip "Bluetooth" + :style {blue-check == "On" ? "background-color: #a3cbe7; color: #1b1f23;" : "background-color: #1b1f23; color: #a3cbe7"} + :onclick "scripts/controls blue-toggle" + {blue-check == "On" ? "󰂯" : "󰂲"} + ) + ) + ) + ) + + (box + :orientation "h" + :space-evenly false + :halign "center" + :valign "center" + :class "slider" + + (eventbox + :cursor "pointer" + + (button + :onclick "pamixer -t" + :class "volume-icon" + {volume-icon} + ) + ) + + (eventbox + :cursor "all-scroll" + + (scale + :orientation "h" + :class "volume-bar" + :min 0 + :max 101 + :value volume-value + :tooltip "Volume: ${volume-value}%" + :onchange "pamixer --set-volume {}" + ) + ) + ) + + (eventbox + :cursor "all-scroll" + + (box + :orientation "h" + :space-evenly false + :halign "center" + :valign "center" + :class "slider" + + (label + :class "brightness-icon" + :text {brightness-icon} + ) + + (scale + :orientation "h" + :class "brightness-bar" + :min 0 + :max 101 + :value brightness-value + :tooltip "Brightness: ${brightness-value}%" + :onchange "brightnessctl set {}%" + ) + ) + ) + ) + + (revealer + :reveal music-panel + :transition "slideup" + + (box + :orientation "h" + :space-evenly false + :halign "center" + :class "music-box" + + (box + :class "song-cover" + :orientation "v" + :style "background-image: url('${music-cover}')" + + (box + :class "song" + :orientation "v" + :space-evenly "false" + :hexpand "false" + :vexpand "false" + + (label + :class "now-playing" + :halign "start" + :text {song-status} + ) + + (label + :class "song-title" + :halign "start" + :text {song-title} + :limit-width 23 + ) + + (label + :class "song-artist" + :halign "start" + :text {song-artist} + :limit-width 35 + ) + + (label + :class "song-position" + :halign "start" + :text song-position + ) + + (eventbox + :cursor "pointer" + + (box + :class "song-controls" + :orientation "h" + :halign "center" + + (button + :class "music-controls" + :onclick "playerctl -p ${players} loop track" + :onrightclick "playerctl -p ${players} loop playlist" + :onmiddleclick "playerctl -p ${players} loop none" "" + ) + + (button + :class "music-controls" + :onclick "playerctl -p ${players} previous" "󰒮" + ) + + (button + :class "music-play" + :onclick "playerctl -p ${players} play-pause" play-pause + ) + + (button + :class "music-controls" + :onclick "playerctl -p ${players} next" "󰒭" + ) + + (button + :class "music-controls" + :onclick "playerctl -p ${players} shuffle toggle" "" + ) + ) + ) + ) + ) + ) + ) + ) + ) +) diff --git a/widgets/control/src/yuck/widgets/info-panel.yuck b/widgets/control/src/yuck/widgets/info-panel.yuck new file mode 100644 index 0000000..1d01f90 --- /dev/null +++ b/widgets/control/src/yuck/widgets/info-panel.yuck @@ -0,0 +1,206 @@ +(defwidget info-panel [] + (box + :orientation "h" + :space-evenly false + :class "info-panel-window" + + (box + :style {info-panel ? "padding-right: 0em;" : "padding-right: 102em;"} + :class "info-panel-spacer" + ) + + (box + :orientation "h" + :space-evenly false + :class "info-panel" + + (box + :orientation "v" + :space-evenly false + + (box + :orientation "h" + :space-evenly false + :halign "center" + + (box + :orientation "v" + :space-evenly false + :class "date-box" + + (circular-progress + :class "time-circle" + :value seconds + :thickness 5 + :start-at seconds + + (box + :orientation "v" + :class "time-circle-text" + + (label + :class "time" + :text {time} + ) + + (label + :class "date" + :text {date} + ) + ) + ) + ) + + (box + :orientation "v" + :class "weather-box" + :style "background: linear-gradient(135deg, ${weather-col-one}, ${weather-col-two});" + + (box + :orientation "v" + :halign "start" + :valign "start" + :class "weather-info" + + {weather-status} + + (label + :halign "start" + :text {weather-temperature} + ) + + (label + :halign "start" + :text "${city}, ${country}" + ) + ) + + (box + :valign "end" + :halign "end" + :class "weather-icon" + {weather-icon} + ) + ) + ) + + (box + :orientation "v" + :space-evenly false + :class "cal-box" + :halign "center" + + (calendar + :class "cal" + :show-details false + :show-heading true + :show-day-names true + :day calendar-day + :month calendar-month + :year calendar-year + ) + ) + + (box + :orientation "h" + :space-evenly true + :class "stat-box" + + (box + :orientation "v" + :halign "center" + :class "stats" + :space-evenly false + + (circular-progress + :hexpand false + :vexpand false + :value 100 + :thickness 3 + :class "low-circle" + + (label + :hexpand false + :vexpand false + :class "low-circle-text" + :text {lows} + ) + ) + + (label + :class "low-text" + :halign "center" + :text "Low" + ) + ) + + + (box + :orientation "v" + :halign "center" + :class "stats" + :space-evenly false + + (circular-progress + :hexpand false + :vexpand false + :value 100 + :thickness 3 + :class "normal-circle" + + (label + :hexpand false + :vexpand false + :class "normal-circle-text" + :text {normals} + ) + ) + + (label + :class "normal-text" + :halign "center" + :text "Normal" + ) + ) + + (box + :orientation "v" + :halign "center" + :class "stats" + :space-evenly false + + (circular-progress + :hexpand false + :vexpand false + :value 100 + :thickness 3 + :class "critical-circle" + + (label + :hexpand false + :vexpand false + :class "critical-circle-text" + :text {criticals} + ) + ) + + (label + :class "critical-text" + :halign "center" + :text "Critical" + ) + ) + + ) + ) + + (box + :width 15 + :space-evenly true + :halign "center" + ) + + (notification-panel) + ) + ) +) \ No newline at end of file diff --git a/widgets/control/src/yuck/widgets/notification-panel.yuck b/widgets/control/src/yuck/widgets/notification-panel.yuck new file mode 100644 index 0000000..2c298f1 --- /dev/null +++ b/widgets/control/src/yuck/widgets/notification-panel.yuck @@ -0,0 +1,201 @@ +(defwidget notification-card [?class summary body image ?SL ?R application timestamp] + (eventbox + :onrightclick R + :class "notification-card-eventbox" + + (box + :space-evenly false + :orientation "v" + + (box + :space-evenly true + :orientation "h" + :class "notification-card-header" + + (label + :class "notification-app-name" + :text application + :halign "start" + ) + + (eventbox + :cursor "pointer" + + (button + :onclick "scripts/logger remove ${SL}" + :class "notification-close" + :halign "end" + :tooltip "Remove this notification" + "" + ) + ) + ) + + (box + :orientation "h" + :space-evenly false + + (box + :halign "start" + :valign "center" + :class "notification-image" + :style "background-image: url(\"${image}\");" + ) + + (box + :orientation "v" + :space-evenly false + :hexpand true + :valign "center" + :class "notification-card-box" + + (label + :limit-width 25 + :wrap true + :text summary + :halign "start" + :valign "center" + :class "notification-summary" + :hexpand true + ) + + (label + :limit-width 30 + :wrap true + :halign "start" + :valign "center" + :text body + :tooltip body + :class "notification-body" + ) + + (label + :class "notification-timestamp" + :text timestamp + :valign "end" + :halign "end" + ) + ) + ) + ) + ) +) + +(defwidget notification-card-screenshot [?class summary image ?SL ?R application timestamp] + (eventbox + :onrightclick R + :class "notification-card-eventbox" + + (box + :space-evenly false + :orientation "v" + + (box + :space-evenly true + :orientation "h" + :class "notification-card-header" + + (label + :class "notification-app-name" + :text "${application}" + :halign "start" + ) + + (label + :class "notification-timestamp" + :text "${timestamp}" + :halign "center" + ) + + (eventbox + :cursor "pointer" + + (button + :onclick "scripts/logger remove ${SL}" + :class "notification-close" + :halign "end" + :tooltip "Remove this notification" + "" + ) + ) + ) + + (box + :orientation "v" + :space-evenly false + + (box + :valign "center" + :halign "center" + :class "notification-image-screenshot" + :style "background-image: url(\"${image}\");" + ) + + (eventbox + :cursor "pointer" + + (box + :orientation "h" + :space-evenly false + :halign "center" + :class "notification-card-box" + + (button + :onclick "viewnior ${image} &" + :class "notification-screenshot-open" + "Open" + ) + + (button + :onclick "scripts/logger remove ${SL} && rm -rf ${image}" + :class "notification-screenshot-delete" + "Delete" + ) + ) + ) + ) + ) + ) +) + + +(defwidget notification-panel [] + (box + :orientation "h" + :space-evenly false + :class "notification-panel-window" + + (box + :orientation "v" + :space-evenly false + + (box + :orientation "h" + :space-evenly true + + (label + :text "Notifications" + :class "notification-box-label" + :valign "center" + :halign "start" + ) + + (eventbox + :cursor "pointer" + + (button + :class "notification-box-clear" + :onclick "scripts/logger clear" + :valign "center" + :halign "end" + "Clear All" + ) + ) + ) + + (literal + :content notifications-cards + ) + ) + ) +) diff --git a/widgets/control/src/yuck/widgets/top-panel.yuck b/widgets/control/src/yuck/widgets/top-panel.yuck new file mode 100644 index 0000000..9696d10 --- /dev/null +++ b/widgets/control/src/yuck/widgets/top-panel.yuck @@ -0,0 +1,207 @@ +(defwidget left [] + (eventbox + :cursor "pointer" + + (literal + :content workspaces + ) + ) +) + +(defwidget center [] + (box + :orientation "h" + :space-evenly false + :valign "center" + :halign "center" + + (eventbox + :onhover "${EWW_CMD} update top-panel-music=true" + :onhoverlost "${EWW_CMD} update top-panel-music=false" + :cursor "pointer" + + (box + :class "music-bar" + :orientation "h" + :space-evenly false + + (box + :class "song-cover-bar" + :style "background-image: url('${music-cover}');" + ) + + (button + :class "song-bar" + {music-scroll} + ;; Replace 'music-scroll' with 'title-artist' if your facing performance drop (Replacing with title-artist will not wrap the text) + ) + + (revealer + :transition "slideright" + :reveal top-panel-music + :duration "350ms" + + (box + :valign "center" + :orientation "h" + + (button + :class "music-previous-bar" + :onclick "playerctl -p ${players} previous" "" + ) + + (button + :class "music-play-bar" + :onclick "playerctl -p ${players} play-pause" play-pause + ) + + (button + :class "music-next-bar" + :onclick "playerctl -p ${players} next" "" + ) + ) + ) + ) + ) + ) +) + +(defwidget right [] + (box + :orientation "h" + :space-evenly false + :halign "end" + + (eventbox + :onclick "$HOME/.config/sweetconfigs-xorg/bin/widgets/control-panel &" + :onhover "${EWW_CMD} update control-panel-hover=true" + :onhoverlost "${EWW_CMD} update control-panel-hover=false" + :cursor "pointer" + + (box + :class "actions" + :style {control-panel-hover ? "background-color: #22262a; padding: .6em;" : "background-color: #1b1f23"} + :orientation "h" + :space-evenly false + :valign "center" + + (box + :orientation "h" + :space-evenly false + :spacing 2 + + (eventbox + :onscroll "$HOME/.config/sweetconfigs-xorg/bin/utilities/brightness {}" + + (label + :class "brightness-icon" + :tooltip "Brightness: ${brightness-value}%" + :text {brightness-icon} + ) + ) + ) + + (box + :orientation "h" + :space-evenly "false" + :spacing 2 + + (eventbox + :onscroll "$HOME/.config/sweetconfigs-xorg/bin/utilities/volume {}" + + (button + :onclick "pamixer -t" + :class "volume-icon" + :tooltip "Volume: ${volume-value}%" + {volume-icon} + ) + ) + ) + + (box + :orientation "h" + :space-evenly false + :spacing 2 + + (button + :onclick "$HOME/.config/sweetconfigs-xorg/bin/menu/network &" + :tooltip "${network-name}" + :class "network-icon" + {network-icon} + ) + ) + ) + ) + + (box + :orientation "h" + :class "time" + :space-evenly "false" + :halign "end" + :valign "center" + + (label + :class "time-icon" + :text "" + ) + + (eventbox + :cursor "pointer" + + (button + :class "time-text" + :tooltip "${date}" + :onclick "$HOME/.config/sweetconfigs-xorg/bin/widgets/info-panel &" + {time} + ) + ) + ) + + (box + :orientation "h" + :space-evenly "false" + :halign "end" + :valign "center" + + (eventbox + :cursor "pointer" + + (button + :class "tray" + :onclick "$HOME/.config/sweetconfigs-xorg/bin/bar/tray eww &" + {tray == "On" ? "" : ""} + ) + ) + ) + + (box + :class "sysmenu" + :orientation "h" + :space-evenly "false" + :halign "end" + :valign "center" + + (eventbox + :cursor "pointer" + + (button + :class "sysmenu-btn" + :onclick "$HOME/.config/sweetconfigs-xorg/bin/menu/power &" + "" + ) + ) + ) + ) +) + + +(defwidget top-panel [] + (box + :orientation "h" + :class "top-panel-window" + + (left) + (center) + (right) + ) +) diff --git a/widgets/control/src/yuck/windows.yuck b/widgets/control/src/yuck/windows.yuck new file mode 100644 index 0000000..3a2a2a7 --- /dev/null +++ b/widgets/control/src/yuck/windows.yuck @@ -0,0 +1,58 @@ +(defwindow control-panel + :geometry + + (geometry + :x "41.2%" + :y "6.5%" + :width "0%" + :height "0%" + :anchor "top right" + ) + + :windowtype "dock" + :wm-ignore true + + (control-panel) +) + +(defwindow info-panel + :geometry + + (geometry + :x "50%" + :y "6.5%" + :width "0%" + :height "0%" + :anchor "top right" + ) + + :windowtype "dock" + :wm-ignore true + + (info-panel) +) +(defwindow top-panel + :geometry + + (geometry + :x "1%" + :y "1%" + :height "6%" + :width "98%" + ) + + :reserve + + (struts + :side "top" + :distance "7%" + ) + + + :stacking "bg" + :windowtype "dock" + :wm-ignore false + + (top-panel) +) + diff --git a/widgets/dashboard/dashboard.scss b/widgets/dashboard/dashboard.scss new file mode 100644 index 0000000..5afbceb --- /dev/null +++ b/widgets/dashboard/dashboard.scss @@ -0,0 +1,304 @@ +.dashboardbox { + background-color: $black; + color: $fg; +} + +.headerbox { + padding: 8px 10px 6px; + .userbox { + .pfp { + background-position: center; + background-repeat: no-repeat; + background-size: cover; + min-height: 32px; + min-width: 32px; + border-radius: 50px; + } + .pcname { + font-family: Cartograph CF; + font-style: italic; + .sep { + color: $blue; + } + } + } + .searchbox { + .searchinput { + background-color: $bg; + padding: 5px 0px; + border-radius: 50px; + transition: all ease .2s; + &:hover { background-color: #000000; } + &:active { background-color: #000000; } + .searchinput-content { + padding: 0px 200px 0px 10px; + font-family: Product Sans; + .icon { + font-family: Iosevka Nerd Font; + color: $blue; + margin-right: 12px; + } + .placeholder { + color: $fg; + } + } + } + } +} + +.content-container { + .contentbox { + background-color: $bg; + border-top-left-radius: 12px; + border-top-right-radius: 12px; + padding: 25px; + + .firstcol { + .usercard { + background-color: $black; + border-radius: 12px; + padding: 10px; + .user-content { + font-family: Cartograph CF; + .pfp { + background-size: cover; + background-position: center; + background-repeat: no-repeat; + min-height: 64px; + min-width: 64px; + border-radius: 50px; + } + .username { + font-family: Cartograph CF; + font-style: italic; + font-size: 20px; + } + .uptime { + font-size: 12px; + } + } + .user-powerbuttons { + button { + background-color: $dimblack; + border-radius: 7px; + padding: 2px 10px 2px 7px; + font-family: Iosevka Nerd Font; + font-size: 16px; + transition: all ease .2s; + &:hover { background-color: $lightblack; } + &:active { background-color: $dimblack; } + } + .poweroff { color: $red; } + .reboot { color: $magenta; } + } + } + .controlscard { + background-color: $black; + padding: 10px; + border-radius: 12px; + .control-container { + .control-item { + transition: all ease .2s; + border-radius: 50px; + + background-color: $dimblack; + color: $blue; + + &:hover { background-color: $lightblack; } + &:active { background-color: $dimblack; } + + &.active { + background: $blue; + color: $bg; + } + + .control-icon { + font-family: Iosevka Nerd Font; + font-size: 14px; + } + + // solving nerd fonts alignment issues + &.network { padding: 9px 19px 9px 10px; } + &.network-disconnected { padding: 9px 18px 9px 11px; } + &.airplane { padding: 9px 17px 9px 12px; } + &.volume { padding: 9px 18px 9px 11px; } + &.volume-muted { padding: 9px 17px 9px 12px; } + &.mic { padding: 9px 16px 9px 13px; } + &.mic-on { padding: 9px 15px 9px 14px; } + &.redshift { padding: 9px 18px 9px 11px; } + &.redshift-on { padding: 9px 17px 9px 12px; } + &.bluetooth { padding: 9px 16px 9px 13px; } + &.bluetooth-on { padding: 9px 15px 9px 14px; } + &.fullscreensht { padding: 9px 17px 9px 12px; } + &.areascreensht { padding: 9px 17px 9px 12px; } + } + } + } + .slidersbox { + .vol-slider { + .vol-label { + font-size: 16px; + font-family: Iosevka Nerd Font; + color: $bg; + margin-left: 10px; + } + & scale trough { + all: unset; + border-radius: 10px; + background-color: $black; + border: none; + min-height: 25px; + min-width: 245px; + margin-left: 3px; + & highlight { + all: unset; + background: $blue; + color: #000000; + border-radius: 10px; + } + } + } + .br-slider { + .br-label { + font-size: 16px; + font-family: Iosevka Nerd Font; + color: $bg; + margin-left: 10px; + } + & scale trough { + all: unset; + border-radius: 100px; + background-color: $black; + border: none; + min-height: 25px; + min-width: 245px; + margin-left: 3px; + & highlight { + all: unset; + background: $blue; + color: #000000; + border-radius: 10px; + } + } + } + .mic-slider { + .mic-label { + font-size: 16px; + font-family: Iosevka Nerd Font; + color: $bg; + margin-left: 10px; + } + & scale trough { + all: unset; + border-radius: 100px; + background-color: $black; + border: none; + min-height: 25px; + min-width: 245px; + margin-left: 3px; + & highlight { + all: unset; + background: $blue; + color: #000000; + border-radius: 10px; + } + } + } + } + } + .lastcol { + .musicbox { + background-color: $black; + border-radius: 12px; + .firstrow { + padding: 10px; + .picbox { + background-size: cover; + background-repeat: no-repeat; + background-position: center; + min-width: 64px; + min-height: 64px; + border-radius: 10px; + } + .info { + font-family: Product Sans; + .music_title { + font-weight: bolder; + } + .music_artist { + color: $lightblack; + } + } + } + .lastrow { + padding: 10px 0px; + background-color: $dimblack; + border-bottom-right-radius: 12px; + border-bottom-left-radius: 12px; + .controls-container { + font-size: 16px; + font-family: Iosevka Nerd Font; + button { + transition: all ease .2s; + border-radius: 7px; + &:hover { background-color: $lightblack; } + &:active { background-color: transparent; } + &.previousbutton, &.nextbutton { padding: 1px 11px 1px 10px; } + &.playpause { + font-size: 20px; + padding: 1px 12px 1px 9px; + } + } + } + } + } + .chart-container { + background-color: $black; + border-radius: 12px; + .header { + padding: 5px 10px; + font-family: Product Sans; + font-size: 14px; + color: $fg; + } + .body .container { + padding: 10px; + .chart-container { + background-color: $lightblack; + border-radius: 3em; + .chart { + color: $blue; + label { + padding: 1em; + font-size: 23px; + } + } + .chart-icon { + background-color: $black; + border-radius: 3em; + padding: 12px 5px; + margin: 0.5em; + font-family: Iosevka Nerd Font; + // nf alignment issues (again). + &.cpu { padding: 12px 9px 12px 4px; } + &.ram { padding: 12px 8px 12px 5px; } + &.temp { padding: 13px 9px 12px 3px; } + &.disk { padding: 13px 8px 12px 4px; } + } + } + } + } + } + } + .footer { + background-color: $bg; + padding: 10px 0px; + font-family: Product Sans; + .protipbox { + .icon { + font-family: Iosevka Nerd Font; + color: $yellow; + } + } + } +} diff --git a/widgets/dashboard/dashboard.yuck b/widgets/dashboard/dashboard.yuck new file mode 100644 index 0000000..e89b013 --- /dev/null +++ b/widgets/dashboard/dashboard.yuck @@ -0,0 +1,384 @@ +(defwidget header [] + (box :class "headerbox" + :orientation "h" + :space-evenly false + (box :class "userbox" + :orientation "h" + :space-evenly false + :halign "start" + :valign "center" + :spacing 10 + (box :class "pfp" + :style "background-image: url('${pfp}');") + (box :class "pcname" + :orientation "h" + :space-evenly false + (label :text "${rawuser}" :class "username") + (label :text "@" :class "sep") + (label :text "${hostname}" :class "hostname") + ) + ) + (box :class "searchbox" + :orientation "h" + :space-evenly true + :halign "end" + :hexpand true + :valign "center" + (button :class "searchinput" + :onclick "~/.config/rofi/launcher/launcher.sh " + (box :class "searchinput-content" + :orientation "h" + :space-evenly false + :hexpand true + :halign "start" + :valign "center" + :vexpand true + (label :text "" :class "icon") + (label :text "Search Applications" :class "placeholder") + ) + ) + ) + ) +) + +(defwidget usercard [] + (box :class "usercard" + :orientation "v" + :space-evenly false + (box :class "user-content" + :orientation "h" + :space-evenly false + :vexpand true + :spacing 15 + (box :class "pfp" + :style "background-image: url('${pfp}');") + (box :class "info" + :hexpand true + :vexpand true + :valign "center" + :halign "start" + :orientation "v" + :space-evenly false + :spacing 3 + (label :text "${username}" :class "username" :halign "start") + (label :text "${uptime}" :class "uptime" :halign "start") + ) + ) + (box :class "user-powerbuttons" + :orientation "h" + :space-evenly false + :hexpand true + :halign "end" + :valign "end" + :spacing 5 + (button :class "reboot" + :onclick "systemctl reboot" + "勒" + ) + (button :class "poweroff" + :onclick "systemctl poweroff" + "襤" + ) + ) + ) +) + +(defwidget control [name icon tooltip onclick active] + (box :class "control-container" + :orientation "h" + :hexpand true + :valign "center" + :space-evenly true + (button :class "control-item ${name} ${active ? 'active' : ''}" + :orientation "h" + :space-evenly true + :halign "center" + :hexpand false + :valign "center" + :onclick "${onclick}" + :tooltip "${tooltip}" + (label :text "${icon}" :class "control-icon") + ) + ) +) + +(defwidget quicksettings [] + (box :class "controlscard" + :orientation "v" + :space-evenly false + :spacing 20 + (box :class "firstrow" + :orientation "h" + :space-evenly true + (control :name "network${network_status == 'Connected' ? '' : '-disconnected'}" + :icon "${network_icon}" + :tooltip "Network" + :active "${network_status == 'Connected' ? 'true' : 'false'}" + :onclick "~/.config/polybar/widgets/scripts/network.sh toggle") + + (control :name "airplane" + :icon "${network_radio == 'on' ? '' : ''}" + :tooltip "Airplane Mode" + :active "${network_radio == 'on' ? false : true}" + :onclick "~/.config/polybar/widgets/scripts/network.sh toggle-radio") + + (control :name "volume${is_muted == 'yes' ? '-muted' : ''}" + :icon "${volume_icon}" + :tooltip "Toggle muted" + :active "${is_muted == 'yes' ? false : true}" + :onclick "~/.config/polybar/widgets/scripts/volume.sh toggle-muted") + + (control :name "mic${mic_status == 'yes' ? '-on' : ''}" + :icon "${mic_status == 'yes' ? '' : ''}" + :tooltip "Toggle microphone" + :active "${mic_status == 'yes' ? true : false}" + :onclick "~/.config/polybar/widgets/scripts/mic toggle") + ) + (box :class "lastrow" + :orientation "h" + :space-evenly true + (control :name "redshift${redshift_state == 'on' ? '-on' : ''}" + :icon "${redshift_state == 'on' ? '' : ''}" + :tooltip "Toggle night light" + :active "${redshift_state == 'on' ? true : false}" + :onclick "~/.config/polybar/widgets/scripts/redshift toggle") + + (control :name "bluetooth${bluetooth_state == 'on' ? '-on' : ''}" + :icon "${bluetooth_state == 'on' ? '' : ''}" + :tooltip "Toggle bluetooth" + :active "${bluetooth_state == 'on' ? true : false}" + :onclick "~/.config/polybar/widgets/scripts/bluetooth toggle") + + (control :name "fullscreensht" + :icon "" + :tooltip "Take a full screenshot" + :active false + :onclick "takeshot --win") + + (control :name "areascreensht" + :icon "" + :tooltip "Take a screenshot of a selected area" + :active false + :onclick "takeshot --area") + ) + ) +) + +(defwidget sliders [] + (box :class "slidersbox" + :orientation "v" + :space-evenly false + :spacing 25 + (box :orientation "h" + :space-evenly "false" + :class "vol-slider" + (overlay + (scale :value volume_value + :onchange "~/.config/polybar/widgets/scripts/volume.sh set {}" + :min 0 + :max 101) + (label :class "vol-label" + :text "${volume_icon} " + :valign "top" + :halign "start") + ) + ) + (box :orientation "h" + :space-evenly "false" + :class "br-slider" + (overlay + (scale :value br + :onchange "~/.config/polybar/widgets/scripts/brightness.sh set {}" + :min 0 + :max 101) + (label :class "br-label" + :text "${br_icon} " + :valign "top" + :halign "start") + ) + ) + (box :orientation "h" + :space-evenly "false" + :class "mic-slider" + (overlay + (scale :value mic + :onchange "~/.config/polybar/widgets/scripts/mic set {}" + :min 0 + :max 101) + (label :class "mic-label" + :text "${mic_status == 'yes' ? '' : ''} " + :valign "top" + :halign "start") + + ) + ) + ) +) + +(defwidget music [] + (box :class "musicbox" + :orientation "v" + :space-evenly false + (box :class "firstrow" + :orientation "h" + :space-evenly false + :vexpand true + (box :class "picbox" + :orientation "v" + :space-evenly true + :style "background-image: url('${art}');" + ) + (box :class "info" + :space-evenly false + :hexpand true + :valign "center" + :orientation "v" + :spacing 5 + (label :class "song" :limit-width 18 :text title) + (label :class "artist" :limit-width 18 :text artist) + ) + ) + (box :class "lastrow" + :orientation "h" + :space-evenly true + (box :class "controls-container" + :orientation "h" + :space-evenly true + (button :onclick "~/.config/eww/scripts/sidebar/music previous" + :class "previousbutton" + :halign "center" + "玲" + ) + (button :onclick "~/.config/eww/sidebar/scripts/music toggle" + :class "playpause" + :halign "center" + playpause + ) + (button :onclick "~/.config/eww/sidebar/scripts/music next" + :class "nextbutton" + :halign "center" + "怜" + ) + ) + ) + ) +) + +(defwidget chart [label value icon icon-class] + (box :class "chart-container" + :orientation "v" + :space-evenly false + (box :class "header" + :space-evenly true + :orientation "v" + (label :text "${label}" :class "label" :halign "start") + ) + (box :class "body" + :space-evenly true + :orientation "h" + (box :class "container" + :space-evenly true + :halign "center" + :hexpand true + :orientation "h" + (box :class "chart-container" :space-evenly false + (overlay + (circular-progress :value value + :thickness 50 + :class "chart" + :start-at 75 + (label :text "L") + ) + (label :text "${icon}" :class "chart-icon ${icon-class}") + ) + ) + ) + ) + ) +) + +(defwidget charts [] + (box :class "chartsbox" + :orientation "v" + :space-evenly false + :spacing 25 + (box :class "firstrow" + :orientation "h" + :space-evenly true + :spacing 25 + (chart :label "Cpu" :icon "" :icon-class "cpu" :value cpu) + (chart :label "Ram" :icon "" :icon-class "ram" :value ram) + ) + (box :class "lastrow" + :orientation "h" + :space-evenly true + :spacing 25 + (chart :label "Temp" :icon "" :icon-class "temp" :value temp) + (chart :label "Disk" :icon "" :icon-class "disk" :value disk) + ) + ) +) + +(defwidget footer [] + (box :class "footer" + :space-evenly true + :orientation "h" + :valign "end" + (box :class "protipbox" + :orientation "h" + :space-evenly false + :halign "center" + :spacing 15 + (label :text "ﯦ " :class "icon") + (label :text "Press the menu icon again to close this popup." :class "placeholder") + ) + ) +) + +(defwidget content [] + (box :class "content-container" + :orientation "v" + :space-evenly false + (box :class "contentbox" + :orientation "h" + :space-evenly true + :spacing 25 + :vexpand true + (box :class "firstcol" + :orientation "v" + :space-evenly false + :spacing 25 + (usercard) + (quicksettings) + (sliders) + ) + (box :class "lastcol" + :orientation "v" + :space-evenly false + :spacing 25 + (music) + (charts) + ) + ) + (footer) + ) +) + +(defwidget dashboard [] + (box :class "dashboardbox" + :orientation "v" + :space-evenly false + (header) + (content) + ) +) + +(defwindow dashboard + :geometry (geometry :x "6px" + :y "-44px" + :width "10px" ; automatically adjusted by eww + :height "10px" ; automatically adjusted by eww + :anchor "bottom center") + :type "dock" + :wm-ignore false +(dashboard)) diff --git a/widgets/date/date.scss b/widgets/date/date.scss new file mode 100644 index 0000000..991cc16 --- /dev/null +++ b/widgets/date/date.scss @@ -0,0 +1,71 @@ +.date { + background-color: $bg; + color: $fg; + border-radius: 4px; +} + +.timebox { + margin: 30px 0px; + .time-container { + .content { + font-family: Product Sans; + font-weight: bolder; + font-size: 60px; + } + .divider { + margin: 8px 15px; + padding: 0px 1px; + background: linear-gradient($red, $magenta, $blue, $cyan); + } + } + .date-container { + font-family: Product Sans; + margin-top: 2px; + } +} + +.cal-box { + font-family: Product Sans; + background-color: $bg; + border-radius: 4px; + padding: 0 1rem .2rem; + color: $fg; + background-color: $contrastbg; + margin: 0px 12px 12px 12px; + + .cal { + background-color: inherit; + padding: .5rem .10rem 0rem; + margin-left: 10px; + + & > * { + border: solid 0px transparent; + } + + &.highlight { + padding: 10rem; + } + } +} + +calendar:selected { + color: $cyan; +} + +calendar.header { + color: $cyan; + font-weight: bold; +} + +calendar.button { + color: $cyan; +} + +calendar.highlight { + color: $green; + font-weight: bold; +} + +calendar:indeterminate { + color: $lightblack; +} \ No newline at end of file diff --git a/widgets/date/date.yuck b/widgets/date/date.yuck new file mode 100644 index 0000000..6556cef --- /dev/null +++ b/widgets/date/date.yuck @@ -0,0 +1,56 @@ +(defwidget divider [] + (box :class "divider" + :orientation "v" + :space-evenly true) +) + +(defwidget time [] + (box :class "timebox" + :orientation "v" + :space-evenly false + (box :class "time-container" + :orientation "h" + :space-evenly false + :halign "center" + :valign "center" + (label :text "${lithour}" :class "content") + (divider) + (label :text "${litmin}" :class "content") + ) + (box :class "date-container" + :orientation "h" + :space-evenly true + :halign "center" + (label :text "${completeday}") + ) + ) +) + +(defwidget cal [] + (box :class "cal-box" + :orientation "v" + :space-evenly false + (calendar :class "cal" + :day calendar_day + :month calendar_month + :year calendar_year) + ) +) + +(defwidget date [] + (box :class "date" + :orientation "v" + :space-evenly false + (time) + (cal) + ) +) + +(defwindow date + :geometry (geometry :x "-11px" + :y "48px" + :width "0px" ; automatically generated + :height "0px" ; automatically generated + :anchor "right top") + :wm-ignore true +(date)) diff --git a/widgets/eww.scss b/widgets/eww.scss new file mode 100644 index 0000000..a4802e6 --- /dev/null +++ b/widgets/eww.scss @@ -0,0 +1,8 @@ +* { + all: unset; +} + +@import "colors.scss"; +@import "date/date.scss"; +@import "actions/actions.scss"; +@import "dashboard/dashboard.scss"; diff --git a/widgets/eww.yuck b/widgets/eww.yuck new file mode 100644 index 0000000..c03484d --- /dev/null +++ b/widgets/eww.yuck @@ -0,0 +1,5 @@ +(include "variables.yuck") +(include "date/date.yuck") +(include "actions/actions.yuck") +(include "dashboard/dashboard.yuck") + diff --git a/widgets/noti/$XDG_CACHE_HOME/dunst/notifications.txt b/widgets/noti/$XDG_CACHE_HOME/dunst/notifications.txt new file mode 100644 index 0000000..e69de29 diff --git a/widgets/noti/$XDG_CACHE_HOME/dunst/quotes.txt b/widgets/noti/$XDG_CACHE_HOME/dunst/quotes.txt new file mode 100644 index 0000000..e69de29 diff --git a/widgets/noti/eww.scss b/widgets/noti/eww.scss new file mode 100644 index 0000000..d603c88 --- /dev/null +++ b/widgets/noti/eww.scss @@ -0,0 +1,586 @@ +// Author: github.com/NeshHari +* { + all: unset; +} + +$base: #1e1e2e; +$mantle: #181825; +$crust: #11111b; +$text: #cdd6f4; +$subtext0: #a6adc8; +$subtext1: #bac2de; +$surface0: #313244; +$surface1: #45475a; +$surface2: #585b70; +$overlay0: #6c7086; +$overlay1: #7f849c; +$overlay2: #9399b2; +$blue: #89b4fa; +$lavender: #b4befe; +$sapphire: #74c7ec; +$sky: #89dceb; +$teal: #94e2d5; +$green: #a6e3a1; +$yellow: #f9e2af; +$peach: #fab387; +$maroon: #eba0ac; +$red: #f38ba8; +$mauve: #cba6f7; +$pink: #f5c2e7; +$flamingo: #f2cdcd; +$rosewater: #f5e0dc; + +.cal-inner-container, +.cal-outer-container { + font-family: Vanilla Caramel; + font-size: 1em; + font-weight: bold; + color: $text; + background-color: $crust; + border-radius: 0.3em; + padding: 0.4em; +} + +.cal-inner-elems { + padding: 0.7em; +} + +.cal-date { + font-family: Vanilla Caramel; + font-size: 1.5em; + color: $base; + background-color: $mauve; + border-radius: 0.1em; + padding: 0.2em; + margin: 0.2em; +} + +calendar.button { + color: $text; +} + +calendar.header, +calendar.highlight { + color: $mauve; +} + +calendar:selected { + color: $base; + background-color: $mauve; + border-radius: 10em; +} + +calendar:indeterminate { + color: $surface2; +} + +.btn-launcher, +.btn-notifications, +.btn-quote, +.btn-mail, +.btn-layout, +.btn-wmclass, +.btn-bluetooth, +.btn-volume, +.btn-update, +.btn-update-num, +.btn-time, +.btn-search, +.btn-sysdata, +.btn-spotify, +.btn-power { + font-family: "Font Awesome 6 Free Solid"; +} + +.btn-spotify { + font-family: FiraCode Nerd Font; + font-weight: bold; + font-size: 1em; + color: $text; + background-color: $crust; +} + +.btn-spotify:hover { + color: $crust; + background-color: $text; + transition: ease-in-out 0.3s all; +} + +.spotify-container { + background-color: $crust; + padding: 1em; + border: 0.2em solid $mantle; +} + +.spotify-img { + background-repeat: no-repeat; + background-size: cover; + min-height: 220px; + min-width: 220px; + border-radius: 0.3em; + border: 0.3em solid $text; + margin: 0.3em 1em; +} + +.spotify-inner-right-container { + margin: 0.3em; + color: $crust; + background-color: $text; + border-radius: 0.3em; + padding: 2em; +} + +.spotify-title { + font-family: Vanilla Caramel; + font-size: 1.6em; + font-weight: bold; + color: $crust; +} + +.spotify-singer { + font-family: CaskaydiaCove Nerd Font; + font-size: 1.1em; + font-weight: bold; + color: $crust; +} + +.spotify-album { + font-size: 1em; + color: $crust; + font-style: italic; + margin-bottom: 1em; +} + +.spotify-controls { + margin-top: 1.5em; + color: $surface2; + background-color: $crust; + border-radius: 3em; +} + +.spotify-controls button { + font-family: CaskaydiaCove Nerd Font; + font-weight: bold; + font-size: 1.2em; + margin: 0.3em; +} + +.spotify-controls button:hover { + color: $text; + transition: ease-in-out 0.3s all; +} + +tooltip { + font-family: Vanilla Caramel; + font-size: 1em; + color: $base; + background-color: $yellow; + border-radius: 0.3em; + border: 0.2em solid $peach; +} + +.notifications-container { + background-color: $crust; + padding: 1.8em; +} + +.notifications-header-container { + font-family: Vanilla Caramel; + font-size: 1em; + margin-bottom: 0.5em; + color: $crust; +} + +.notifications-header { + font-size: 2.3em; + font-weight: bold; + background-color: $text; + border-radius: 5em; + padding: 0 0.5em; + margin-bottom: 0.2em; +} + +.notifications-scroll { + background-color: $mantle; + padding: 1em 1.5em 1em 1.5em; + border-radius: 0.3em; +} + +.notifications-footer { + font-family: Vanilla Caramel; + font-size: 1.3em; + color: $crust; + padding-top: 1em; +} + +.notifications-close-container { + background-color: $red; + border: 0.1em solid $red; + border-radius: 0.3em; + padding: 0.2em 0.6em 0.2em 0.6em; + margin: 0 1em; +} + +.notifications-close-container:hover { + background-color: $crust; + color: $red; + transition: ease-in-out 0.3s all; +} + +.notifications-clear-container { + background-color: $peach; + border: 0.1em solid $peach; + border-radius: 0.3em; + padding: 0.2em 0.6em 0.2em 0.6em; + margin: 0 1em; +} + +.notifications-clear-container:hover { + background-color: $crust; + color: $peach; + transition: ease-in-out 0.3s all; +} + +.notifications-cards { + font-family: CaskaydiaCove Nerd Font; + border-radius: 0.3em; + background-color: $text; + color: $crust; +} + +.notifications-cards-header { + font-family: CaskaydiaCove Nerd Font; + font-weight: bold; + background-color: $crust; + color: $text; + padding: 0.8em; + border-radius: 0.3em 0.3em 0 0; +} + +.notifications-card-appname { + font-weight: bold; + padding-left: 2em; +} + +.btn-card-close { + font-family: "Font Awesome 6 Free Solid"; + font-size: 0.8em; + color: $crust; + background-color: $red; + border-radius: 0.3em; + padding-left: 0.8em; + padding-right: 0.8em; + border: 0.1em solid $red; +} + +.btn-card-close:hover { + color: $red; + background-color: $base; + transition: ease 0.5s; +} + +.notifications-card-body { + border-radius: 0.3em; +} + +.notifications-card-image { + background-repeat: no-repeat; + background-size: contain; + min-height: 100px; + min-width: 100px; + border-radius: 0.3em; + padding: 0.5em; + margin-right: 0.8em; +} + +.summary-header { + font-family: Vanilla Caramel; + font-style: italic; + font-size: 1.1em; + background-color: $overlay2; + color: $base; + padding: 0.5em; + margin-bottom: 0.2em; +} + +.notifications-card-timestamp { + font-family: CaskaydiaCove Nerd Font; + font-size: 0.8em; + font-weight: bold; + background-color: $crust; + color: $text; + margin-top: 0.5em; + padding: 0.5em 1em 0.5em 1em; + border-radius: 1em; + margin-bottom: 0.5em; +} + +.notifications-body-text { + color: $crust; + border-radius: 1em; + padding: 0.5em; +} + +.notifications-card-body-content { + font-family: Fira Code Nerd Font; + font-weight: bold; + font-size: 0.8em; + color: $crust; + padding: 0.8em; +} + +/* WHOLE BAR */ +.bar { + background-color: $crust; + padding: 1em 0em; +} + +/* TOP OF BAR */ +.btn-xmonad { + font-family: "CaskaydiaCove Nerd Font"; + font-size: 1.8em; + color: $crust; + background-color: $lavender; + border-radius: 100%; + margin: 0em 0.3em; + padding: 0em 0.3em; + border: 0.1em dashed $base; +} + +.workspace-container { + font-family: "Font Awesome 6 Free Solid"; +} + +.workspace-container button { + background-color: $base; + margin: 0.5em; + padding: 0.3em; + border-radius: 0.1em; +} + +.workspace-container button:hover { + background-color: $green; + color: $crust; + transition: ease-in-out 0.3s all; +} + +// Empty +.wsc0 { + color: $overlay1; +} + +// Focused +.wsc1, +.wsc01, +.wsc11, +.wsc21, +.wsc31, +.wsc41 { + color: $green; +} + +// Ocuppied +.wsc00, +.wsc10, +.wsc20, +.wsc30, +.wsc40 { + color: $yellow; +} + +.btn-layout { + color: $subtext0; +} + +.class-names { + font-family: CaskaydiaCove Nerd Font; + font-weight: bold; + font-size: 1em; + color: $overlay1; +} + +/* MIDDLE OF BAR */ +.app-container { + background-color: $mantle; + padding: 0.1em; + margin-bottom: 0.5em; + border-radius: 0.3em; +} + +.app-container > * { + background-repeat: no-repeat; + background-size: 1.2em; + min-height: 20px; + min-width: 20px; + margin: 0.3em; +} + +.app-container > *:hover { + background-size: 1.3em; + transition: ease-in-out 0.3s all; +} + +.btn-launcher { + color: $teal; +} + +.btn-wmclass { + font-family: CaskaydiaCove Nerd Font; + font-weight: bold; + font-size: 1em; + color: $overlay1; +} + +.btn-search { + color: $pink; +} + +.btn-search:hover { + font-size: 1.1em; + transition: ease-in-out 0.3s all; +} + +.btn-quote { + color: $green; +} + +.btn-quote:hover { + font-size: 1.1em; + transition: ease-in-out 0.3s all; +} + +.quote-container { + background-color: $crust; + padding: 1em; + border-radius: 0.3em; + border: 0.2em solid $mantle; +} + +.quote-img { + background-repeat: no-repeat; + background-size: 30px; + min-height: 30px; + min-width: 30px; + border-radius: 2em; + margin-bottom: 1em; +} + +.quote { + font-family: Vanilla Caramel; + color: $text; +} + +.btn-notifications { + color: $yellow; +} + +.btn-notifications:hover { + font-size: 1.1em; + transition: ease-in-out 0.3s all; +} + +/* BOTTOM OF BAR */ +.btn-time { + font-family: CaskaydiaCove Nerd Font; + font-weight: bold; + font-size: 1em; + background-color: $mantle; + border-radius: 0.3em; + margin: 0em 0.5em; + color: $mauve; +} + +.btn-time > * { + margin: 0.1em 0.5em; +} + +.btn-mail { + color: $yellow; +} + +.label-mail { + font-family: "CaskaydiaCove Nerd Font"; + color: $yellow; +} + +.btn-update { + color: $sapphire; +} + +.label-update { + color: $sapphire; + font-family: "CaskaydiaCove Nerd Font"; +} + +.btn-volume { + color: $lavender; + font-size: 0.9em; +} + +.volume scale trough { + all: unset; + background-color: $mantle; + min-height: 4em; + min-width: 0.5em; + margin: 0.5em 0em; + border-radius: 0.3em; +} + +.volbar trough highlight { + background-color: $lavender; + border-radius: 0.3em; +} + +.btn-sysdata { + color: $peach; +} + +.metric { + font-family: "Font Awesome 6 Free Solid"; + font-size: 0.9em; + background-color: $crust; + color: $peach; + padding: 0.3em; +} + +.metric scale trough { + all: unset; + background-color: $surface2; + min-height: 0.3em; + min-width: 2em; + border-radius: 0.3em; +} + +.metric scale trough highlight { + background-color: $peach; +} + +.btn-bluetooth { + color: $maroon; +} + +.label-bluetooth { + font-family: "CaskaydiaCove Nerd Font"; + font-weight: bold; + color: $maroon; +} + +.btn-power { + color: $red; +} + +.powermenu-container { + font-family: "Font Awesome 6 Free Solid"; + font-size: 2em; + color: $red; + background-color: $crust; + padding: 1em; + border: 0.2em solid $mantle; +} + +.powermenu-container > *:hover { + color: $crust; + background-color: $red; + border-radius: 10%; +} + diff --git a/widgets/noti/eww.yuck b/widgets/noti/eww.yuck new file mode 100644 index 0000000..53a8cb5 --- /dev/null +++ b/widgets/noti/eww.yuck @@ -0,0 +1,159 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;; NOTIFICAITONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; custom rewrite of github.com/syndrizzle/hotfiles +(defpoll notifications_stats :interval "5s" "scripts/logger.py stats") +(deflisten notications_scroll_pane "scripts/logger.py subscribe") + +(defwidget notifications_layout [] + (box :orientation "v" :space-evenly "false" :class "notifications-container" + (box :class "notifications-header-container" :space-evenly "true" :orientation "v" + (label :text "~ NOTIFICATIONS ~" :class "notifications-header" :hexpand true :halign "center")) + (scroll :hscroll false :vscroll true :vexpand "true" :hexpand "true" :class "notifications-scroll" + (literal :content notications_scroll_pane)) + (box :class "notifications-footer" :space-evenly "true" :orientation "h" + (button :onclick "scripts/clearall.sh" :class "notifications-clear-container" "CLEAR ALL") + (button :onclick "eww -c ~/.config/polybar/widgets/noti close notifications" :class "notifications-close-container" "CLOSE")))) + +(defwidget cardimage [summary body ?limit_summary ?limit_body appname icon icon_width icon_height close ?close_action image image_width image_height timestamp urgency] + (eventbox :class "notifications-cards" + (box :orientation "v" :space-evenly false :class "notifications-cards-outer" + (box :class "notifications-cards-header" :space-evenly false :hexpand true :valign "center" :vexpand false + (label :text appname :hexpand true :halign "center" :class "notifications-card-appname") + (button :class "btn-card-close" :onclick close_action :timeout "1s" "")) + (box :space-evenly false :halign "fill" + (box :hexpand true :vexpand true :valign "fill" :orientation "v" :spacing 0 :class "notifications-card-body" :space-evenly false + (label :text summary :limit-width "${limit_summary != "" ? limit_summary : 30}" :halign "fill" :class "summary-header" :show-truncated "true" :wrap "true") + (box :space-evenly true :halign "fill" :class "notifications-card-body-content" :valign "center" + (label :text body :limit-width "${limit_body != "" ? limit_body : 50}" :wrap true :halign "center" :class "notifications-body-text") + (box :class "notifications-card-image-container" :halign "end" + (box :style "background-image: url('${image}')" :hexpand true :vexpand true :width image_width :height image_height :class "notifications-card-image"))) + (label :text "@${timestamp}" :halign "center" :class "notifications-card-timestamp")))))) + +(defwindow notifications :stacking "fg" + :stacking "fg" + :wm-ignore true + :geometry (geometry :x "-11px" :y "48px" :width "18%" :height "70%" :anchor "right top") + (notifications_layout)) + + +(defpoll quote :interval "24h" "scripts/quotes --quote-and-quotee") + +(defwidget quote [] + (box :class "quote-container" :orientation "v" :hexpand "false" :vexpand "true" :space-evenly "false" + (button :style "background-image :url('quote.svg');" :class "quote-img" :halign "center") + (label :class "quote" :halign "center" :wrap "true" :limit-width 300 :text quote))) + +(defwindow showquote + :stacking "fg" + :geometry (geometry :x "8%" + :y "48px" + :width 300) +(quote)) + +(defpoll volume :interval "1s" "amixer -D pulse sget Master | grep 'Left:' | awk -F'[][]' '{ print $2 }' | tr -d '%' | head -1") + +(defwidget showvol [] + (box :class "metric" + :orientation "h" + :space-evenly "false" + :tooltip volume + :style (font-size 25) + :height 30 + (button :onclick "amixer -D pulse set Master 1+ toggle" "") + (scale :min 0 + :max 100 + :value volume + :onchange "amixer -D pulse sset Master {}%" + :width 60 + ) + ) +) + + +(defpoll wifi-signal :interval "10s" "nmcli -f IN-USE,SIGNAL,SSID device wifi | awk '/^\*/{if (NR!=1) {print $2}}'") + +(defwidget showwifi [] + (box :class "metric" + :orientation "h" + :space-evenly "false" + :active "false" + :style (font-size 25) + :height 30 + (box "") + (scale :min 0 + :max 100 + :active "false" + :value {wifi-signal != "" ? wifi-signal : "0.0"} + :width 68 + ) + ) +) + +(defpoll ram :interval "3s" "printf $(free -m | grep Mem | awk '{print ($3/$2)*100}')") + +(defwidget showmemory [] + (box :class "metric" + :orientation "h" + :space-evenly "false" + :active "false" + :style (font-size 30) + :height 30 + (box "") + (scale :min 0 + :max 100 + :value ram + :width 68 + ) + ) +) + +(defpoll cpu :interval "3s" "top -b -n 1 | grep 'Cpu(s)' | awk '{print $2}'") + +(defwidget showcpu [] + (box :class "metric" + :orientation "h" + :space-evenly "false" + :active "false" + :style (font-size 30) + :height 30 + (box "") + (scale :min 0 + :max 100 + :value cpu + :width 65 + ) + ) +) + +(defwidget sysdata [] + (box :class "metric" + :orientation "v" + :space-evenly "false" + (showvol) + (showwifi) + (showcpu) + (showmemory) + ) +) + +(defwindow showsysdata + :stacking "fg" + :geometry (geometry :x "87.5%" + :y "4%" + :height 120) +(sysdata)) + +(defwidget poweroptions [] + (box :class "powermenu-container" :orientation "v" :spacing 30 + (button :class "lock" :onclick "lockscreen" "") + (button :class "logout" :onclick "pkill bsp" "") + (button :class "suspend" :onclick "systemctl suspend" "") + (button :class "restart" :onclick "reboot" "") + (button :class "shutdown" :onclick "shutdown -h now" "") + ) +) + +(defwindow powermenu + :stacking "fg" + :geometry (geometry :x "1812px" + :y "48px" ) + (poweroptions)) \ No newline at end of file diff --git a/widgets/noti/images/default.png b/widgets/noti/images/default.png new file mode 100644 index 0000000..903c825 Binary files /dev/null and b/widgets/noti/images/default.png differ diff --git a/widgets/noti/images/discord.svg b/widgets/noti/images/discord.svg new file mode 100644 index 0000000..1840ba4 --- /dev/null +++ b/widgets/noti/images/discord.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/widgets/noti/images/edge.svg b/widgets/noti/images/edge.svg new file mode 100644 index 0000000..75dabd6 --- /dev/null +++ b/widgets/noti/images/edge.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/widgets/noti/images/files.svg b/widgets/noti/images/files.svg new file mode 100644 index 0000000..b56904f --- /dev/null +++ b/widgets/noti/images/files.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/noti/images/git.svg b/widgets/noti/images/git.svg new file mode 100644 index 0000000..5fb759b --- /dev/null +++ b/widgets/noti/images/git.svg @@ -0,0 +1,16 @@ + + + + + image/svg+xml + + + + + + + + + github + + \ No newline at end of file diff --git a/widgets/noti/images/kitty.svg b/widgets/noti/images/kitty.svg new file mode 100644 index 0000000..24615cd --- /dev/null +++ b/widgets/noti/images/kitty.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/widgets/noti/images/profile.png b/widgets/noti/images/profile.png new file mode 100644 index 0000000..0a4016a Binary files /dev/null and b/widgets/noti/images/profile.png differ diff --git a/widgets/noti/images/spotify.svg b/widgets/noti/images/spotify.svg new file mode 100644 index 0000000..b8bec5e --- /dev/null +++ b/widgets/noti/images/spotify.svg @@ -0,0 +1,15 @@ + + + Spotify-color + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/widgets/noti/images/temp.png b/widgets/noti/images/temp.png new file mode 100644 index 0000000..ec04644 Binary files /dev/null and b/widgets/noti/images/temp.png differ diff --git a/widgets/noti/quote.svg b/widgets/noti/quote.svg new file mode 100644 index 0000000..23ac6a0 --- /dev/null +++ b/widgets/noti/quote.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/widgets/noti/scripts/__pycache__/cache.cpython-310.pyc b/widgets/noti/scripts/__pycache__/cache.cpython-310.pyc new file mode 100644 index 0000000..433d798 Binary files /dev/null and b/widgets/noti/scripts/__pycache__/cache.cpython-310.pyc differ diff --git a/widgets/noti/scripts/__pycache__/handlers.cpython-310.pyc b/widgets/noti/scripts/__pycache__/handlers.cpython-310.pyc new file mode 100644 index 0000000..dd157f6 Binary files /dev/null and b/widgets/noti/scripts/__pycache__/handlers.cpython-310.pyc differ diff --git a/widgets/noti/scripts/__pycache__/utils.cpython-310.pyc b/widgets/noti/scripts/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000..1ac4db0 Binary files /dev/null and b/widgets/noti/scripts/__pycache__/utils.cpython-310.pyc differ diff --git a/widgets/noti/scripts/cache.py b/widgets/noti/scripts/cache.py new file mode 100755 index 0000000..f15ea84 --- /dev/null +++ b/widgets/noti/scripts/cache.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 + +"""A DBus eavesdropper for org.freedesktop.Notifications +interface. + +This is created mainly to cache the raw image data that is +sent by stupid applications like Spotify, Discord, etc. +Now that I think about it all of the electron clients do this. + +Usually any application, if they had to they'd send the +notifications as a path i.e. caching the image themselves +and then returning the path to it. + +Also, +is a really nice manual. Give it a read. +""" + +# Authored By dharmx under: +# GNU GENERAL PUBLIC LICENSE +# Version 3, 29 June 2007 +# +# Copyright (C) 2007 Free Software Foundation, Inc. +# Everyone is permitted to copy and distribute verbatim copies +# of this license document, but changing it is not allowed. +# +# Permissions of this strong copyleft license are conditioned on +# making available complete source code of licensed works and +# modifications, which include larger works using a licensed work, +# under the same license. Copyright and license notices must be +# preserved. Contributors provide an express grant of patent rights. +# +# Read the complete license here: +# + +import datetime +import os +import pathlib +import sys +import typing + +import dbus +import utils + +from dbus.mainloop.glib import DBusGMainLoop +from gi.repository import GLib + + +# TODO: Use actual enumerations by using the enum module from the standard library. +class Urgency: + """Acts as an Enum for indicating the urgency levels as per + the notifications specification. + + You may use these to match wheter a specific notification belongs + to a specific urgency class or, not. + + Attributes: + LOW: Ads, Login, etc. + NORMAL: USB unplugged, Drive mounted, etc. + CRITICAL: Your PC is on fire, Storage Full, etc. + """ + + LOW = b"\x00" + NORMAL = b"\x01" + CRITICAL = b"\x02" + + +class Eavesdropper: + """A quick and naive way of saving the image-data. + + The main idea is to keep a notification server running that + implements image-data and image-path as per the freedesktop + notification specification. + And, then you'd run the eavesdropper which will connect to that + interface (org.freedesktop.Notifications) and will continuously + monitor that interface. + And, if any application sends a notification, that contains a raw + icon then it will be saved into the cache directory. + + Attributes: + callback: + The arbitrary subroutine that will executed on getting a notification. + cache_dir: + The directory path that all of those image-data would be saved. + """ + + # TODO: Segregate more. + def __init__(self, callback: typing.Callable = print, cache_dir: str = "/tmp"): + """Assigns the CTOR parameters to the field variables (duh..) + + Arguments: + callback: The arbitrary subroutine that will executed on getting a notification. + cache_dir: The directory path that all of those image-data would be saved. + """ + self.callback = callback + self.cache_dir = f"{os.path.expandvars(cache_dir)}/image-data" + # translation: mkdir --parents cache_dir + pathlib.PosixPath(self.cache_dir).mkdir(parents=True, exist_ok=True) + + def _message_callback( + self, + _, + message: dbus.lowlevel.MethodReturnMessage or dbus.lowlevel.MethodCallMessage, + ): + """A filter callback for parsing the specific messages that are received from the DBus interface. + + Arguments: + proxy_bus: + The bus that sent the message. + message: + In this case a message is sent when the + Notify method is called AND when the Notify method returns something. + + If the message is of type dbus.lowlevel.MethodCallMessage then this will NOT call the passed callback. + """ + + # we will be filtering this out as we only need the value that the method call returns + # i.e. dbus.lowlevel.MethodReturnMessage + if type(message) != dbus.lowlevel.MethodCallMessage: + return + + args_list = message.get_args_list() + # convert dbus data types to pythonic ones + # eg: dbus.String('Hello') -> 'Hello' + args_list = [utils.unwrap(item) for item in args_list] + + # set fallbacks like a fine gentleman + details = { + "appname": args_list[0].strip() or "Unknown", + "summary": args_list[3].strip() or "", + "body": args_list[4].strip() or "", + "id": datetime.datetime.now().strftime("%s"), + "urgency": "unknown", + } + + if "urgency" in args_list[6]: + details["urgency"] = args_list[6]["urgency"] + + if args_list[2].strip(): + # if the iconpath value is a path i.e. if it has slashes on them + # then assign that as the iconpath + if "/" in args_list[2] or "." in args_list[2]: + details["iconpath"] = args_list[2] + else: + # and if the iconpath is just a string that has no extensions or, + # a pathlike structure like: 'bell' or 'custom-folder-bookmark' + # it might have a dash (-) sign but not all the time. + # then fetch that actual path of that icon as that is a part of the + # icon theme naming convention and the current icon theme should probably have it + details["iconpath"] = utils.get_gtk_icon_path(args_list[2]) + else: + # if there are no icon hints then use fallback (generic bell) + details["iconpath"] = utils.get_gtk_icon_path("custom-notification") + + if "image-data" in args_list[6]: + # capture the raw image bytes and save them to the cache_dir/x.png path + details[ + "iconpath" + ] = f"{self.cache_dir}/{details['appname']}-{details['id']}.png" + utils.save_img_byte(args_list[6]["image-data"], details["iconpath"]) + + # BUG: add a print statement -> init logger.py and disown the process + # BUG: then you'll notice the notifications with value (progress) hint does not get logged + if "value" in args_list[6]: + details["progress"] = args_list[6]["value"] + + # execute arbitrary callback and passing details about the current notification. + self.callback(details) + + # TODO: Segregate more. + def eavesdrop( + self, timeout: int or bool = False, timeout_callback: typing.Callable = print + ): + """Primes the session bus instance and starts a GLib mainloop. + + Arguments: + timeout: + Intervals for executing the callback. + timeout_callback: + Callback that will be executed on intervals. + """ + DBusGMainLoop(set_as_default=True) + + rules = { + "interface": "org.freedesktop.Notifications", + "member": "Notify", + "eavesdrop": "true", # https://bugs.freedesktop.org/show_bug.cgi?id=39450 + } + + bus = dbus.SessionBus() + # discard all other interfaces except org.freedesktop.Notifications + # setting eavesdrop to true enables DBus to send the messages that are + # not meant for you. + # removing the eavesdrop key from rules will not send the Notify method's + # contents to you (you can try and see what happens) + bus.add_match_string( + ",".join([f"{key}={value}" for key, value in rules.items()]) + ) + bus.add_message_filter(self._message_callback) + + try: + loop = GLib.MainLoop() + if timeout: + # executes a callback in intervals + GLib.set_timeout(timeout, timeout_callback) + loop.run() + except (KeyboardInterrupt, Exception) as excep: + sys.stderr.write(str(excep) + "\n") + bus.close() + + +# vim:filetype=python diff --git a/widgets/noti/scripts/clearall.sh b/widgets/noti/scripts/clearall.sh new file mode 100755 index 0000000..46ba6df --- /dev/null +++ b/widgets/noti/scripts/clearall.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +rm -r ~/.config/eww/\$XDG_CACHE_HOME/ +eww -c ~/.config/polybar/widgets/noti open --toggle notifications +eww -c ~/.config/polybar/widgets/noti open --toggle notifications diff --git a/widgets/noti/scripts/handlers.py b/widgets/noti/scripts/handlers.py new file mode 100755 index 0000000..5bc9a45 --- /dev/null +++ b/widgets/noti/scripts/handlers.py @@ -0,0 +1,326 @@ +"""This file is tightly integrated with logger.py and won't work without it. +Unlike the files cache.py and utils.py. + +This module just redirects specific presets of messages based on the source +(the application) that sent that message. + +The redirector will call the specific function based off the appname, then +the called handler function will evaluate the YUCK literal and replace all +of the items on the format string with the passed attributes and then return it. +""" + +# Authored By dharmx under: +# GNU GENERAL PUBLIC LICENSE +# Version 3, 29 June 2007 +# +# Copyright (C) 2007 Free Software Foundation, Inc. +# Everyone is permitted to copy and distribute verbatim copies +# of this license document, but changing it is not allowed. +# +# Permissions of this strong copyleft license are conditioned on +# making available complete source code of licensed works and +# modifications, which include larger works using a licensed work, +# under the same license. Copyright and license notices must be +# preserved. Contributors provide an express grant of patent rights. +# +# Read the complete license here: +# + +import datetime + +import cache +import utils + +# WARN: Subject to heavy change. +# TODO: Use OOP instead of passing around variables +# TODO: Add fallback / canned lambdas for common handler operations eg: formats["appname"] % attributes + + +# TODO: Handle '\n' in body / summary. +def redir_to_handlers(formats, attributes: dict) -> str: + r"""Function for evaluating which handler function will be called. + + Before calling the handler function it will do some filtering and + then actually call the handler which should return a fully evaluated + YUCK literal string. + + Arguments: + formats: + All of the YUCK literal format strings. + attributes: + Details about the currently sent notification like summary, body, appname, etc. + + Returns: + A str that is a primed YUCK literal with passed attributes. + + Example: + Format: + (_cardimage :identity ':::###::::XXXWWW%(id)s===::' + :close_action 'scripts/logger.py rmid %(id)s' + :limit_body '%(BODY_LIMITER)s' + :limit_summary '%(SUMMARY_LIMITER)s' + :summary '%(summary)s' + :body '%(body)s' + :close '繁' + :image_height 100 + :image_width 100 + :image '%(iconpath)s' + :appname '%(appname)s' + :icon '%(iconpath)s' + :icon_height 32 + :icon_width 32 + :timestamp '%(TIMESTAMP)s' + :urgency '%(URGENCY)s') + Primed: + (_cardimage :identity ':::###::::XXXWWW1658665761===::' + :close_action 'scripts/logger.py rmid 1658665761' + :limit_body '110' + :limit_summary '30' + :summary 'Picom' + :body 'The compositer is now disabled.' + :close '繁' + :image_height 100 + :image_width 100 + :image '/home/maker/.icons/custom/stock/128/custom-crying.png' + :appname 'Picom' + :icon '/home/maker/.icons/custom/stock/128/custom-crying.png' + :icon_height 32 + :icon_width 32 + :timestamp '17:59' + :urgency 'CRITICAL') + """ + + # assign the timestamp of the notification. + attributes["TIMESTAMP"] = datetime.datetime.now().strftime( + attributes["TIMESTAMP_FORMAT"] + ) + + # turn the urgency values (which are in bytes) to a more readable format (string). + match attributes["urgency"]: + case cache.Urgency.LOW: + attributes["URGENCY"] = "LOW" + case cache.Urgency.NORMAL: + attributes["URGENCY"] = "NORMAL" + case cache.Urgency.CRITICAL: + attributes["URGENCY"] = "CRITICAL" + case _: + attributes["URGENCY"] = "NORMAL" + + # handle next lines (especially discord code blocks) + # NOTE: may make this only discord / firefox specific + attributes["body"] = attributes["body"].replace("\n", " ") + attributes["summary"] = attributes["summary"].replace("\n", " ") + + # check if there are any pango tags on the body and summary and if so + # it will then remove it. + if utils.contains_pango(attributes["body"]): + attributes["body"] = utils.strip_pango_tags(attributes["body"]) + if utils.contains_pango(attributes["summary"]): + attributes["summary"] = utils.strip_pango_tags(attributes["summary"]) + + # it will replace all of the apostrophes with \' i.e. escape them. + if "'" in attributes["body"]: + attributes["body"] = attributes["body"].replace("'", "\\'") + if "'" in attributes["summary"]: + attributes["summary"] = attributes["summary"].replace("'", "\\'") + + # only 15 chars would be taken if the summary has non-english charecters. + attributes["SUMMARY_LIMITER"] = "" + summary_lang_char_check = utils.has_non_english_chars(attributes["summary"][:15]) + # if summary has asian characters like Japanese, Hindi or, Chinese + if summary_lang_char_check["CJK"]: + attributes["SUMMARY_LIMITER"] = 14 + # if summary has Cyrillic characters + elif summary_lang_char_check["CYR"]: + attributes["SUMMARY_LIMITER"] = 30 + + attributes["BODY_LIMITER"] = "" + body_lang_char_check = utils.has_non_english_chars(attributes["body"][:70]) + if body_lang_char_check["CJK"]: + attributes["BODY_LIMITER"] = 80 + elif body_lang_char_check["CYR"]: + attributes["BODY_LIMITER"] = 110 + else: + attributes["BODY_LIMITER"] = 100 + + # redirect to handlers + match attributes["appname"]: + case "notify-send": + return notify_send_handler(formats, attributes) + case "volume": + return volume_handler(formats, attributes) + case "backlight": + return brightness_handler(formats, attributes) + case "shot": + return shot_handler(formats, attributes) + case "shot_icon": + return shot_icon_handler(formats, attributes) + case "todo": + return todo_handler(formats, attributes) + case "Spotify": + return Spotify_handler(formats, attributes) + case _: + return default_handler(formats, attributes) + + +# TODO: Segregate redir_to_handlers into more utility / helper functions. +# NOTE: Currently it is a bit hard to utilize config values without making the code dirtier. +# NOTE: For instance shot_handler will eventually load the command strings from the config JSON for attributes["DELETE"]. See logger.py +def shot_handler(formats, attributes: dict) -> str: + """Handler for screenshot related notifications. + + Note that this handler will only handle the screenshots itself. + That is, it won't handle it if say.. the screenshot is copied to the clipboard, etc. + All of those are handled by shot_icon_handler. + + Arguments: + formats: See redir_to_handlers. + attributes: See redir_to_handlers. + + Returns: + See redir_to_handlers + """ + # TODO: Make this better + attributes[ + "DELETE" + ] = f"rm --force \\'{attributes['iconpath']}\\' && scripts/logger.py rmid {attributes['id']}" + attributes["OPEN"] = f"xdg-open \\'{attributes['iconpath']}\\'" + + # capitalize words i.e. notify_send -> Notify Send + attributes["appname"] = utils.prettify_name(attributes["appname"]) + return formats["shot"] % attributes + + +def Spotify_handler(formats, attributes: dict) -> str: + """Handler for notifications related to the official electron client for Spotify. + + Arguments: + formats: See redir_to_handlers. + attributes: See redir_to_handlers. + + Returns: + See redir_to_handlers + """ + return formats["Spotify"] % attributes + + +def default_handler(formats, attributes: dict) -> str: + r"""Handler for basic notifications. The notifications that are ordinary. + Or, rather the notifications that do not match any of the match-cases in + the redir_to_handlers function. + + Arguments: + formats: See redir_to_handlers. + attributes: See redir_to_handlers. + + Example: + notify-send Hello + notify-send Greetings + notify-send -u low -i bell Greetings Ding\! + notify-send -a appname-does-not-exist -i bell Yo + + Returns: + See redir_to_handlers + """ + attributes["appname"] = utils.prettify_name(attributes["appname"]) + return formats["default"] % attributes + + +def notify_send_handler(formats, attributes: dict) -> str: + """Handler for notifications related to the notify-send command. + + See: + default_handler + + Arguments: + formats: See redir_to_handlers. + attributes: See redir_to_handlers. + + Returns: + See redir_to_handlers + """ + attributes["appname"] = utils.prettify_name(attributes["appname"]) + return formats["notify-send"] % attributes + + +def brightness_handler(formats, attributes: dict) -> str: + """Handler for notifications related to brightness control. + + Arguments: + formats: See redir_to_handlers. + attributes: See redir_to_handlers. + + Returns: + See redir_to_handlers + """ + attributes["appname"] = utils.prettify_name(attributes["appname"]) + return formats["brightness"] % attributes + + +def volume_handler(formats, attributes: dict) -> str: + """Handler for notifications related to volume control. + + Arguments: + formats: See redir_to_handlers. + attributes: See redir_to_handlers. + + Returns: + See redir_to_handlers + """ + attributes["appname"] = utils.prettify_name(attributes["appname"]) + return formats["volume"] % attributes + + +# TODO: Make this general purpose and not just todo specific. +def todo_handler(formats, attributes: dict) -> str: + """Handler for notifications related to todo-bin CLI application by Siddomy. + + The notification body needs to be in a particular format in order for it to register. + That is: tasks done and are remaining. + + The fragments and will be picked up by this handler. + + Arguments: + formats: See redir_to_handlers. + attributes: See redir_to_handlers. + + Returns: + See redir_to_handlers + """ + splitted = attributes["body"].split(" ") + attributes["TOTAL"] = int(splitted[4]) + attributes["DONE"] = int(splitted[0]) + + # handle division by zero + attributes["PERC"] = ( + (attributes["DONE"] / attributes["TOTAL"]) * 100 + if attributes["DONE"] > 0 + else 0 + ) + + attributes["appname"] = utils.prettify_name(attributes["appname"]) + return formats["todo"] % attributes + + +def shot_icon_handler(formats, attributes: dict) -> str: + """Almost same as default_handler only just it uses a different icon. + + Redundant, but still nice to if you want to add additional + functionalities on this particular appname. + + See: + default_handler + shot_handler + + Arguments: + formats: See redir_to_handlers. + attributes: See redir_to_handlers. + + Returns: + See redir_to_handlers + """ + attributes["appname"] = utils.prettify_name(attributes["appname"]) + return formats["shot_icon"] % attributes + + +# vim:filetype=python diff --git a/widgets/noti/scripts/logger.py b/widgets/noti/scripts/logger.py new file mode 100755 index 0000000..be0f544 --- /dev/null +++ b/widgets/noti/scripts/logger.py @@ -0,0 +1,159 @@ +#!/usr/bin/env --split-string=python -u +"""Script for logging desktop notifications in the form of YUCK literal. + +General idea is to have a file log the notifications in the form of YUCK +literal containing a widget structure which will then be concatenated into +a box widget to take a list-like structure. + +The said structure needs to be re-rendered whenever the log file notices +a change. Like, deleting an entry or, adding an entry or, editing an entry. + +Note, if you still have not guessed already, if you make any changes to the +log file then the list of notifications will be re-rendered again. +""" + +# Authored By dharmx under: +# GNU GENERAL PUBLIC LICENSE +# Version 3, 29 June 2007 +# +# Copyright (C) 2007 Free Software Foundation, Inc. +# Everyone is permitted to copy and distribute verbatim copies +# of this license document, but changing it is not allowed. +# +# Permissions of this strong copyleft license are conditioned on +# making available complete source code of licensed works and +# modifications, which include larger works using a licensed work, +# under the same license. Copyright and license notices must be +# preserved. Contributors provide an express grant of patent rights. +# +# Read the complete license here: +# + +# TODO: Utilize JSON instead of YUCK Literals. +# WARN: This script is under active development and is subject to frequent changes. +# WARN: Currently this script is quite inefficient. So, you have been warned. + +import json +import os +import pathlib +import sys +from threading import main_thread + +import cache +import handlers +import subprocess +import utils + +# presetted notification format strings i.e. they are just utility items that help reducing the size of code +# another reason is convenience i.e. it would be easier to have all the formats in one place and defining them +# would be less cumbersome. +FORMATS = { + "spotifyd": "(cardimage :identity ':::###::::XXXWWW%(id)s===::' :close_action 'scripts/logger.py rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '' :image_height 50 :image_width 50 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s')", + "ncspot": "(cardimage :identity ':::###::::XXXWWW%(id)s===::' :close_action 'scripts/logger.py rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '' :image_height 50 :image_width 50 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s')", + "Spotify": "(cardimage :class 'Spotify-rectangle' :identity ':::###::::XXXWWW%(id)s===::' :close_action 'scripts/logger.py rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '' :image_height 50 :image_width 50 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s')", + "shot_icon": "(cardimage :class 'shot-image-bord-dashed' :identity ':::###::::XXXWWW%(id)s===::' :close_action 'scripts/logger.py rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '' :image_height 50 :image_width 50 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s')", + "shot": "(cardscr :identity ':::###::::XXXWWW%(id)s===::' :close_action 'scripts/logger.py rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :delete '%(DELETE)s' :open '%(OPEN)s' :summary '%(summary)s' :image '%(iconpath)s' :image_height 250 :image_width 100 :urgency '%(URGENCY)s' :close '' :timestamp '%(TIMESTAMP)s')", + "default": "(cardimage :identity ':::###::::XXXWWW%(id)s===::' :close_action 'scripts/logger.py rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '' :image_height 50 :image_width 50 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s')", + "notify-send": "(cardimage :identity ':::###::::XXXWWW%(id)s===::' :close_action 'scripts/logger.py rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '' :image_height 50 :image_width 50 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s')", + "empty": "(box :class 'notifications-empty-box' :height 750 :orientation 'vertical' :space-evenly false (image :class 'notifications-empty-banner' :valign 'end' :vexpand true :path 'images/wedding-bells.png' :image-width 250 :image-height 250) (label :vexpand true :valign 'start' :wrap true :class 'notifications-empty-label' :text '%(QUOTE)s'))", + "brightness": "(cardprog :identity ':::###::::XXXWWW%(id)s===::' :close_action 'scripts/logger.py rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '' :image_height 50 :image_width 50 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s' :progress '%(progress)s')", + "todo": "(cardradial :identity ':::###::::XXXWWW%(id)s===::' :close_action 'scripts/logger.py rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '' :appname '%(appname)s' :progress %(PERC)s :thickness 20.0 :total %(TOTAL)s :done %(DONE)s :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s')", + "volume": "(cardprog :identity ':::###::::XXXWWW%(id)s===::' :close_action 'scripts/logger.py rmid %(id)s' :limit_body '%(BODY_LIMITER)s' :limit_summary '%(SUMMARY_LIMITER)s' :summary '%(summary)s' :body '%(body)s' :close '' :image_height 50 :image_width 50 :image '%(iconpath)s' :appname '%(appname)s' :icon '%(iconpath)s' :icon_height 32 :icon_width 32 :timestamp '%(TIMESTAMP)s' :urgency '%(URGENCY)s' :progress '%(progress)s')", +} + +spotify_song_name = str( + subprocess.run( + ["playerctl", "metadata", "title", "-p", "spotify"], capture_output=True + ).stdout +)[2:-3] + +if __name__ == "__main__": + # load only notification related options from the config JSON + config = json.loads( + pathlib.PosixPath( + os.path.expandvars("$HOME/.config/eww/.config.json") + ).read_text() + )["notify"] + + # kind of like the sliding window algorithm i.e. will pop the notifications if this number is reached + HISTORY_LIMIT = config["limit"] + # file path where the notifications will be saved + CACHE_PATH = os.path.expandvars(config["cache_path"]) + # directory path where the notifications will be saved + # WARN: Do not edit this; Only edit this if you know what you are doing! + CACHE_DIR = os.path.dirname(CACHE_PATH) + # file path where the quotes are stored + QUOTE_PATH = os.path.expandvars(config["quote_path"]) + # fallback if the quote DB has no quotes in them + DEFAULT_QUOTE = config["default_quote"] + # Watcher interval; Reflects how fast notifications will be rendered. + INTERVAL = config["interval"] + + # handle IndexError + if len(sys.argv) < 2: + sys.argv = ("dummy", "dummy") + match sys.argv[1]: + case "subscribe": + utils.create_parents_file(CACHE_PATH) # mkdir --parents + utils.create_parents_file(QUOTE_PATH) # mkdir --parents + utils.watcher( + CACHE_PATH, + lambda contents: sys.stdout.write( + "(box :spacing 20 :orientation 'vertical' :space-evenly false " + + # handle empty and display fallback + contents.replace("\n", " ") + ")\n" + if contents.strip() + else ( + (FORMATS["empty"] + "\n") + % {"QUOTE": utils.get_rand_quote(QUOTE_PATH, DEFAULT_QUOTE)} + ) + ), + INTERVAL, + ) + case "rmid": + # grep based off the notification id and then remove that YUCK literal entry from the log file + utils.file_matched_index_rm( + CACHE_PATH, f":identity ':::###::::XXXWWW{sys.argv[2]}===::'" + ) + case "stats": + sys.stdout.write( + json.dumps( + utils.parse_and_print_stats( + pathlib.PosixPath(CACHE_PATH).read_text() + ) + ) + + "\n" + ) + case "rm": + utils.file_rm_line(CACHE_PATH, int(sys.argv[2])) + case "quote": + sys.stdout.write(utils.get_rand_quote(QUOTE_PATH, DEFAULT_QUOTE)) + case "cls": + pathlib.PosixPath(CACHE_PATH).write_text("") + case "init": + + def master_callback(details: dict): + r"""Callback function that handles fetching and logging the notification details. + + Arguments: + details: the JSON that should have a similar structure to the following: + { + "summary": "Hello", + "body": "Who's there?", + "id": 16444442, + "urgency": "LOW" + } + """ + details["TIMESTAMP_FORMAT"] = config["timestamp"] + if ( + not config["excluded_appnames"] + or details["appname"] not in config["excluded_appnames"] + ): + saved_path = handlers.redir_to_handlers(FORMATS, details) + # actual point where the notification is being logged. + utils.file_add_line(CACHE_PATH, saved_path, HISTORY_LIMIT) + + # start eavesdropping on the org.freedesktop.Notifications interface and log the notification info + cache.Eavesdropper(master_callback, CACHE_DIR).eavesdrop() + +# vim:filetype=python diff --git a/widgets/noti/scripts/monitor_logger.py b/widgets/noti/scripts/monitor_logger.py new file mode 100755 index 0000000..1dbf342 --- /dev/null +++ b/widgets/noti/scripts/monitor_logger.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +import subprocess +import os + + +def launch_logger(): + logger_path = os.path.join("scripts", "logger.py") + try: + # Check if logger.py is already running + subprocess.check_output(["pidof", "-x", "python", logger_path]) + print("logger.py is already running") + except subprocess.CalledProcessError: + # logger.py is not running, launch it + subprocess.Popen(["python", logger_path, "init", "&"]) + print("logger.py has been launched") + + +if __name__ == "__main__": + launch_logger() diff --git a/widgets/noti/scripts/quotes b/widgets/noti/scripts/quotes new file mode 100755 index 0000000..1afc860 --- /dev/null +++ b/widgets/noti/scripts/quotes @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# Author: github.com/NeshHari + +import random +import sys + +quotes = [ + "Be the change you wish to see in the world. ~ Mahatma Gandhi", + "I have a dream. ~ Martin Luther King Jr.", + "Not all those who wander are lost. ~ J.R.R. Tolkien", + "All that we see or seem is but a dream within a dream. ~ Edgar Allan Poe", + "To be yourself in a world that is constantly trying to make you something else is the greatest accomplishment. ~ Ralph Waldo Emerson", + "The only way to do great work is to love what you do. ~ Steve Jobs", + "We are what we repeatedly do. Excellence, then, is not an act, but a habit. ~ Aristotle", + "In three words I can sum up everything I've learned about life: it goes on. ~ Robert Frost", + "The best and most beautiful things in the world cannot be seen or even touched - they must be felt with the heart. ~ Helen Keller", + "It does not matter how slowly you go as long as you do not stop. ~ Confucius", + "Believe you can and you're halfway there. ~ Theodore Roosevelt", + "The unexamined life is not worth living. ~ Socrates", + "In the end, we will remember not the words of our enemies, but the silence of our friends. ~ Martin Luther King Jr.", + "Success is not final, failure is not fatal: it is the courage to continue that counts. ~ Winston Churchill", + "You miss 100% of the shots you don't take. ~ Wayne Gretzky", + "If you want to go fast, go alone. If you want to go far, go together. ~ African proverb", + "You can't build a reputation on what you are going to do. ~ Henry Ford", + "The only thing we have to fear is fear itself. ~ Franklin D. Roosevelt", + "The journey of a thousand miles begins with one step. ~ Lao Tzu", + "A house divided against itself cannot stand. ~ Abraham Lincoln", + "Ask not what your country can do for you, ask what you can do for your country. ~ John F. Kennedy", + "I am not a product of my circumstances. I am a product of my decisions. ~ Stephen Covey", + "Our greatest glory is not in never falling, but in rising every time we fall. ~ Confucius", + "Education is the most powerful weapon which you can use to change the world. ~ Nelson Mandela", + "It always seems impossible until it's done. ~ Nelson Mandela", + "The only true wisdom is in knowing you know nothing. ~ Socrates", + "The best way to predict your future is to create it. ~ Abraham Lincoln", + "The future belongs to those who believe in the beauty of their dreams. ~ Eleanor Roosevelt", + "Believe in yourself and all that you are. Know that there is something inside you that is greater than any obstacle. ~ Christian D. Larson", + "The true test of leadership is how well you function in a crisis. ~ Brian Tracy", + "Change your thoughts and you change your world. ~ Norman Vincent Peale", + "The way to get started is to quit talking and begin doing. ~ Walt Disney", + "Don't wait for opportunities, create them. ~ Unknown", + "You must be the change you wish to see in the world. ~ Mahatma Gandhi", + "Happiness can be found, even in the darkest of times, if one only remembers to turn on the light. ~ Albus Dumbledore", + "Life is a journey, and if you fall in love with the journey, you will be in love forever. ~ Peter Hagerty", + "The only thing we have to fear is fear itself. ~ Franklin D. Roosevelt", + "All our dreams can come true, if we have the courage to pursue them. ~ Walt Disney", + "The only limit to our realization of tomorrow will be our doubts of today. ~ Franklin D. Roosevelt", + "You are not a drop in the ocean. You are the entire ocean in a drop. ~ Rumi", + "The best way to find yourself is to lose yourself in the service of others.~ Mahatma Gandhi", + "Life is 10% what happens to us and 90% how we react to it. ~ Charles R. Swindoll", + "The greatest glory in living lies not in never falling, but in rising every time we fall. ~ Nelson Mandela", + "The purpose of our lives is to be happy. ~ Dalai Lama", + "The most wasted of all days is one without laughter. ~ E.E. Cummings", + "I can't change the direction of the wind, but I can adjust my sails to always reach my destination. ~ Jimmy Dean", + "You have within you right now, everything you need to deal with whatever the world can throw at you. ~ Brian Tracy", + "The greatest wealth is to live content with little. ~ Plato", + "It's not about how hard you hit. It's about how hard you can get hit and keep moving forward. ~ Rocky Balboa", + "The biggest adventure you can ever take is to live the life of your dreams. ~ Oprah Winfrey", + "The best way to predict the future is to create it. ~ Peter Drucker", + "Don't be pushed around by the fears in your mind. Be led by the dreams in your heart. ~ Roy T. Bennett", +] + + +last_quote = "" +quote = random.choice(quotes) +while quote == last_quote: + quote = random.choice(quotes) +quote_parts = quote.split("~") +last_quote = quote_parts[0] + "~" + quote_parts[1] + +if len(sys.argv) >= 2: + if sys.argv[1] == "--quote-and-quotee": + print(quote_parts[0] + "\n\n~" + quote_parts[1]) diff --git a/widgets/noti/scripts/utils.py b/widgets/noti/scripts/utils.py new file mode 100755 index 0000000..1109ed2 --- /dev/null +++ b/widgets/noti/scripts/utils.py @@ -0,0 +1,469 @@ +#!/usr/bin/env python + +"""Utility module. Shared across almost all of the python scripts / modules.""" + +# Authored By dharmx under: +# GNU GENERAL PUBLIC LICENSE +# Version 3, 29 June 2007 +# +# Copyright (C) 2007 Free Software Foundation, Inc. +# Everyone is permitted to copy and distribute verbatim copies +# of this license document, but changing it is not allowed. +# +# Permissions of this strong copyleft license are conditioned on +# making available complete source code of licensed works and +# modifications, which include larger works using a licensed work, +# under the same license. Copyright and license notices must be +# preserved. Contributors provide an express grant of patent rights. +# +# Read the complete license here: +# + +import json +import os +import pathlib +import random +import re +import sys +import time +import typing +import unicodedata + +# from wand.image import COLORSPACE_TYPES, Image +from html.parser import HTMLParser +from io import StringIO + +import dbus +import gi + +# supress GIO warnings +gi.require_version("Gtk", "3.0") +gi.require_version("GdkPixbuf", "2.0") + +import requests +from gi.repository import GdkPixbuf, Gio, GLib, Gtk + + +class PangoStripper(HTMLParser): + def __init__(self): + super().__init__() + self.reset() + self.strict = False + self.convert_charrefs = True + self.text = StringIO() + + def handle_data(self, d): + self.text.write(d) + + def get_data(self): + return self.text.getvalue() + + +def contains_pango(string: str) -> bool: + """Checks if a string contains HTML tags or not. Since a pango string has to have at least two tags + + Arguments: + string: The Pango markup format string. + + Returns: + A bool value i.e. whether a has pango markup or not. + """ + return any(item in string for item in ["", "", ""]) + + +def strip_pango_tags(pango: str) -> str: + """Removes HTML tags like , , etc. + + Arguments: + pango: The Pango format string that needs to be stripped of its tags. + + Returns: + A str without the tags. + """ + stripper = PangoStripper() + stripper.feed(pango) + return stripper.get_data() + + +def create_parents_file(file_path: str): + """A replication of [ -f "$FILE_PATH" ] || mkdir --parents "$FILE_PATH" + + Arguments: + file_path: The location of the file. + """ + pathlib.PosixPath(os.path.dirname(file_path)).mkdir(parents=True, exist_ok=True) + pathlib.PosixPath(file_path).touch(exist_ok=True) + + +def watcher( + file_path: str, callback: typing.Callable, interval: int, initial: bool = True +): + """A file watcher function that executes a callback function if any change is made to a file. + + Arguments: + file_path: The file location that needs to be monitored. + callback: A callback function that would be invoked when any change is noticed. + interval: The time rate at which the file will be checked for changes. + initial: Initially execute the callback once before entering the watch loop. + """ + try: + old = pathlib.PosixPath(file_path).read_text() + if initial: + callback(old) + while not time.sleep(interval): + new = pathlib.PosixPath(file_path).read_text() + if new != old: + callback(new) + old = new + except KeyboardInterrupt: + sys.stdout.write("Closed.\n") + except FileNotFoundError: + sys.stderr.write("The path does not exist!\n") + except Exception as excep: + sys.stderr.write(f"{excep}\n") + + +def get_rand_quote(file_path: str, default_quote: str) -> str: + """Get a random quote or, rather a random line. + + Arguments: + file_path: The location of the file. + default_quote: Fallback quote if the file doesn't exist r, is empty. + """ + loaded_quotes: str = pathlib.PosixPath(file_path).read_text().strip() + return random.choice(loaded_quotes.splitlines()) if loaded_quotes else default_quote + + +def file_matched_index_rm(file_path: str, pattern: str): + """Removes line from a file if a pattern matches that line. + + Arguments: + file_path: The location of the file. + pattern: Pattern to match the line that needs to be removed. + """ + posix_file_path = pathlib.PosixPath(file_path) + lines = posix_file_path.read_text().splitlines() + + # skip the line if pattern matches + rm_index_lines = [ + lines[index] + for index in range(len(lines)) + if not re.search(pattern, lines[index]) + ] + + if len(lines) != len(rm_index_lines): + posix_file_path.write_text("\n".join(rm_index_lines)) + + +def file_rm_line(file_path: str, position: int | bool | range = True): + """Ranged line removal, indexed line removal and stack-like (pop / reverse pop) line removal. + + Arguments: + file_path: The location of the file. + position: If the value is of type: + range: then that the lines starting from that range will be removed. + int: then that specific file line number will be removed. + bool: True will pop and False will pop from the end. + """ + file = pathlib.PosixPath(file_path) + match str(type(position)): + case "": + file_contents = file.read_text().splitlines() + # no need to run the loop if all you need to do is remove the first line + if position == 0: + file_rm_line(file_path, position=True) + return + elif position == len(file_contents) - 1: + file_rm_line(file_path, position=False) + return + line_removed_contents = [] + # skip the line that matches the position value. + for index in range(len(file_contents)): + if index != position: + line_removed_contents += [file_contents[index]] + file.write_text("\n".join(line_removed_contents)) + # filter falsey and truthy and use the actual bool class signature + case "": + file_contents = file.read_text().splitlines() + file_contents = file_contents[1:] if position else file_contents[:-1] + file.write_text("\n".join(file_contents)) + case "": + if not position: + file.write_text("") + return + file_contents = file.read_text().splitlines() + write_contents = [] + # keep on skipping the lines if they are in the range. + for index in range(len(file_contents)): + if index not in position: + write_contents += [file_contents[index]] + file.write_text("\n".join(write_contents)) + + +def prettify_name(name: str) -> str: + """Transforms 'Hello-World' -> 'Hello World', 'notify-send++lol' -> 'Notify Send Lol' + + Arguments: + name: The string that needs to be prettified. + + Returns: + A prettified str. + """ + return " ".join( + item.capitalize() + for item in name.replace("-", " ").replace("_", " ").split(" ") + ) + + +def file_add_line(file_path: str, write_contents: str, limit: int, top: bool = True): + """Functions like sliding window algorithm + + That is after the file hits a certain line limit, lines from the behind will be dropped (older entries) + and THEN the newer ones will be entered. + + Arguments: + file_path: The location of the file. + write_contents: The contents that needs to be written to the file. + limit: The line limit. + top: Adds new entry at the beginning of the file if True, at the end otherwise. + """ + file = pathlib.PosixPath(file_path) + file_contents = file.read_text().splitlines() + if len(file_contents) == limit: + file_contents = file_contents[:-1] + file_contents = ( + [write_contents] + file_contents if top else file_contents + [write_contents] + ) + file.write_text("\n".join(file_contents)) + + +def parse_and_print_stats(file_contents: str) -> dict: + """Looks the words CRITICAL, LOW and NORMAL and calcuates its frequency. + + Arguments: + file_contents: the string that needs to be calculated for frequency. + + Returns: + Individual frequency of the words in dict format. + {"CRITICAL": 10.00, "NORMAL": 85.00, "LOW": 5.00} + """ + stats = {"critical": 0, "low": 0, "normal": 0, "total": 0} + + for line in file_contents.splitlines(): + if "CRITICAL" in line: + stats["critical"] += 1 + stats["total"] += 1 + elif "LOW" in line: + stats["low"] += 1 + stats["total"] += 1 + elif "NORMAL" in line: + stats["normal"] += 1 + stats["total"] += 1 + + # handle division / zero + stats["critical"] = ( + stats["critical"] * 100 / stats["total"] if stats["critical"] > 0 else 0 + ) + stats["normal"] = ( + stats["normal"] * 100 / stats["total"] if stats["normal"] > 0 else 0 + ) + stats["low"] = stats["low"] * 100 / stats["total"] if stats["low"] > 0 else 0 + return stats + + +def has_non_english_chars(string: str) -> dict: + """Check if there is any CJK / Cyrillic characters in the given string. + + Arguments: + string: the string that needs to be checked. + + Returns: + A dict containing True / False for keys representing if such characters exist or not. + {"CJK": True, "CYR": False} for string value: "おはようございます means Good Morning!" + """ + return { + "CJK": any(unicodedata.category(char) == "Lo" for char in string), + "CYR": any(unicodedata.category(char) == "Lu" for char in string), + } + + +def unwrap( + value: dbus.Array + | dbus.Boolean + | dbus.Byte + | dbus.Dictionary + | dbus.Double + | dbus.Int16 + | dbus.ByteArray + | dbus.Int32 + | dbus.Int64 + | dbus.Signature + | dbus.UInt16 + | dbus.UInt32 + | dbus.UInt64 + | dbus.String, +) -> str | int | list | tuple | float | dict | bool | bytes: + """Try to trivially translate a dictionary's elements into nice string formatting. + + Arguments: + value: A type out of: + dbus.Boolean, + dbus.Byte, + dbus.Dictionary, + dbus.Double, + dbus.Int16, + dbus.ByteArray, + dbus.Int32, + dbus.Int64, + dbus.Signature, + dbus.UInt16, + dbus.UInt32, + dbus.UInt64 and dbus.String + + Returns: + A str or int or list or tuple or float or dict or bool or bytes depending on the value. + """ + if isinstance(value, dbus.ByteArray): + return "".join([str(byte) for byte in value]) + if isinstance(value, (dbus.Array, list, tuple)): + return [unwrap(item) for item in value] + if isinstance(value, (dbus.Dictionary, dict)): + return dict([(unwrap(x), unwrap(y)) for x, y in value.items()]) + if isinstance(value, (dbus.Signature, dbus.String)): + return str(value) + if isinstance(value, dbus.Boolean): + return bool(value) + if isinstance( + value, + (dbus.Int16, dbus.UInt16, dbus.Int32, dbus.UInt32, dbus.Int64, dbus.UInt64), + ): + return int(value) + if isinstance(value, dbus.Byte): + return bytes([int(value)]) + return value + + +def save_img_byte(px_args: typing.Iterable, save_path: str): + """Converts image data to an image file. + + See for the whole description. + + Arguments: + px_args: Should contain an iterable in the following format. + [image_width, image_height, rowstride, has_alpha, bits_per_sample, _, image_bytes] + save_path: the filepath where this pixbuf should be saved to. + """ + # https://specifications.freedesktop.org/notification-spec/latest/ar01s08.html + # https://specifications.freedesktop.org/notification-spec/latest/ar01s05.html + GdkPixbuf.Pixbuf.new_from_bytes( + width=px_args[0], + height=px_args[1], + has_alpha=px_args[3], + data=GLib.Bytes(px_args[6]), + colorspace=GdkPixbuf.Colorspace.RGB, + rowstride=px_args[2], + bits_per_sample=px_args[4], + ).savev(save_path, "png") + + +def get_gtk_icon_path(icon_name: str, size: int = 128) -> str: + """Returns the icon path by the specified name from your current icon theme. + + Recursively search for lower sizes until 32x32 and + return a fallback if the requested path does not exists. + + Arguments: + icon_name: Icon name as per the icon naming specification: + + size: the pixel size (widthxheight -> heightxheight -> size) of the requested icon. + """ + if size < 32: + return os.path.expandvars( + "/usr/share/icons/Papirus-Dark/48x48/status/dialog-information.svg" + ) + if info := Gtk.IconTheme.get_default().lookup_icon(icon_name, size, 0): + return info.get_filename() + return get_gtk_icon_path(icon_name, size - 1) + + +def get_mime_icon_path(mimetype: str, size: int = 32) -> str: + """Gets the default icon path from the current GTK icon theme for the specified mime type. + + Arguments: + mimetype: The file type like png, json, etc. + size: The of the returned icon + + Returns: + A str that is the path to the requested icon. + """ + icon = Gio.content_type_get_icon(mimetype) + theme = Gtk.IconTheme.get_default() + if info := theme.choose_icon(icon.get_names(), size, 0): + return info.get_filename() + + +def get_location() -> dict | None: + try: + response = requests.get("https://api64.ipify.org?format=json").json() + ip_address = response["ip"] + response = requests.get(f"https://ipapi.co/{ip_address}/json/").json() + return { + "latitude": response.get("latitude"), + "longitude": response.get("longitude"), + "city": response.get("city"), + "country": response.get("country_name"), + "lang": response.get("languages").split(",")[0], + } + except requests.exceptions.ConnectionError: + return None + + +def auto_locate(cache_dir: str) -> dict | None: + cache_posix_path = pathlib.PosixPath(f"{cache_dir}/location.json") + if not cache_posix_path.is_file(): # assuming the directory exists + fetched_location = get_location() + if not fetched_location: + return None + + cache_posix_path.write_text(json.dumps(fetched_location)) + return fetched_location + return json.loads(cache_posix_path.read_text()) + + +def fetch_save(link: str, save_path: str, callback: typing.Callable = None) -> bool: + try: + data = requests.get(link) + if data.status_code == 200: + metadata = data.json() + if callback: + metadata = callback(metadata) + pathlib.PosixPath(save_path).write_text(json.dumps(metadata)) + return True + return False + except requests.exceptions.ConnectionError: + return False + + +def img_dark_bright_col(filepath: str, colors: int = 10) -> tuple: + with Image(filename=filepath) as image: + image.quantize( + number_colors=colors, + colorspace_type=COLORSPACE_TYPES[21], + dither=True, + measure_error=False, + treedepth=8, + ) + return tuple( + "#%02X%02X%02X" % (item.red_int8, item.green_int8, item.blue_int8) + for item in image.histogram + ) + + +if __name__ == "__main__": + match sys.argv[1]: + case "histogram": + image_path = sys.argv[2] + histogram_colors = int(sys.argv[3]) + print(json.dumps(img_dark_bright_col(image_path, histogram_colors))) + +# vim:filetype=python diff --git a/widgets/scripts/bluetooth b/widgets/scripts/bluetooth new file mode 100755 index 0000000..e735c62 --- /dev/null +++ b/widgets/scripts/bluetooth @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +get_state () { + state=$(bluetoothctl show | grep 'Powered' | awk '{print $2}') + if [[ $state == "yes" ]]; then + echo on + else + echo off + fi +} + +turn_off () { + bluetoothctl power off 2>&1 > /dev/null +} + +turn_on () { + bluetoothctl power on 2>&1 > /dev/null +} + +toggle () { + state=$(get_state) + if [[ $state == "on" ]]; then + turn_off + else + turn_on + fi +} + +if [[ $1 == "state" ]]; then + get_state +fi + +if [[ $1 == "toggle" ]]; then + toggle +fi diff --git a/widgets/scripts/brightness.sh b/widgets/scripts/brightness.sh new file mode 100755 index 0000000..a14734c --- /dev/null +++ b/widgets/scripts/brightness.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# @requires: brightnessctl + +percentage () { + local val=$(echo $1 | tr '%' ' ' | awk '{print $1}') + local icon1=$2 + local icon2=$3 + local icon3=$4 + local icon4=$5 + if [ "$val" -le 15 ]; then + echo $icon1 + elif [ "$val" -le 30 ]; then + echo $icon2 + elif [ "$val" -le 60 ]; then + echo $icon3 + else + echo $icon4 + fi +} + +get_brightness () { + (( br = $(brightnessctl get) * 100 / $(brightnessctl max) )) + echo $br +} + +get_percent () { + echo $(get_brightness)% +} + +get_icon () { + local br=$(get_percent) + echo $(percentage "$br" "" "" "" "") +} + +if [[ $1 == "br" ]]; then + get_brightness +fi + +if [[ $1 == "percent" ]]; then + get_percent +fi + +if [[ $1 == "icon" ]]; then + get_icon +fi + +if [[ $1 == "set" ]]; then + light -S $2 +fi \ No newline at end of file diff --git a/widgets/scripts/cpu.sh b/widgets/scripts/cpu.sh new file mode 100755 index 0000000..0afb9e3 --- /dev/null +++ b/widgets/scripts/cpu.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +grep 'cpu ' /proc/stat | awk '{usage=($2+$4)*100/($2+$4+$5)} END {print usage "%"}' | tr '.' ' ' | awk '{print $1}' \ No newline at end of file diff --git a/widgets/scripts/disk.sh b/widgets/scripts/disk.sh new file mode 100755 index 0000000..5717c07 --- /dev/null +++ b/widgets/scripts/disk.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +df --output=pcent / | tail -n 1 | sed 's/%//g' | awk '{print $1}' \ No newline at end of file diff --git a/widgets/scripts/mic b/widgets/scripts/mic new file mode 100755 index 0000000..7c8dea0 --- /dev/null +++ b/widgets/scripts/mic @@ -0,0 +1,31 @@ +#!/bin/bash + +if [ "$1" = "status" ]; then + state=$(amixer sget Capture | grep "\[on\]") + if [ -n "$state" ]; then + echo "no" + else + echo "yes" + fi +elif [ "$1" = "toggle" ]; then + state=$(amixer sget Capture | grep "\[on\]") + if [ -n "$state" ]; then + amixer sset Capture nocap + echo "no" + else + amixer sset Capture cap + echo "yes" + fi +elif [ "$1" = "set" ] && [ "$2" -gt 0 ] && [ "$2" -le 100 ]; then + volume="$2%" + amixer sset Capture "${volume}" + echo "Volume set to ${volume}" +elif [ "$1" = "get" ]; then + (( mic_vol = $(amixer sget Capture | grep -oE '[0-9]+%' | tr -d '%') * 100 / 100 )) + echo $mic_vol +elif [ "$1" = "percent" ] && [ "$2" -gt 0 ] && [ "$2" -le 100 ]; then + amixer sset Capture "${2}%" + echo "Volume set to ${2}%" +else + echo "Usage: $0 [status|toggle|set|get|percent]" +fi diff --git a/widgets/scripts/network.sh b/widgets/scripts/network.sh new file mode 100755 index 0000000..c5a78f3 --- /dev/null +++ b/widgets/scripts/network.sh @@ -0,0 +1,100 @@ +#!/bin/bash + +symbol() { + [ $(cat /sys/class/net/w*/operstate) = down ] && echo 睊 && exit + echo  +} + +name() { + iwgetid -r +} + +cut () { + toshow="$1" + maxlen="$2" + + sufix="" + + if test $(echo $toshow | wc -c) -ge $maxlen ; then + sufix=" ..." + fi + + echo "${toshow:0:$maxlen}$sufix" +} + +[ "$1" = "icon" ] && symbol + +if [[ $1 == "ssid" ]]; then + ssid=$(name) + if [[ "$ssid" == "" ]]; then + cut "Disconnected" 10 + else + cut "$ssid" 10 + fi +fi + +if [[ $1 == "name" || $1 == "class" ]]; then + wifiname=$(name) + if [[ $wifiname == "" ]]; then + if [[ $1 == "name" ]]; then + echo "Disconnected" + elif [[ $1 == "class" ]]; then + echo "disconnected" + fi + else + if [[ $1 == "name" ]]; then + echo "Connected to $wifiname" + elif [[ $1 == "class" ]]; then + echo "connected" + fi + fi +fi + +if [[ $1 == "status" ]]; then + name=$(name) + if [[ $name != "" ]]; then + echo "Connected" + else + echo "Disconnected" + fi +fi + +if [[ $1 == "disconnect" ]]; then + wifiname=$(nmcli d | grep wifi | sed 's/^.*wifi.*connected//g' | xargs) + nmcli con down id "${wifiname}" +fi + +if [[ $1 == "connect" ]]; then + nmcli con up ifname "$(/usr/bin/ls /sys/class/ieee80211/*/device/net/)" +fi + +if [[ $1 == "toggle" ]]; then + name=$(name) + if [[ "$name" == "" ]]; then + nmcli radio wifi on + else + nmcli radio wifi off + fi +fi + +radio_status () { + radio_status=$(nmcli radio wifi) + if [[ $radio_status == "enabled" ]]; then + echo "on" + else + echo "off" + fi +} + +if [[ $1 == "radio-status" ]]; then + radio_status +fi + +if [[ $1 == "toggle-radio" ]]; then + stat=$(radio_status) + if [[ $stat == "on" ]]; then + nmcli radio wifi off + else + nmcli radio wifi on + fi +fi diff --git a/widgets/scripts/pfp.sh b/widgets/scripts/pfp.sh new file mode 100755 index 0000000..4b4bc1b --- /dev/null +++ b/widgets/scripts/pfp.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +declare -a profiles_photos=("$HOME/.face.jpg" "$HOME/.face.png") + +for pfp in ${profiles_photos[@]}; do + if test -f $pfp; then + echo $pfp + exit 0 + fi +done + +echo $HOME/.config/eww/images/pp.png diff --git a/widgets/scripts/ram.sh b/widgets/scripts/ram.sh new file mode 100755 index 0000000..a2f0065 --- /dev/null +++ b/widgets/scripts/ram.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +percentage () { + local val=$(echo $1 | tr '%' ' ' | awk '{print $1}') + local icon1=$2 + local icon2=$3 + local icon3=$4 + local icon4=$5 + if [ "$val" -le 15 ]; then + echo $icon1 + elif [ "$val" -le 30 ]; then + echo $icon2 + elif [ "$val" -le 60 ]; then + echo $icon3 + else + echo $icon4 + fi +} + +get_ram () { + free -m | grep Mem | awk '{print ($3/$2)*100}' | tr '.' ' ' | awk '{print $1}' +} + +get_percent () { + echo $(get_ram)% +} + +get_class () { + local percent=$(get_percent) + echo $(percentage "$percent" "yellow" "magenta" "purple" "red") +} + +if [[ $1 == "ram" ]]; then + get_ram +fi + +if [[ $1 == "percent" ]]; then + get_percent +fi + +if [[ $1 == "class" ]]; then + get_class +fi \ No newline at end of file diff --git a/widgets/scripts/redshift b/widgets/scripts/redshift new file mode 100755 index 0000000..38ce6a9 --- /dev/null +++ b/widgets/scripts/redshift @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +cache_file=$HOME/.cache/eww/services/redshift_state + +initial_hook () { + if [ ! -d $(dirname $cache_file) ]; then + mkdir -p $(dirname $cache_file) + fi + if [ ! -f $cache_file ]; then + echo off > $cache_file + fi +} + +get_state () { + # check if redshift is running + cat $cache_file +} + +restore () { + local state=$(get_state) + if [[ $state == "on" ]]; then + enable_redshift + else + disable_redshift + fi +} + +disable_redshift () { + redshift -x 2>&1 > /dev/null + # saving state + echo off > $cache_file +} + +enable_redshift () { + redshift -x 2>&1 > /dev/null + redshift -O 5000 2>&1 > /dev/null + # saving new state + echo on > $cache_file +} + +toggle () { + local state=$(get_state) + if [[ $state == "on" ]]; then + disable_redshift + else + enable_redshift + fi +} + +initial_hook + +if [[ $1 == "state" ]]; then + get_state +fi + +if [[ $1 == "enable" ]]; then + enable_redshift +fi + +if [[ $1 == "disable" ]]; then + disable_redshift +fi + +if [[ $1 == "toggle" ]]; then + toggle +fi + +if [[ $1 == "restore" ]]; then + restore +fi diff --git a/widgets/scripts/temp.sh b/widgets/scripts/temp.sh new file mode 100755 index 0000000..92be642 --- /dev/null +++ b/widgets/scripts/temp.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +# thanks to rxyhn + +path () { + local temp_path=null + for i in /sys/class/hwmon/hwmon*/temp*_input; + do + temp_path="$(echo "$(<$(dirname $i)/name): $(cat ${i%_*}_label 2>/dev/null || + echo $(basename ${i%_*})) $(readlink -f $i)");" + label="$(echo $temp_path | awk '{print $2}')" + if [ "$label" = "Package" ]; + then + echo ${temp_path} | awk '{print $5}' | tr -d ';\n' + exit; + fi + done +} + +get () { + local path=$(path) + if [[ $path == "" ]]; then + path="/sys/class/thermal/thermal_zone0/temp" + fi + + local max_temp=100 + local temp=$(cat $path) + + jq -n $(jq -n $temp/1000)/$max_temp*100 +} + +_ () { + ${@} + exit 0 +} + +if [[ $1 == "get" ]]; then + _ get +fi \ No newline at end of file diff --git a/widgets/scripts/user.sh b/widgets/scripts/user.sh new file mode 100755 index 0000000..29568c8 --- /dev/null +++ b/widgets/scripts/user.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +name () { + username=$(whoami) + echo ${username^} +} + +name \ No newline at end of file diff --git a/widgets/scripts/volume.sh b/widgets/scripts/volume.sh new file mode 100755 index 0000000..0c6b618 --- /dev/null +++ b/widgets/scripts/volume.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +# @requires: amixer + +SINK=$(amixer scontrols | grep -oP "'[^']*'" | sed "s/'//g" | head -n 1) + +percentage () { + local val=$(echo $1 | tr '%' ' ' | awk '{print $1}') + local icon1=$2 + local icon2=$3 + local icon3=$4 + local icon4=$5 + if [ "$val" -le 15 ]; then + echo $icon1 + elif [ "$val" -le 30 ]; then + echo $icon2 + elif [ "$val" -le 60 ]; then + echo $icon3 + else + echo $icon4 + fi +} + +is_muted () { + amixer get Master | grep -o "\[off\]" | head -n 1 +} + +get_percentage () { + local muted=$(is_muted) + if [[ $muted == '[off]' ]]; then + echo 0% + else + per=$(amixer get $SINK | grep -o '[0-9]*%' | head -n 1 | tr -d '%') + echo "${per}%" + fi +} + +get_icon () { + local vol=$(get_percentage) + if [[ $vol == "0%" ]]; then + echo "婢" + else + echo $(percentage "$vol" "" "" "墳" "") + fi +} + +get_class () { + local vol=$(get_percentage) + if [[ $vol == "0%" ]]; then + echo "red" + else + echo $(percentage "$vol" "red" "magenta" "yellow" "blue") + fi +} + +get_vol () { + local percent=$(get_percentage) + echo $percent | tr -d '%' +} + +if [[ $1 == "icon" ]]; then + get_icon +fi + +if [[ $1 == "class" ]]; then + get_class +fi + +if [[ $1 == "percentage" ]]; then + get_percentage +fi + +if [[ $1 == "vol" ]]; then + get_vol +fi + +if [[ $1 == "muted" ]]; then + is_muted +fi + +if [[ $1 == "toggle-muted" ]]; then + amixer set Master toggle +fi + +if [[ $1 == "set" ]]; then + val=$(echo $2 | tr '.' ' ' | awk '{print $1}') + if test $val -gt 100; then + val=100 + fi + amixer set $SINK $val% +fi diff --git a/widgets/scripts/workspaces b/widgets/scripts/workspaces new file mode 100755 index 0000000..bb74335 --- /dev/null +++ b/widgets/scripts/workspaces @@ -0,0 +1,73 @@ +#!/usr/bin/env bash + +get_ws_name () { + local entry=$1 + echo ${entry#?} +} + +get_class_name () { + local entry=$1 + echo ${entry} | sed 's/[0-9].*//g' +} + +get_shape_by_class () { + local class=$1 + local sfocused=$2 + local sinactive=$3 + local soccupied=$4 + local afocused=$5 + local ainactive=$6 + local aoccupied=$7 + + # check focused + for info in $afocused; do + if [[ "$info" == "$class" ]]; then + echo $sfocused + fi + done + + # check inactive + for info in $ainactive; do + if [[ "$info" == "$class" ]]; then + echo $sinactive + fi + done + + # check occupied + for info in $aoccupied; do + if [[ "$info" == "$class" ]]; then + echo $soccupied + fi + done +} + +workspaces () { + local info=$(echo $1 | sed 's/:/ /g') + local workspaces="(box :class \"ws\" :spacing \"-1\" :orientation \"h\" :halign \"center\" :space-evenly \"false\"" + + # define shapes + local sfocused="" + local sinactive="" + local soccupied="" + + # define shapes associations + local afocused="A I" # focused and focused without childs + local ainactive="i" # unoccupied / inactive + local aoccupied="a" # occupied + + # create buttons + for entry in $info; do + local name=$(get_ws_name $entry) + local class=$(get_class_name $entry) + local shape=$(get_shape_by_class "$class" "$sfocused" "$sinactive" "$soccupied" "$afocused" "$ainactive" "$aoccupied") + + workspaces="${workspaces}(button :onclick \"dkcmd ws ${name}\" :class \"$class btn\" \"${shape}\")" + done + + workspaces="$workspaces)" + echo $workspaces +} + +dkcmd status type=ws | while read -r line; do + workspaces "$line" +done diff --git a/widgets/variables.yuck b/widgets/variables.yuck new file mode 100644 index 0000000..3be954d --- /dev/null +++ b/widgets/variables.yuck @@ -0,0 +1,46 @@ +(defpoll network_status :interval "1s" "~/.config/polybar/widgets/scripts/network.sh status") +(defpoll network_icon :interval "1s" "~/.config/polybar/widgets/scripts/network.sh icon") +(defpoll network_radio :interval "1s" "~/.config/polybar/widgets/scripts/network.sh radio-status") +(defpoll network_ssid :interval "1s" "~/.config/polybar/widgets/scripts/network.sh ssid") + +(defpoll volume_icon :interval "1s" "~/.config/polybar/widgets/scripts/volume.sh icon") +(defpoll volume :interval "1s" "~/.config/polybar/widgets/scripts/volume.sh percentage") +(defpoll volume_value :interval "1s" "~/.config/polybar/widgets/scripts/volume.sh vol") +(defpoll is_muted :interval "1s" "~/.config/polybar/widgets/scripts/volume.sh muted") + +(defpoll mic :interval "1s" "~/.config/polybar/widgets/scripts/mic get") +(defpoll mic_status :interval "1s" "~/.config/polybar/widgets/scripts/mic status") + +(defpoll bluetooth_state :interval "1s" "~/.config/polybar/widgets/scripts/bluetooth state") +(defpoll redshift_state :interval "1s" "~/.config/polybar/widgets/scripts/redshift state") + +(defpoll pfp :interval "1s" "~/.config/polybar/widgets/scripts/pfp.sh") +(defpoll rawuser :interval "30s" "whoami") +(defpoll username :interval "30s" "~/.config/polybar/widgets/scripts/user.sh") +(defpoll hostname :interval "30s" "cat /etc/hostname") +(defpoll uptime :interval "5s" "uptime -p") + +(defpoll br_icon :interval "1s" "~/.config/polybar/widgets/scripts/brightness.sh icon") +(defpoll br :interval "1s" "~/.config/polybar/widgets/scripts/brightness.sh br") + + +(defpoll completeday :interval "1h" "date '+%A, %d %B'") +(defpoll lithour :interval "30m" "date +%I") +(defpoll litmin :interval "30s" "date +%M") +(defpoll calendar_day :interval "10h" "date '+%d'") +(defpoll calendar_month :interval "10h" "date '+%m") +(defpoll calendar_year :interval "10h" "date '+%Y'") +(defpoll date :interval "30s" "date '+%d %b, %I:%M %p'") +(defpoll shortday :interval "1h" "date '+%d %b %y'") +(defpoll weekday :interval "24h" "date +%A") + +(defpoll cpu :interval "3s" "~/.config/polybar/widgets/scripts/cpu.sh") +(defpoll ram :interval "3s" "~/.config/polybar/widgets/scripts/ram.sh ram") +(defpoll temp :interval "3s" "~/.config/polybar/widgets/scripts/temp.sh get") +(defpoll disk :interval "3s" "~/.config/polybar/widgets/scripts/disk.sh") + +(defpoll title :interval "0s" "~/.config/eww/sidebar/scripts/music title") +(defpoll artist :interval "0s" "~/.config/eww/sidebar/scripts/music artist") +(defpoll album :interval "0s" "~/.config/eww/sidebar/scripts/music album") +(defpoll playpause :interval "0s" "~/.config/eww/sidebar/scripts/music statusicon") +(defpoll art :interval "0s" "~/.config/eww/sidebar/scripts/music coverloc") \ No newline at end of file