summaryrefslogtreecommitdiff
path: root/.config/eww
diff options
context:
space:
mode:
Diffstat (limited to '.config/eww')
-rw-r--r--.config/eww/.editorconfig3
-rw-r--r--.config/eww/applauncher.scss34
-rw-r--r--.config/eww/applauncher.yuck31
-rw-r--r--.config/eww/bar.yuck46
-rw-r--r--.config/eww/eww.scss1
-rwxr-xr-x.config/eww/listapps.py120
-rw-r--r--.config/eww/powermenu.yuck12
-rw-r--r--.config/eww/wall.scss9
-rw-r--r--.config/eww/wall.yuck2
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)}%")