diff options
Diffstat (limited to '.config')
| -rw-r--r-- | .config/eww/.editorconfig | 3 | ||||
| -rw-r--r-- | .config/eww/applauncher.scss | 34 | ||||
| -rw-r--r-- | .config/eww/applauncher.yuck | 31 | ||||
| -rw-r--r-- | .config/eww/bar.yuck | 46 | ||||
| -rw-r--r-- | .config/eww/eww.scss | 1 | ||||
| -rwxr-xr-x | .config/eww/listapps.py | 120 | ||||
| -rw-r--r-- | .config/eww/powermenu.yuck | 12 | ||||
| -rw-r--r-- | .config/eww/wall.scss | 9 | ||||
| -rw-r--r-- | .config/eww/wall.yuck | 2 |
9 files changed, 225 insertions, 33 deletions
diff --git a/.config/eww/.editorconfig b/.config/eww/.editorconfig index 1e8dd63..79e4fce 100644 --- a/.config/eww/.editorconfig +++ b/.config/eww/.editorconfig @@ -9,3 +9,6 @@ indent_size = 2 [*.scss] indent_style = tab + +[*.py] +indent_size = 4 diff --git a/.config/eww/applauncher.scss b/.config/eww/applauncher.scss new file mode 100644 index 0000000..02e0c0a --- /dev/null +++ b/.config/eww/applauncher.scss @@ -0,0 +1,34 @@ +.applauncher { + background-color: $bg; + border: 2px solid $ui2; + color: $tx; + padding: 16px; + border-radius: 24px; + font-family: Inter; +} + +.app-input { + background-color: $ui; + padding: 4px 16px; + margin-bottom: 8px; + border-radius: 999px; +} + +.applist > scrollbar { + background-color: $ui; + border-radius: 3px; + + slider { + background-color: $re; + min-width: 6px; + border-radius: 3px; + } +} + +.app { + padding: 4px; +} + +.app:hover { + color: $re; +} diff --git a/.config/eww/applauncher.yuck b/.config/eww/applauncher.yuck index 7a02e8b..8a483ac 100644 --- a/.config/eww/applauncher.yuck +++ b/.config/eww/applauncher.yuck @@ -1,3 +1,5 @@ +(deflisten applist :initial '[{"name":"true"}]' `./listapps.py query`) + (defwindow applauncher :monitor 0 :geometry (geometry :x "50%" @@ -5,9 +7,30 @@ :width "500px" :height "400px" :anchor "center center") - :stacking "overlay" + :stacking "fg" :exclusive false :focusable true - (box :orientation "v" - :class "applauncher" - (input :onaccept `eww close applauncher`))) + (eventbox :onhoverlost `${EWW_CMD} close applauncher` + (box :orientation "v" + :space-evenly false + :class "applauncher" + (input :onchange `./listapps.py query {}` + :onaccept `./listapps.py run ${applist[0].name} & ${EWW_CMD} close applauncher` + :class "app-input") + (scroll :class "applist" + :vexpand true + :vscroll true + :hscroll false + (box :orientation "v" + :space-evenly false + (for appdata in applist + (app :appdata appdata))))))) + +(defwidget app [appdata] + (button :onclick `./listapps.py run ${appdata.name} & ${EWW_CMD} close applauncher` + :halign "start" + :hexpand true + :class "app" + (box :orientation "h" + :hexpand true + (label :text {appdata.name})))) diff --git a/.config/eww/bar.yuck b/.config/eww/bar.yuck index a691155..d6bfc28 100644 --- a/.config/eww/bar.yuck +++ b/.config/eww/bar.yuck @@ -3,7 +3,7 @@ (defpoll song :initial "" :interval "5s" `rmpc song`) (deflisten cava :initial "" `cava -p ~/.config/eww/cava.ini | python3 ~/.config/eww/cava.py`) -(defwindow bar +(defwindow bar [output] :monitor 0 :geometry (geometry :x "0%" :y "8px" @@ -13,30 +13,32 @@ :stacking "fg" :exclusive true :focusable false - (bar)) + (bar :output output)) -(defwidget bar [] +(defwidget bar [output] (centerbox :orientation "h" :class "bar" - (tags) - (center) + (tags :output output) + (music) (status))) -(defwidget tags [] +(defwidget tags [output] (box :orientation "h" :halign "start" :class "tags" (for tag in "[1, 2, 3, 4, 5, 6, 7, 8, 9]" (button :onclick `riverctl set-focused-tags $(dc -e"2 ${tag} 1- ^p")` - :class {jq(tags_json, `.[] | select(.id == ${tag}) | {focused, occupied} | to_entries | map(select(.value) | .key) | join(" ") | {"class": .}`).class})))) + :class {jq(tags_json, `.[] | select(.output == "${output}" and .id == ${tag}) | {focused, occupied} | to_entries | map(select(.value) | .key) | join(" ") | {"class": .}`).class})))) - -(defwidget center [] +(defwidget music [] (box :orientation "h" :spacing 4 :space-evenly false :class "music" - (label :text "${song.metadata.artist} - ${song.metadata.title}" :class "song") + (label :text "${song.metadata.artist} - ${song.metadata.title}" + :class "song" + :show-truncated true + :limit-width 55) (label :text {cava} :class "cava"))) @@ -60,18 +62,18 @@ :class "status" :spacing 8 :space-evenly false - (revealer-on-hover :var showvol - :varname "showvol" - :icon "a" - :transition "slideleft" - :class "volume" - (scale :orientation "h" - :min 0 - :max 1 - :value {volume} - :onchange `wpctl set-volume @DEFAULT_SINK@ {}`) - (label :text "${matches(volume, "MUTED") ? " " : volume == 0 ? "" : volume < 50 ? "" : " "} ${volume}%" - :class "volume")) + ;(revealer-on-hover :var showvol + ;:varname "showvol" + ;:icon "a" + ;:transition "slideleft" + ;:class "volume" + ;(scale :orientation "h" + ;:min 0 + ;:max 1 + ;:value {volume} + ;:onchange `wpctl set-volume @DEFAULT_SINK@ {}`) + (label :text "${matches(volume, "MUTED") ? " " : volume == 0 ? "" : volume < 50 ? "" : " "} ${volume}%" + :class "volume") (label :text "${ssid == "" ? "" : ""} ${ssid}" :class "network") (label :text " ${brightness}%" diff --git a/.config/eww/eww.scss b/.config/eww/eww.scss index 4837d8f..490db5d 100644 --- a/.config/eww/eww.scss +++ b/.config/eww/eww.scss @@ -31,4 +31,5 @@ $ma2: #A02F6F; @import "./bar.scss"; @import "./wall.scss"; +@import "./applauncher.scss"; @import "./powermenu.scss"; diff --git a/.config/eww/listapps.py b/.config/eww/listapps.py new file mode 100755 index 0000000..2dae1dc --- /dev/null +++ b/.config/eww/listapps.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 + +import glob +import json +import os +import os.path +import shlex +import sqlite3 +import sys +from dataclasses import dataclass + + +@dataclass +class App: + name: str + icon: str | None + command: str + + +def parse_desktop_file(path: str) -> App: + section = '' + app = App('', None, '') + with open(path, 'r') as file: + for line in file: + line = line.split('#')[0] + if line == '': + continue + if line.startswith('['): + section = line.rstrip().removeprefix('[').removesuffix(']') + continue + entry = line.split('=') + if len(entry) < 2: + continue + k, v = entry[0], entry[1].rstrip() + if k == 'Name': + app.name = v + elif k == 'Icon': + app.icon = v + elif k == 'Exec': + app.command = v.replace('%u', '').replace('%U', '').replace('%F', '').rstrip() + return app + + +def get_apps() -> dict[str, App]: + apps = {} + for dir in os.environ['PATH'].split(':'): + if os.path.exists(dir): + for ent in os.listdir(dir): + if os.access(os.path.join(dir, ent), os.X_OK): + apps[ent] = App(ent, None, ent) + for path in glob.glob('/usr/share/applications/*.desktop'): + app = parse_desktop_file(path) + apps[app.name] = app + for path in glob.glob(os.path.expanduser('~/.local/share/applications/*.desktop')): + app = parse_desktop_file(path) + apps[app.name] = app + return apps + + +def printusage(argv0): + print(f'{argv0} query [QUERY] | run NAME', file=sys.stderr) + + +def main(argv: list[str]) -> int: + conn = sqlite3.connect(os.path.expanduser('~/.local/share/eww/apps.sqlite')) + conn.execute(''' + create table if not exists app( + id integer primary key, + name text unique not null, + count integer default 0 not null + ); + ''') + + apps = get_apps() + with conn: + for app in apps.values(): + conn.execute('insert or ignore into app (name) values (?)', (app.name, )) + + if len(argv) < 2: + printusage(argv[0]) + return 1 + + if argv[1] == 'query': + out = [] + query = '' if len(argv) < 3 else argv[2] + for name, in conn.execute(''' + select name + from app + where name like '%'||?||'%' + order by count desc, name + ''', (query, )).fetchall(): + app = apps[name] + d = {'name': app.name} + if app.icon is not None: + d['icon'] = app.icon + out.append(d) + data = json.dumps(out, separators=(',', ':')) + argv = ['eww', 'update', 'applist=' + data] + os.execvp(argv[0], argv) + elif argv[1] == 'run': + if app := apps.get(argv[2]): + with conn: + conn.execute('insert or ignore into app (name) values (?)', (app.name, )) + conn.execute('update app set count = count + 1 where name = ?', (app.name, )) + argv = shlex.split(app.command) + os.execvp(argv[0], argv) + else: + print(f'app {argv[2]} not found', file=sys.stderr) + else: + printusage(argv[0]) + return 1 + + return 0 + + +if __name__ == '__main__': + try: + sys.exit(main(sys.argv)) + except BrokenPipeError: + pass diff --git a/.config/eww/powermenu.yuck b/.config/eww/powermenu.yuck index ce3c0a8..eae1e2b 100644 --- a/.config/eww/powermenu.yuck +++ b/.config/eww/powermenu.yuck @@ -14,20 +14,20 @@ :class "powermenu-buttons" :spacing "20" (box :orientation "v" :space-evenly false - (button :vexpand "true" "") + (button :vexpand true "") (label :text "Power Off")) (box :orientation "v" :space-evenly false - (button :vexpand "true" "") + (button :vexpand true "") (label :text "Reboot")) (box :orientation "v" :space-evenly false - (button :vexpand "true" "") + (button :vexpand true "") (label :text "Lock")) (box :orientation "v" :space-evenly false - (button :vexpand "true" "") + (button :vexpand true "") (label :text "Sleep")) (box :orientation "v" :space-evenly false - (button :vexpand "true" "") + (button :vexpand true "") (label :text "Log Out")) (box :orientation "v" :space-evenly false - (button :vexpand "true" :timeout "5000" :onclick `eww close powermenu` "") + (button :vexpand true :timeout "5000" :onclick `eww close powermenu` "") (label :text "Cancel"))))) diff --git a/.config/eww/wall.scss b/.config/eww/wall.scss index 4ab994a..6e5e93f 100644 --- a/.config/eww/wall.scss +++ b/.config/eww/wall.scss @@ -1,5 +1,5 @@ .wall { - margin: 96px 32px; + margin: 80px 32px; color: $bg; font-size: 2rem; font-family: Poppins; @@ -12,6 +12,13 @@ .date { font-weight: 300; + font-size: 2.25rem; + margin-bottom: 4px; +} + +.uptime { + font-weight: 300; + font-size: 1.75rem; } .sysmon { diff --git a/.config/eww/wall.yuck b/.config/eww/wall.yuck index a2516f8..9f4fed7 100644 --- a/.config/eww/wall.yuck +++ b/.config/eww/wall.yuck @@ -4,6 +4,7 @@ (defpoll date :initial "" :interval "60s" `date '+%B %d, %Y'`) (defpoll netup :initial "0" :interval "2s" `ifstat -t2 | awk '$1 == "wlan0" { print $6 }' | format-bytes`) (defpoll netdown :initial "0" :interval "2s" `ifstat -t2 | awk '$1 == "wlan0" { print $8 }' | format-bytes`) +(defpoll uptime :initial `{"d":0,"h":0,"m":0}` :interval "60s" `uptime-json`) (defwindow wall :monitor 0 @@ -23,6 +24,7 @@ :space-evenly false (label :class "time" :halign "start" :text {time}) (label :class "date" :halign "start" :text {date}) + (label :class "uptime" :halign "start" :text "Up ${uptime.d > 0 ? "${uptime.d} day${uptime.d != 1 ? "s" : ""}, " : ""}${uptime.h} hour${uptime.h != 1 ? "s" : ""}, ${uptime.m} minute${uptime.m != 1 ? "s" : ""}") (label :class "sysmon" :halign "start" :text "System Monitor") (label :class "sysmon-item" :halign "start" :text " ${round(EWW_CPU.avg, 0)}%") (label :class "sysmon-item" :halign "start" :text " ${round(EWW_RAM.used_mem_perc, 0)}%") |
