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 @@
+
\ 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 @@
+
\ 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 @@
+
\ 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