Compare commits
28 Commits
af42a00659
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
39beb2cec4
|
|||
|
36e0309bc9
|
|||
|
09609d6473
|
|||
|
4410db7880
|
|||
|
cd5d707bda
|
|||
|
d5a9500be1
|
|||
|
1efb93fc60
|
|||
|
1275a1ef4a
|
|||
|
1cfc8f1d37
|
|||
|
6a4f00a9df
|
|||
|
7408668b7f
|
|||
|
8d35c32782
|
|||
|
91d93955ab
|
|||
|
f920a19118
|
|||
|
80d5f9c979
|
|||
|
5a32bfeac5
|
|||
|
5b2ecf82f7
|
|||
|
4134296e7c
|
|||
|
875cc8e8d1
|
|||
|
72ce9353fc
|
|||
|
07e5e31722
|
|||
|
dd9e8b5981
|
|||
|
91191722ac
|
|||
|
5a9aec229e
|
|||
|
e74a4390bb
|
|||
|
f64db7b9ce
|
|||
|
e52005df82
|
|||
|
5b367986ee
|
19
.gitignore
vendored
19
.gitignore
vendored
@@ -69,6 +69,7 @@ waybar/scripts/*
|
|||||||
!waybar/scripts/vpn-status.sh
|
!waybar/scripts/vpn-status.sh
|
||||||
!waybar/scripts/notification-status.sh
|
!waybar/scripts/notification-status.sh
|
||||||
!waybar/scripts/mic-status.sh
|
!waybar/scripts/mic-status.sh
|
||||||
|
!waybar/scripts/network-status.sh
|
||||||
|
|
||||||
# eww
|
# eww
|
||||||
!eww
|
!eww
|
||||||
@@ -91,6 +92,9 @@ eww/scripts/*
|
|||||||
!sway/scripts
|
!sway/scripts
|
||||||
sway/scripts/*
|
sway/scripts/*
|
||||||
!sway/scripts/power-menu.sh
|
!sway/scripts/power-menu.sh
|
||||||
|
!sway/scripts/screenshot.sh
|
||||||
|
!sway/scripts/ws-to-current.sh
|
||||||
|
!sway/scripts/power-aware-effects.sh
|
||||||
|
|
||||||
# bin
|
# bin
|
||||||
!bin
|
!bin
|
||||||
@@ -108,7 +112,14 @@ swaync/*
|
|||||||
fuzzel/*
|
fuzzel/*
|
||||||
!fuzzel/fuzzel.ini
|
!fuzzel/fuzzel.ini
|
||||||
|
|
||||||
# flameshot
|
# mako
|
||||||
!flameshot
|
!mako
|
||||||
flameshot/*
|
mako/*
|
||||||
!flameshot/flameshot.ini
|
!mako/config
|
||||||
|
|
||||||
|
# claude
|
||||||
|
!claude
|
||||||
|
claude/*
|
||||||
|
!claude/CLAUDE.md
|
||||||
|
!claude/settings.json
|
||||||
|
!claude/statusline-command.sh
|
||||||
|
|||||||
38
claude/CLAUDE.md
Normal file
38
claude/CLAUDE.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Global CLAUDE.md
|
||||||
|
|
||||||
|
## Comments
|
||||||
|
|
||||||
|
Comments must be one of two forms:
|
||||||
|
|
||||||
|
```
|
||||||
|
# descriptive but non-verbose comment
|
||||||
|
# KEYWORD:(@janezicmatej) descriptive but non-verbose comment
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- always lowercase
|
||||||
|
- no trailing punctuation
|
||||||
|
- keywords: WARN, FIX, NOTE, TODO, PERF, TEST, HACK
|
||||||
|
- keyword comments must include attribution `(@janezicmatej)`
|
||||||
|
|
||||||
|
## Git
|
||||||
|
- commit only when asked or when delegating to agents
|
||||||
|
- agents MUST commit with `--no-gpg-sign`
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
- implement changes directly; don't use sub-agents/Task for simple operations like renames, file moves, or single-file edits; only use Task for genuinely complex parallel exploration or research
|
||||||
|
- when redirected or interrupted, immediately stop the current approach and follow the new direction; don't explain or defend the previous approach
|
||||||
|
- focus on one goal per session; don't drift into unrelated improvements
|
||||||
|
- use Task agents for research and exploration, not for straightforward implementation
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
- NEVER modify anything outside the scope of what was asked
|
||||||
|
- don't introduce unrelated changes, touch unrelated configs, or change defaults not mentioned
|
||||||
|
- if something related should change, ask first
|
||||||
|
|
||||||
|
## Config locations
|
||||||
|
- MCP server configs go in `.claude.json` (project) or `$CLAUDE_CONFIG_DIR/.claude.json` (global, defaults to `~/.claude.json`), not `settings.json`
|
||||||
|
- `settings.json` is for permissions, hooks, and behavior settings
|
||||||
|
|
||||||
|
## Code quality
|
||||||
|
- after making changes, check if the project has linters/formatters and run them
|
||||||
18
claude/settings.json
Normal file
18
claude/settings.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"mcp__todo-mcp__*",
|
||||||
|
"mcp__nixos__*",
|
||||||
|
"Bash(git remote get-url origin)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hooks": {},
|
||||||
|
"statusLine": {
|
||||||
|
"type": "command",
|
||||||
|
"command": "sh \"${CLAUDE_CONFIG_DIR:-$HOME/.claude}/statusline-command.sh\""
|
||||||
|
},
|
||||||
|
"effortLevel": "xhigh",
|
||||||
|
"skipDangerousModePermissionPrompt": true,
|
||||||
|
"voiceEnabled": true,
|
||||||
|
"mcpServers": {}
|
||||||
|
}
|
||||||
52
claude/statusline-command.sh
Normal file
52
claude/statusline-command.sh
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
input=$(cat)
|
||||||
|
|
||||||
|
model=$(echo "$input" | jq -r '.model.display_name // empty')
|
||||||
|
total_in=$(echo "$input" | jq -r '.context_window.total_input_tokens // 0')
|
||||||
|
total_out=$(echo "$input" | jq -r '.context_window.total_output_tokens // 0')
|
||||||
|
ctx=$(echo "$input" | jq -r '.context_window.used_percentage // empty')
|
||||||
|
five_hr=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
|
||||||
|
five_hr_reset=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty')
|
||||||
|
seven_day=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
|
||||||
|
seven_day_reset=$(echo "$input" | jq -r '.rate_limits.seven_day.resets_at // empty')
|
||||||
|
|
||||||
|
# format seconds until reset as human-readable
|
||||||
|
fmt_remaining() {
|
||||||
|
now=$(date +%s)
|
||||||
|
diff=$(( $1 - now ))
|
||||||
|
[ "$diff" -le 0 ] && return
|
||||||
|
h=$(( diff / 3600 ))
|
||||||
|
m=$(( (diff % 3600) / 60 ))
|
||||||
|
if [ "$h" -gt 0 ]; then
|
||||||
|
printf '%dh%dm' "$h" "$m"
|
||||||
|
else
|
||||||
|
printf '%dm' "$m"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
parts=""
|
||||||
|
|
||||||
|
[ -n "$model" ] && parts="$model"
|
||||||
|
|
||||||
|
if [ -n "$total_in" ] && [ "$total_in" != "0" ]; then
|
||||||
|
total=$((total_in + total_out))
|
||||||
|
parts="${parts:+$parts | }tokens:${total}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$ctx" ]; then
|
||||||
|
parts="${parts:+$parts | }ctx:$(printf '%.0f' "$ctx")%"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$five_hr" ]; then
|
||||||
|
five_hr_ttl=""
|
||||||
|
[ -n "$five_hr_reset" ] && five_hr_ttl=$(fmt_remaining "$five_hr_reset")
|
||||||
|
parts="${parts:+$parts | }5h:$(printf '%.0f' "$five_hr")%${five_hr_ttl:+($five_hr_ttl)}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$seven_day" ]; then
|
||||||
|
seven_day_ttl=""
|
||||||
|
[ -n "$seven_day_reset" ] && seven_day_ttl=$(fmt_remaining "$seven_day_reset")
|
||||||
|
parts="${parts:+$parts | }7d:$(printf '%.0f' "$seven_day")%${seven_day_ttl:+($seven_day_ttl)}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -n "$parts" ] && printf '%s' "$parts"
|
||||||
239
eww/eww.scss
239
eww/eww.scss
@@ -1,239 +0,0 @@
|
|||||||
// gruvbox-material-soft-dark palette
|
|
||||||
$bg: #32302f;
|
|
||||||
$bg_dim: #252423;
|
|
||||||
$bg_sel: #45403d;
|
|
||||||
$fg: #d4be98;
|
|
||||||
$red: #ea6962;
|
|
||||||
$green: #a9b665;
|
|
||||||
$yellow: #d8a657;
|
|
||||||
$blue: #7daea3;
|
|
||||||
$magenta: #d3869b;
|
|
||||||
$cyan: #89b482;
|
|
||||||
$gray: #928374;
|
|
||||||
|
|
||||||
// base
|
|
||||||
* {
|
|
||||||
all: unset;
|
|
||||||
font-family: "JetBrainsMono Nerd Font";
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// transparent backdrop for click-away dismiss
|
|
||||||
.backdrop {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// popup container
|
|
||||||
.popup {
|
|
||||||
background-color: $bg_dim;
|
|
||||||
border: 1px solid $bg_sel;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// section titles
|
|
||||||
.title {
|
|
||||||
color: $fg;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
color: $gray;
|
|
||||||
font-size: 12px;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// labels and values
|
|
||||||
.label {
|
|
||||||
color: $gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
color: $fg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value.dim {
|
|
||||||
color: $gray;
|
|
||||||
font-size: 12px;
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// detail lines (smaller, dimmer)
|
|
||||||
.details {
|
|
||||||
margin-top: 4px;
|
|
||||||
padding-top: 8px;
|
|
||||||
border-top: 1px solid $bg_sel;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail {
|
|
||||||
color: $gray;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// rows
|
|
||||||
.row {
|
|
||||||
padding: 2px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// progress bars
|
|
||||||
progressbar {
|
|
||||||
border-radius: 3px;
|
|
||||||
|
|
||||||
trough {
|
|
||||||
min-height: 4px;
|
|
||||||
border-radius: 3px;
|
|
||||||
background-color: $bg_sel;
|
|
||||||
|
|
||||||
progress {
|
|
||||||
min-height: 4px;
|
|
||||||
border-radius: 3px;
|
|
||||||
background-color: $green;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
progressbar.warning trough progress {
|
|
||||||
background-color: $yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
progressbar.critical trough progress {
|
|
||||||
background-color: $red;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sliders
|
|
||||||
scale {
|
|
||||||
margin: 0 4px;
|
|
||||||
|
|
||||||
trough {
|
|
||||||
min-height: 4px;
|
|
||||||
border-radius: 3px;
|
|
||||||
background-color: $bg_sel;
|
|
||||||
|
|
||||||
highlight {
|
|
||||||
min-height: 4px;
|
|
||||||
border-radius: 3px;
|
|
||||||
background-color: $blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
slider {
|
|
||||||
min-width: 14px;
|
|
||||||
min-height: 14px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: $fg;
|
|
||||||
margin: -5px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// buttons
|
|
||||||
.toggle-btn {
|
|
||||||
color: $gray;
|
|
||||||
padding: 2px 8px;
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: $bg_sel;
|
|
||||||
min-width: 30px;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: $bg_dim;
|
|
||||||
background-color: $green;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: lighten($bg_sel, 10%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// device list buttons
|
|
||||||
.device-btn {
|
|
||||||
color: $fg;
|
|
||||||
padding: 6px 10px;
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: transparent;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background-color: $bg_sel;
|
|
||||||
color: $fg;
|
|
||||||
|
|
||||||
label {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// device rows (non-clickable list items)
|
|
||||||
.device-row {
|
|
||||||
padding: 4px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// close/disconnect button
|
|
||||||
.close-btn {
|
|
||||||
color: $gray;
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $red;
|
|
||||||
background-color: $bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// profile selector buttons
|
|
||||||
.profile-btn {
|
|
||||||
color: $gray;
|
|
||||||
padding: 6px 12px;
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: $bg;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: $bg_dim;
|
|
||||||
background-color: $cyan;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $bg_sel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// separator line
|
|
||||||
.separator {
|
|
||||||
min-height: 1px;
|
|
||||||
background-color: $bg_sel;
|
|
||||||
margin: 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// status indicators
|
|
||||||
.indicator {
|
|
||||||
min-width: 14px;
|
|
||||||
|
|
||||||
&.online {
|
|
||||||
color: $green;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.offline {
|
|
||||||
color: $gray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// empty state
|
|
||||||
.empty {
|
|
||||||
color: $gray;
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// active player label
|
|
||||||
.playing {
|
|
||||||
color: $green;
|
|
||||||
}
|
|
||||||
|
|
||||||
// slider row layout
|
|
||||||
.slider-row {
|
|
||||||
padding: 4px 0;
|
|
||||||
}
|
|
||||||
421
eww/eww.yuck
421
eww/eww.yuck
@@ -1,421 +0,0 @@
|
|||||||
; sway shell popups
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; variables
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
(defpoll sys :interval "5s"
|
|
||||||
:initial '{"cpu":0,"ram_percent":0,"ram_used":"0Gi","ram_total":"0Gi","temp":0,"disk_percent":0,"disk_used":"0","disk_total":"0","swap":"0/0","load":"0 0 0","uptime":""}'
|
|
||||||
"~/.config/eww/scripts/system.sh")
|
|
||||||
|
|
||||||
(defpoll bat :interval "30s"
|
|
||||||
:initial '{"capacity":0,"status":"Unknown","power":"0","time":"","cycles":0,"profile":"unknown"}'
|
|
||||||
"~/.config/eww/scripts/battery.sh")
|
|
||||||
|
|
||||||
(defpoll vol :interval "5s"
|
|
||||||
:initial '{"volume":0,"muted":false,"mic_volume":0,"mic_muted":false,"brightness":0,"sinks":[],"sources":[],"sink_count":0,"source_count":0}'
|
|
||||||
"~/.config/eww/scripts/volume.sh")
|
|
||||||
|
|
||||||
(defpoll bt :interval "15s"
|
|
||||||
:initial '{"powered":false,"count":0,"devices":[]}'
|
|
||||||
"~/.config/eww/scripts/bluetooth.sh")
|
|
||||||
|
|
||||||
(defpoll net :interval "10s"
|
|
||||||
:initial '{"type":"none","iface":"none","ip":"none","gateway":"none","ssid":"","signal":0,"conn_name":"","count":0,"networks":[],"unknown_count":0,"unknown":[]}'
|
|
||||||
"~/.config/eww/scripts/network.sh")
|
|
||||||
|
|
||||||
(defpoll vpn_data :interval "15s"
|
|
||||||
:initial '{"tailscale":{"running":false,"ip":"","hostname":"","login":"","exit_nodes":[],"exit_count":0,"peers":[],"peer_count":0},"wireguard":{"active":false,"iface":""}}'
|
|
||||||
"~/.config/eww/scripts/vpn.sh")
|
|
||||||
|
|
||||||
(defpoll kbd :interval "10s"
|
|
||||||
:initial '{"current":"unknown","layouts":[],"layout_count":0,"keyboards":[],"kb_count":0}'
|
|
||||||
"~/.config/eww/scripts/keyboard.sh")
|
|
||||||
|
|
||||||
(defpoll media :interval "5s"
|
|
||||||
:initial '{"count":0,"players":[]}'
|
|
||||||
"~/.config/eww/scripts/media.sh")
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; media popup
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
(defwidget media-widget []
|
|
||||||
(box :class "popup" :orientation "v" :space-evenly false :spacing 8
|
|
||||||
(label :class "title" :text "media" :halign "start")
|
|
||||||
(box :visible {media.count == 0}
|
|
||||||
(label :class "empty" :text "no active players"))
|
|
||||||
(box :orientation "v" :space-evenly false :spacing 4
|
|
||||||
(for player in {media.players}
|
|
||||||
(box :class "device-row" :orientation "v" :space-evenly false :spacing 2
|
|
||||||
(box :orientation "h" :space-evenly false :spacing 8
|
|
||||||
(label :class "label ${player.status == 'Playing' ? 'playing' : ''}"
|
|
||||||
:text {player.display} :halign "start" :hexpand true)
|
|
||||||
(button :class "toggle-btn ${player.status == 'Playing' ? 'active' : ''}"
|
|
||||||
:onclick "~/.config/eww/scripts/media.sh play-pause '${player.name}'"
|
|
||||||
{player.status == "Playing" ? "pause" : "play"})
|
|
||||||
(button :class "close-btn"
|
|
||||||
:onclick "~/.config/eww/scripts/media.sh prev '${player.name}'"
|
|
||||||
"prev")
|
|
||||||
(button :class "close-btn"
|
|
||||||
:onclick "~/.config/eww/scripts/media.sh next '${player.name}'"
|
|
||||||
"next"))
|
|
||||||
(label :class "value" :text "${player.artist}${player.artist != '' ? ' - ' : ''}${player.title}"
|
|
||||||
:halign "start" :limit-width 45))))))
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; system popup
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
(defwidget system-widget []
|
|
||||||
(box :class "popup" :orientation "v" :space-evenly false :spacing 8
|
|
||||||
(label :class "title" :text "system" :halign "start")
|
|
||||||
|
|
||||||
; cpu
|
|
||||||
(box :class "row" :orientation "v" :space-evenly false :spacing 2
|
|
||||||
(box :orientation "h"
|
|
||||||
(label :class "label" :text "cpu" :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text "${sys.cpu}%"))
|
|
||||||
(progress :class "bar" :value {sys.cpu}))
|
|
||||||
|
|
||||||
; ram
|
|
||||||
(box :class "row" :orientation "v" :space-evenly false :spacing 2
|
|
||||||
(box :orientation "h"
|
|
||||||
(label :class "label" :text "ram" :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text "${sys.ram_used}/${sys.ram_total}"))
|
|
||||||
(progress :class "bar" :value {sys.ram_percent}))
|
|
||||||
|
|
||||||
; temp
|
|
||||||
(box :class "row" :orientation "h"
|
|
||||||
(label :class "label" :text "temp" :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text "${sys.temp}°"))
|
|
||||||
|
|
||||||
; disk
|
|
||||||
(box :class "row" :orientation "v" :space-evenly false :spacing 2
|
|
||||||
(box :orientation "h"
|
|
||||||
(label :class "label" :text "disk" :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text "${sys.disk_used}/${sys.disk_total}"))
|
|
||||||
(progress :class "bar" :value {sys.disk_percent}))
|
|
||||||
|
|
||||||
; details
|
|
||||||
(box :class "details" :orientation "v" :space-evenly false :spacing 2
|
|
||||||
(label :class "detail" :text "swap ${sys.swap}" :halign "start")
|
|
||||||
(label :class "detail" :text "load ${sys.load}" :halign "start")
|
|
||||||
(label :class "detail" :text "up ${sys.uptime}" :halign "start"))))
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; battery popup
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
(defwidget battery-widget []
|
|
||||||
(box :class "popup" :orientation "v" :space-evenly false :spacing 8
|
|
||||||
(label :class "title" :text "battery" :halign "start")
|
|
||||||
|
|
||||||
; capacity bar
|
|
||||||
(box :class "row" :orientation "v" :space-evenly false :spacing 2
|
|
||||||
(box :orientation "h"
|
|
||||||
(label :class "label" :text {bat.status} :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text "${bat.capacity}%"))
|
|
||||||
(progress :class "bar ${bat.capacity < 15 ? 'critical' : bat.capacity < 30 ? 'warning' : ''}"
|
|
||||||
:value {bat.capacity}))
|
|
||||||
|
|
||||||
; power + time
|
|
||||||
(box :class "row" :orientation "h"
|
|
||||||
(label :class "label" :text "power" :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text "${bat.power}W"))
|
|
||||||
(box :class "row" :orientation "h" :visible {bat.time != ""}
|
|
||||||
(label :class "label" :text "remaining" :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text {bat.time}))
|
|
||||||
(box :class "row" :orientation "h"
|
|
||||||
(label :class "label" :text "cycles" :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text {bat.cycles}))
|
|
||||||
|
|
||||||
; power profile selector
|
|
||||||
(box :class "separator")
|
|
||||||
(label :class "title" :text "profile" :halign "start")
|
|
||||||
(box :class "profiles" :orientation "h" :space-evenly true :spacing 4
|
|
||||||
(button :class "profile-btn ${bat.profile == 'power-saver' ? 'active' : ''}"
|
|
||||||
:onclick "~/.config/eww/scripts/battery.sh set-profile power-saver"
|
|
||||||
"saver")
|
|
||||||
(button :class "profile-btn ${bat.profile == 'balanced' ? 'active' : ''}"
|
|
||||||
:onclick "~/.config/eww/scripts/battery.sh set-profile balanced"
|
|
||||||
"balanced")
|
|
||||||
(button :class "profile-btn ${bat.profile == 'performance' ? 'active' : ''}"
|
|
||||||
:onclick "~/.config/eww/scripts/battery.sh set-profile performance"
|
|
||||||
"perform"))))
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; volume popup
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
(defwidget volume-widget []
|
|
||||||
(box :class "popup" :orientation "v" :space-evenly false :spacing 8
|
|
||||||
; output section
|
|
||||||
(box :orientation "v" :space-evenly false :spacing 4
|
|
||||||
(label :class "title" :text "output" :halign "start")
|
|
||||||
(box :class "row slider-row" :orientation "h" :space-evenly false :spacing 8
|
|
||||||
(button :class "toggle-btn ${vol.muted ? 'active' : ''}"
|
|
||||||
:onclick "~/.config/eww/scripts/volume.sh toggle-mute"
|
|
||||||
{vol.muted ? "muted" : "vol"})
|
|
||||||
(scale :class "slider" :min 0 :max 100 :value {vol.volume}
|
|
||||||
:onchange "pamixer --set-volume {}" :hexpand true)
|
|
||||||
(label :class "value" :text "${vol.volume}%"))
|
|
||||||
(for sink in {vol.sinks}
|
|
||||||
(button :class "device-btn ${sink.active ? 'active' : ''}"
|
|
||||||
:onclick "~/.config/eww/scripts/volume.sh set-sink '${sink.sink_name}'"
|
|
||||||
(label :text {sink.name} :halign "start"))))
|
|
||||||
|
|
||||||
; input section
|
|
||||||
(box :class "separator")
|
|
||||||
(box :orientation "v" :space-evenly false :spacing 4
|
|
||||||
(label :class "title" :text "input" :halign "start")
|
|
||||||
(box :class "row slider-row" :orientation "h" :space-evenly false :spacing 8
|
|
||||||
(button :class "toggle-btn ${vol.mic_muted ? 'active' : ''}"
|
|
||||||
:onclick "~/.config/eww/scripts/volume.sh toggle-mic"
|
|
||||||
{vol.mic_muted ? "muted" : "mic"})
|
|
||||||
(scale :class "slider" :min 0 :max 100 :value {vol.mic_volume}
|
|
||||||
:onchange "pamixer --default-source --set-volume {}" :hexpand true)
|
|
||||||
(label :class "value" :text "${vol.mic_volume}%"))
|
|
||||||
(for source in {vol.sources}
|
|
||||||
(button :class "device-btn ${source.active ? 'active' : ''}"
|
|
||||||
:onclick "~/.config/eww/scripts/volume.sh set-source '${source.source_name}'"
|
|
||||||
(label :text {source.name} :halign "start"))))
|
|
||||||
|
|
||||||
; brightness section
|
|
||||||
(box :class "separator")
|
|
||||||
(label :class "title" :text "brightness" :halign "start")
|
|
||||||
(box :class "row slider-row" :orientation "h" :space-evenly false :spacing 8
|
|
||||||
(label :class "label" :text "bl")
|
|
||||||
(scale :class "slider" :min 0 :max 100 :value {vol.brightness}
|
|
||||||
:onchange "brightnessctl set {}%" :hexpand true)
|
|
||||||
(label :class "value" :text "${vol.brightness}%"))))
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; bluetooth popup
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
(defwidget bluetooth-widget []
|
|
||||||
(box :class "popup" :orientation "v" :space-evenly false :spacing 8
|
|
||||||
(box :class "row" :orientation "h"
|
|
||||||
(label :class "title" :text "bluetooth" :halign "start" :hexpand true)
|
|
||||||
(button :class "toggle-btn ${bt.powered ? 'active' : ''}"
|
|
||||||
:onclick "~/.config/eww/scripts/bluetooth.sh toggle-power"
|
|
||||||
{bt.powered ? "on" : "off"}))
|
|
||||||
|
|
||||||
(box :visible {bt.powered} :orientation "v" :space-evenly false :spacing 4
|
|
||||||
(for device in {bt.devices}
|
|
||||||
(box :class "device-row" :orientation "h" :space-evenly false :spacing 8
|
|
||||||
(label :class "label" :text {device.name} :halign "start" :hexpand true)
|
|
||||||
(label :class "value dim" :visible {device.battery >= 0}
|
|
||||||
:text "${device.battery}%")
|
|
||||||
(button :class "close-btn"
|
|
||||||
:onclick "~/.config/eww/scripts/bluetooth.sh disconnect '${device.mac}'"
|
|
||||||
"x")))
|
|
||||||
(label :visible {bt.count == 0} :class "empty" :text "no devices connected"))))
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; network popup
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
(defwidget network-widget []
|
|
||||||
(box :class "popup" :orientation "v" :space-evenly false :spacing 8
|
|
||||||
(label :class "title" :text "network" :halign "start")
|
|
||||||
|
|
||||||
; connection info
|
|
||||||
(box :class "row" :orientation "h"
|
|
||||||
(label :class "label" :text "interface" :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text {net.iface}))
|
|
||||||
(box :class "row" :orientation "h"
|
|
||||||
(label :class "label" :text "ip" :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text {net.ip}))
|
|
||||||
(box :class "row" :orientation "h"
|
|
||||||
(label :class "label" :text "gateway" :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text {net.gateway}))
|
|
||||||
(box :class "row" :orientation "h" :visible {net.type == "wifi"}
|
|
||||||
(label :class "label" :text "ssid" :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text "${net.ssid} ${net.signal}%"))
|
|
||||||
|
|
||||||
; saved wifi networks nearby
|
|
||||||
(box :visible {net.count > 0} :orientation "v" :space-evenly false :spacing 4
|
|
||||||
(box :class "separator")
|
|
||||||
(label :class "title" :text "saved" :halign "start")
|
|
||||||
(box :orientation "v" :space-evenly false :spacing 0
|
|
||||||
(for network in {net.networks}
|
|
||||||
(button :class "device-btn ${network.active ? 'active' : ''}"
|
|
||||||
:onclick {network.active ? "~/.config/eww/scripts/network.sh disconnect '${network.ssid}'" : "~/.config/eww/scripts/network.sh connect '${network.ssid}'"}
|
|
||||||
(box :orientation "h" :space-evenly false
|
|
||||||
(label :class "label" :text {network.ssid} :halign "start" :hexpand true)
|
|
||||||
(label :class "value dim" :text "${network.signal}%"))))))
|
|
||||||
|
|
||||||
; unknown wifi networks nearby
|
|
||||||
(box :visible {net.unknown_count > 0} :orientation "v" :space-evenly false :spacing 4
|
|
||||||
(box :class "separator")
|
|
||||||
(label :class "title" :text "nearby" :halign "start")
|
|
||||||
(box :orientation "v" :space-evenly false :spacing 0
|
|
||||||
(for network in {net.unknown}
|
|
||||||
(button :class "device-btn"
|
|
||||||
:onclick "~/.config/eww/scripts/network.sh connect-new '${network.ssid}'"
|
|
||||||
(box :orientation "h" :space-evenly false
|
|
||||||
(label :class "label" :text {network.ssid} :halign "start" :hexpand true)
|
|
||||||
(label :class "value dim" :text "${network.signal}%"))))))))
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; vpn popup
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
(defwidget vpn-widget []
|
|
||||||
(box :class "popup" :orientation "v" :space-evenly false :spacing 8
|
|
||||||
; tailscale
|
|
||||||
(box :class "row" :orientation "h"
|
|
||||||
(label :class "title" :text "tailscale" :halign "start" :hexpand true)
|
|
||||||
(button :class "toggle-btn ${vpn_data.tailscale.running ? 'active' : ''}"
|
|
||||||
:onclick "~/.config/eww/scripts/vpn.sh ${vpn_data.tailscale.running ? 'ts-down' : 'ts-up'}"
|
|
||||||
{vpn_data.tailscale.running ? "on" : "off"}))
|
|
||||||
|
|
||||||
(box :visible {vpn_data.tailscale.running} :orientation "v" :space-evenly false :spacing 4
|
|
||||||
(box :class "row" :orientation "h"
|
|
||||||
(label :class "label" :text "ip" :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text {vpn_data.tailscale.ip}))
|
|
||||||
(box :class "row" :orientation "h"
|
|
||||||
(label :class "label" :text "host" :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text {vpn_data.tailscale.hostname}))
|
|
||||||
(box :class "row" :orientation "h"
|
|
||||||
(label :class "label" :text "network" :halign "start" :hexpand true)
|
|
||||||
(label :class "value" :text {vpn_data.tailscale.login}))
|
|
||||||
|
|
||||||
; exit nodes
|
|
||||||
(box :visible {vpn_data.tailscale.exit_count > 0} :orientation "v" :space-evenly false :spacing 4
|
|
||||||
(box :class "separator")
|
|
||||||
(label :class "subtitle" :text "exit node" :halign "start")
|
|
||||||
(box :orientation "v" :space-evenly false :spacing 0
|
|
||||||
(for node in {vpn_data.tailscale.exit_nodes}
|
|
||||||
(button :class "device-btn ${node.active ? 'active' : ''}"
|
|
||||||
:onclick "~/.config/eww/scripts/vpn.sh ts-exit '${node.active ? '' : node.ip}'"
|
|
||||||
(label :text {node.name} :halign "start")))))
|
|
||||||
|
|
||||||
; peers
|
|
||||||
(box :orientation "v" :space-evenly false :spacing 4
|
|
||||||
(box :class "separator")
|
|
||||||
(label :class "subtitle" :text "devices" :halign "start")
|
|
||||||
(for peer in {vpn_data.tailscale.peers}
|
|
||||||
(box :class "device-row" :orientation "h" :space-evenly false :spacing 8
|
|
||||||
(label :class "indicator ${peer.online ? 'online' : 'offline'}"
|
|
||||||
:text {peer.online ? "●" : "○"})
|
|
||||||
(label :class "label" :text {peer.name} :halign "start" :hexpand true)
|
|
||||||
(label :class "value dim" :text {peer.ip})))))
|
|
||||||
|
|
||||||
; wireguard
|
|
||||||
(box :class "separator")
|
|
||||||
(box :class "row" :orientation "h"
|
|
||||||
(label :class "title" :text "wireguard" :halign "start" :hexpand true)
|
|
||||||
(button :class "toggle-btn ${vpn_data.wireguard.active ? 'active' : ''}"
|
|
||||||
:onclick "~/.config/eww/scripts/vpn.sh ${vpn_data.wireguard.active ? 'wg-down' : 'wg-up'}"
|
|
||||||
{vpn_data.wireguard.active ? "on" : "off"}))))
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; keyboard popup
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
(defwidget keyboard-widget []
|
|
||||||
(box :class "popup" :orientation "v" :space-evenly false :spacing 8
|
|
||||||
(label :class "title" :text "keyboard" :halign "start")
|
|
||||||
|
|
||||||
; layout selector
|
|
||||||
(label :class "subtitle" :text "layout" :halign "start")
|
|
||||||
(for layout in {kbd.layouts}
|
|
||||||
(button :class "device-btn ${layout == kbd.current ? 'active' : ''}"
|
|
||||||
:onclick "~/.config/eww/scripts/keyboard.sh switch"
|
|
||||||
(label :text {layout} :halign "start")))
|
|
||||||
|
|
||||||
; keyboard devices
|
|
||||||
(box :visible {kbd.kb_count > 1} :orientation "v" :space-evenly false :spacing 4
|
|
||||||
(box :class "separator")
|
|
||||||
(label :class "subtitle" :text "devices" :halign "start")
|
|
||||||
(for kb in {kbd.keyboards}
|
|
||||||
(box :class "device-row" :orientation "h" :space-evenly false :spacing 8
|
|
||||||
(label :class "label" :text {kb.name} :halign "start" :hexpand true)
|
|
||||||
(label :class "value dim" :text {kb.layout}))))))
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; backdrop (click-away to close)
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
(defwidget backdrop-widget []
|
|
||||||
(eventbox :onclick "~/.config/eww/scripts/popup.sh close-all"
|
|
||||||
(box :class "backdrop" :hexpand true :vexpand true)))
|
|
||||||
|
|
||||||
(defwindow backdrop
|
|
||||||
:monitor 0
|
|
||||||
:geometry (geometry :x "0px" :y "0px" :width "100%" :height "100%" :anchor "top left")
|
|
||||||
:stacking "overlay"
|
|
||||||
:exclusive false
|
|
||||||
:focusable true
|
|
||||||
(backdrop-widget))
|
|
||||||
|
|
||||||
; =============================================================================
|
|
||||||
; windows
|
|
||||||
; =============================================================================
|
|
||||||
|
|
||||||
(defwindow system-popup
|
|
||||||
:monitor 0
|
|
||||||
:geometry (geometry :x "380px" :y "3px" :width "300px" :anchor "top right")
|
|
||||||
:stacking "overlay"
|
|
||||||
:exclusive false
|
|
||||||
:focusable false
|
|
||||||
(system-widget))
|
|
||||||
|
|
||||||
(defwindow battery-popup
|
|
||||||
:monitor 0
|
|
||||||
:geometry (geometry :x "260px" :y "3px" :width "300px" :anchor "top right")
|
|
||||||
:stacking "overlay"
|
|
||||||
:exclusive false
|
|
||||||
:focusable false
|
|
||||||
(battery-widget))
|
|
||||||
|
|
||||||
(defwindow volume-popup
|
|
||||||
:monitor 0
|
|
||||||
:geometry (geometry :x "140px" :y "3px" :width "320px" :anchor "top right")
|
|
||||||
:stacking "overlay"
|
|
||||||
:exclusive false
|
|
||||||
:focusable false
|
|
||||||
(volume-widget))
|
|
||||||
|
|
||||||
(defwindow bluetooth-popup
|
|
||||||
:monitor 0
|
|
||||||
:geometry (geometry :x "500px" :y "3px" :width "280px" :anchor "top right")
|
|
||||||
:stacking "overlay"
|
|
||||||
:exclusive false
|
|
||||||
:focusable false
|
|
||||||
(bluetooth-widget))
|
|
||||||
|
|
||||||
(defwindow network-popup
|
|
||||||
:monitor 0
|
|
||||||
:geometry (geometry :x "560px" :y "3px" :width "320px" :anchor "top right")
|
|
||||||
:stacking "overlay"
|
|
||||||
:exclusive false
|
|
||||||
:focusable false
|
|
||||||
(network-widget))
|
|
||||||
|
|
||||||
(defwindow vpn-popup
|
|
||||||
:monitor 0
|
|
||||||
:geometry (geometry :x "480px" :y "3px" :width "320px" :anchor "top right")
|
|
||||||
:stacking "overlay"
|
|
||||||
:exclusive false
|
|
||||||
:focusable false
|
|
||||||
(vpn-widget))
|
|
||||||
|
|
||||||
(defwindow keyboard-popup
|
|
||||||
:monitor 0
|
|
||||||
:geometry (geometry :x "50px" :y "3px" :width "280px" :anchor "top right")
|
|
||||||
:stacking "overlay"
|
|
||||||
:exclusive false
|
|
||||||
:focusable false
|
|
||||||
(keyboard-widget))
|
|
||||||
|
|
||||||
(defwindow media-popup
|
|
||||||
:monitor 0
|
|
||||||
:geometry (geometry :x "700px" :y "3px" :width "350px" :anchor "top right")
|
|
||||||
:stacking "overlay"
|
|
||||||
:exclusive false
|
|
||||||
:focusable false
|
|
||||||
(media-widget))
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# battery info as JSON for eww battery-popup
|
|
||||||
|
|
||||||
action="${1:-status}"
|
|
||||||
|
|
||||||
if [[ "$action" == "set-profile" ]]; then
|
|
||||||
powerprofilesctl set "$2"
|
|
||||||
( data=$(~/.config/eww/scripts/battery.sh); eww update bat="$data" ) &
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
bat_path=$(echo /sys/class/power_supply/BAT* 2>/dev/null | awk '{print $1}')
|
|
||||||
|
|
||||||
if [[ ! -d "$bat_path" ]]; then
|
|
||||||
jq -nc '{capacity:0,status:"No battery",power:"0",time:"",cycles:0,profile:"unknown"}'
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
capacity=$(cat "$bat_path/capacity" 2>/dev/null || echo 0)
|
|
||||||
status=$(cat "$bat_path/status" 2>/dev/null || echo "Unknown")
|
|
||||||
|
|
||||||
power_uw=$(cat "$bat_path/power_now" 2>/dev/null || echo 0)
|
|
||||||
power=$(awk -v p="$power_uw" 'BEGIN{printf "%.1f", p/1000000}')
|
|
||||||
|
|
||||||
cycles=$(cat "$bat_path/cycle_count" 2>/dev/null || echo 0)
|
|
||||||
[[ "$cycles" =~ ^[0-9]+$ ]] || cycles=0
|
|
||||||
|
|
||||||
bat_upower=$(upower -e 2>/dev/null | grep BAT | head -1)
|
|
||||||
time_str=""
|
|
||||||
if [[ -n "$bat_upower" ]]; then
|
|
||||||
time_str=$(upower -i "$bat_upower" 2>/dev/null | awk '/time to/{print $4, $5}')
|
|
||||||
fi
|
|
||||||
|
|
||||||
profile=$(powerprofilesctl get 2>/dev/null || echo "unknown")
|
|
||||||
|
|
||||||
jq -nc \
|
|
||||||
--argjson capacity "$capacity" \
|
|
||||||
--arg status "$status" \
|
|
||||||
--arg power "$power" \
|
|
||||||
--arg time "$time_str" \
|
|
||||||
--argjson cycles "${cycles:-0}" \
|
|
||||||
--arg profile "$profile" \
|
|
||||||
'{$capacity,$status,$power,$time,$cycles,$profile}'
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# bluetooth device info as JSON for eww bluetooth-popup
|
|
||||||
|
|
||||||
action="${1:-status}"
|
|
||||||
|
|
||||||
case "$action" in
|
|
||||||
status)
|
|
||||||
powered=$(bluetoothctl show 2>/dev/null | grep -q "Powered: yes" && echo true || echo false)
|
|
||||||
|
|
||||||
devices="[]"
|
|
||||||
count=0
|
|
||||||
if [[ "$powered" == "true" ]]; then
|
|
||||||
# get connected devices in one pass
|
|
||||||
devices=$(bluetoothctl devices Connected 2>/dev/null | while read -r _ mac name; do
|
|
||||||
info=$(bluetoothctl info "$mac" 2>/dev/null)
|
|
||||||
battery=$(awk '/Battery Percentage:/{gsub(/[()]/,""); print $4}' <<< "$info")
|
|
||||||
jq -nc --arg name "$name" --arg mac "$mac" --argjson battery "${battery:--1}" \
|
|
||||||
'{$name,$mac,$battery}'
|
|
||||||
done | jq -sc '.')
|
|
||||||
[[ -z "$devices" || "$devices" == "null" ]] && devices="[]"
|
|
||||||
count=$(jq 'length' <<< "$devices" 2>/dev/null || echo 0)
|
|
||||||
fi
|
|
||||||
|
|
||||||
jq -nc \
|
|
||||||
--argjson powered "$powered" \
|
|
||||||
--argjson count "$count" \
|
|
||||||
--argjson devices "$devices" \
|
|
||||||
'{$powered,$count,$devices}'
|
|
||||||
;;
|
|
||||||
toggle-power)
|
|
||||||
if bluetoothctl show 2>/dev/null | grep -q "Powered: yes"; then
|
|
||||||
bluetoothctl power off
|
|
||||||
else
|
|
||||||
bluetoothctl power on
|
|
||||||
fi
|
|
||||||
( sleep 0.5; data=$(~/.config/eww/scripts/bluetooth.sh); eww update bt="$data" ) &
|
|
||||||
;;
|
|
||||||
disconnect)
|
|
||||||
bluetoothctl disconnect "$2"
|
|
||||||
( sleep 0.5; data=$(~/.config/eww/scripts/bluetooth.sh); eww update bt="$data" ) &
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# keyboard layout info as JSON for eww keyboard-popup
|
|
||||||
|
|
||||||
action="${1:-status}"
|
|
||||||
|
|
||||||
case "$action" in
|
|
||||||
status)
|
|
||||||
inputs=$(swaymsg -t get_inputs 2>/dev/null)
|
|
||||||
|
|
||||||
current=$(jq -r '[.[] | select(.type == "keyboard")] | .[0].xkb_active_layout_name // "unknown"' <<< "$inputs")
|
|
||||||
layouts=$(jq -c '[.[] | select(.type == "keyboard")] | .[0].xkb_layout_names // []' <<< "$inputs")
|
|
||||||
layout_count=$(jq 'length' <<< "$layouts" 2>/dev/null || echo 0)
|
|
||||||
|
|
||||||
keyboards=$(jq -c '[.[] | select(.type == "keyboard") |
|
|
||||||
{id: .identifier, name: .name, layout: .xkb_active_layout_name}] | unique_by(.name)' <<< "$inputs")
|
|
||||||
kb_count=$(jq 'length' <<< "$keyboards" 2>/dev/null || echo 0)
|
|
||||||
|
|
||||||
jq -nc \
|
|
||||||
--arg current "${current:-unknown}" \
|
|
||||||
--argjson layouts "${layouts:-[]}" \
|
|
||||||
--argjson layout_count "${layout_count:-0}" \
|
|
||||||
--argjson keyboards "${keyboards:-[]}" \
|
|
||||||
--argjson kb_count "${kb_count:-0}" \
|
|
||||||
'{$current,$layouts,$layout_count,$keyboards,$kb_count}'
|
|
||||||
;;
|
|
||||||
switch)
|
|
||||||
swaymsg input type:keyboard xkb_switch_layout next 2>/dev/null
|
|
||||||
( sleep 0.3; data=$(~/.config/eww/scripts/keyboard.sh); eww update kbd="$data" ) &
|
|
||||||
;;
|
|
||||||
set-layout)
|
|
||||||
swaymsg input type:keyboard xkb_switch_layout "$2" 2>/dev/null
|
|
||||||
( sleep 0.3; data=$(~/.config/eww/scripts/keyboard.sh); eww update kbd="$data" ) &
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# media player info as JSON for eww media-popup
|
|
||||||
|
|
||||||
action="${1:-status}"
|
|
||||||
|
|
||||||
case "$action" in
|
|
||||||
status)
|
|
||||||
players=$(playerctl -l 2>/dev/null | head -10)
|
|
||||||
if [[ -z "$players" ]]; then
|
|
||||||
jq -nc '{count:0,players:[]}'
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
result="[]"
|
|
||||||
while IFS= read -r name; do
|
|
||||||
status=$(playerctl -p "$name" status 2>/dev/null || echo "Stopped")
|
|
||||||
artist=$(playerctl -p "$name" metadata artist 2>/dev/null || echo "")
|
|
||||||
title=$(playerctl -p "$name" metadata title 2>/dev/null || echo "")
|
|
||||||
album=$(playerctl -p "$name" metadata album 2>/dev/null || echo "")
|
|
||||||
|
|
||||||
# clean up player name for display
|
|
||||||
display=${name%%.*}
|
|
||||||
|
|
||||||
result=$(jq -c --arg name "$name" --arg display "$display" \
|
|
||||||
--arg status "$status" --arg artist "$artist" \
|
|
||||||
--arg title "$title" --arg album "$album" \
|
|
||||||
'. + [{name:$name, display:$display, status:$status, artist:$artist, title:$title, album:$album}]' <<< "$result")
|
|
||||||
done <<< "$players"
|
|
||||||
|
|
||||||
count=$(jq 'length' <<< "$result")
|
|
||||||
jq -nc --argjson count "$count" --argjson players "$result" '{$count,$players}'
|
|
||||||
;;
|
|
||||||
play-pause)
|
|
||||||
playerctl -p "$2" play-pause 2>/dev/null
|
|
||||||
( sleep 0.3; data=$(~/.config/eww/scripts/media.sh); eww update media="$data" ) &
|
|
||||||
;;
|
|
||||||
next)
|
|
||||||
playerctl -p "$2" next 2>/dev/null
|
|
||||||
( sleep 0.5; data=$(~/.config/eww/scripts/media.sh); eww update media="$data" ) &
|
|
||||||
;;
|
|
||||||
prev)
|
|
||||||
playerctl -p "$2" previous 2>/dev/null
|
|
||||||
( sleep 0.5; data=$(~/.config/eww/scripts/media.sh); eww update media="$data" ) &
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# network info as JSON for eww network-popup
|
|
||||||
|
|
||||||
action="${1:-status}"
|
|
||||||
|
|
||||||
case "$action" in
|
|
||||||
status)
|
|
||||||
active=$(nmcli -t -f DEVICE,TYPE,STATE,CONNECTION device status 2>/dev/null \
|
|
||||||
| grep ':connected:' | head -1)
|
|
||||||
iface=$(echo "$active" | cut -d: -f1)
|
|
||||||
conn_type=$(echo "$active" | cut -d: -f2)
|
|
||||||
conn_name=$(echo "$active" | cut -d: -f4-)
|
|
||||||
|
|
||||||
ip=$(ip -4 -o addr show "$iface" 2>/dev/null | awk '{print $4}' | cut -d/ -f1)
|
|
||||||
gateway=$(ip route show default dev "$iface" 2>/dev/null | awk '{print $3}')
|
|
||||||
|
|
||||||
networks="[]"
|
|
||||||
net_count=0
|
|
||||||
|
|
||||||
# always check active wifi regardless of primary connection type
|
|
||||||
ssid=$(nmcli -t -f active,ssid dev wifi 2>/dev/null | grep '^yes' | cut -d: -f2-)
|
|
||||||
signal=$(nmcli -t -f active,signal dev wifi 2>/dev/null | grep '^yes' | cut -d: -f2-)
|
|
||||||
signal=${signal:-0}
|
|
||||||
|
|
||||||
# scan nearby wifi
|
|
||||||
saved=$(nmcli -t -f NAME connection show 2>/dev/null | sort -u)
|
|
||||||
# replace last 3 colons with tabs to handle SSIDs containing colons
|
|
||||||
all_wifi=$(nmcli -t -f SSID,SIGNAL,SECURITY,IN-USE dev wifi list --rescan no 2>/dev/null \
|
|
||||||
| sed 's/:\([^:]*\):\([^:]*\):\([^:]*\)$/\t\1\t\2\t\3/' \
|
|
||||||
| awk -F'\t' 'NF>=3 && $1!=""' \
|
|
||||||
| sort -t$'\t' -k4,4r -k2,2rn \
|
|
||||||
| awk -F'\t' '!seen[$1]++')
|
|
||||||
|
|
||||||
# known networks nearby
|
|
||||||
networks=$(echo "$all_wifi" \
|
|
||||||
| while IFS=$'\t' read -r s sig sec use; do
|
|
||||||
echo "$saved" | grep -qxF "$s" && printf '%s\t%s\n' "$s" "$sig"
|
|
||||||
done \
|
|
||||||
| head -10 \
|
|
||||||
| jq -Rnc --arg active "$ssid" '[inputs | split("\t") |
|
|
||||||
{ssid:.[0], signal:(.[1]|tonumber), active:(.[0] == $active)}]')
|
|
||||||
net_count=$(jq 'length' <<< "$networks" 2>/dev/null || echo 0)
|
|
||||||
|
|
||||||
# unknown networks nearby
|
|
||||||
unknown=$(echo "$all_wifi" \
|
|
||||||
| while IFS=$'\t' read -r s sig sec use; do
|
|
||||||
echo "$saved" | grep -qxF "$s" || printf '%s\t%s\t%s\n' "$s" "$sig" "$sec"
|
|
||||||
done \
|
|
||||||
| head -5 \
|
|
||||||
| jq -Rnc '[inputs | split("\t") |
|
|
||||||
{ssid:.[0], signal:(.[1]|tonumber), security:.[2]}]')
|
|
||||||
unknown_count=$(jq 'length' <<< "$unknown" 2>/dev/null || echo 0)
|
|
||||||
|
|
||||||
jq -nc \
|
|
||||||
--arg type "${conn_type:-none}" \
|
|
||||||
--arg iface "${iface:-none}" \
|
|
||||||
--arg ip "${ip:-none}" \
|
|
||||||
--arg gateway "${gateway:-none}" \
|
|
||||||
--arg ssid "$ssid" \
|
|
||||||
--argjson signal "${signal:-0}" \
|
|
||||||
--arg conn_name "$conn_name" \
|
|
||||||
--argjson count "${net_count:-0}" \
|
|
||||||
--argjson networks "${networks:-[]}" \
|
|
||||||
--argjson unknown_count "${unknown_count:-0}" \
|
|
||||||
--argjson unknown "${unknown:-[]}" \
|
|
||||||
'{$type,$iface,$ip,$gateway,$ssid,$signal,$conn_name,$count,$networks,$unknown_count,$unknown}'
|
|
||||||
;;
|
|
||||||
connect)
|
|
||||||
( nmcli dev wifi connect "$2" 2>/dev/null; sleep 1; data=$(~/.config/eww/scripts/network.sh); eww update net="$data" ) &
|
|
||||||
;;
|
|
||||||
disconnect)
|
|
||||||
( nmcli connection down "$2" 2>/dev/null; sleep 1; data=$(~/.config/eww/scripts/network.sh); eww update net="$data" ) &
|
|
||||||
;;
|
|
||||||
connect-new)
|
|
||||||
ssid="$2"
|
|
||||||
pass=$(zenity --entry --hide-text --title="WiFi" --text="Password for $ssid" 2>/dev/null)
|
|
||||||
( [[ -n "$pass" ]] && nmcli dev wifi connect "$ssid" password "$pass" 2>/dev/null; sleep 1; data=$(~/.config/eww/scripts/network.sh); eww update net="$data" ) &
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# toggle an eww popup
|
|
||||||
# opens on the currently focused monitor
|
|
||||||
|
|
||||||
POPUPS=(system-popup battery-popup network-popup vpn-popup volume-popup bluetooth-popup keyboard-popup media-popup)
|
|
||||||
|
|
||||||
target="$1"
|
|
||||||
|
|
||||||
if [[ -z "$target" ]]; then
|
|
||||||
echo "usage: popup.sh <popup-name|close-all>" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$target" == "close-all" ]]; then
|
|
||||||
eww close "${POPUPS[@]}" 2>/dev/null
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# check if target is already open
|
|
||||||
if eww active-windows 2>/dev/null | grep -q "$target"; then
|
|
||||||
eww close "$target" 2>/dev/null
|
|
||||||
else
|
|
||||||
# close others, open popup
|
|
||||||
screen=$(swaymsg -t get_outputs 2>/dev/null \
|
|
||||||
| jq '[.[] | .focused] | index(true) // 0')
|
|
||||||
eww close "${POPUPS[@]}" 2>/dev/null
|
|
||||||
eww open --screen "${screen:-0}" "$target"
|
|
||||||
fi
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# system metrics as JSON for eww system-popup
|
|
||||||
|
|
||||||
# delta-based cpu: sample /proc/stat twice 0.5s apart
|
|
||||||
read -r _ u1 n1 s1 i1 _ < /proc/stat
|
|
||||||
sleep 0.5
|
|
||||||
read -r _ u2 n2 s2 i2 _ < /proc/stat
|
|
||||||
cpu=$(( (u2+n2+s2 - u1-n1-s1) * 100 / (u2+n2+s2+i2 - u1-n1-s1-i1) ))
|
|
||||||
|
|
||||||
ram_info=$(free -b | awk '/^Mem:/{printf "%.0f %.1f %.1f", $3/$2*100, $3/1073741824, $2/1073741824}')
|
|
||||||
ram_percent=$(awk '{print $1}' <<< "$ram_info")
|
|
||||||
ram_used=$(awk '{printf "%.1f", $2}' <<< "$ram_info")
|
|
||||||
ram_total=$(awk '{printf "%.1f", $3}' <<< "$ram_info")
|
|
||||||
|
|
||||||
temp=$(cat /sys/class/thermal/thermal_zone*/temp 2>/dev/null | sort -rn | head -1)
|
|
||||||
temp=$(( ${temp:-0} / 1000 ))
|
|
||||||
|
|
||||||
disk_info=$(df -h / | awk 'NR==2{gsub(/%/,""); printf "%s %s %s", $5, $3, $2}')
|
|
||||||
disk_percent=$(awk '{print $1}' <<< "$disk_info")
|
|
||||||
disk_used=$(awk '{print $2}' <<< "$disk_info")
|
|
||||||
disk_total=$(awk '{print $3}' <<< "$disk_info")
|
|
||||||
|
|
||||||
swap=$(free -h | awk '/^Swap:/{printf "%s/%s", $3, $2}')
|
|
||||||
load=$(awk '{print $1, $2, $3}' /proc/loadavg)
|
|
||||||
uptime_str=$(uptime -p 2>/dev/null | sed 's/up //' || echo "n/a")
|
|
||||||
|
|
||||||
jq -nc \
|
|
||||||
--argjson cpu "${cpu:-0}" \
|
|
||||||
--argjson ram_percent "${ram_percent:-0}" \
|
|
||||||
--arg ram_used "${ram_used:-0}Gi" \
|
|
||||||
--arg ram_total "${ram_total:-0}Gi" \
|
|
||||||
--argjson temp "${temp:-0}" \
|
|
||||||
--argjson disk_percent "${disk_percent:-0}" \
|
|
||||||
--arg disk_used "$disk_used" \
|
|
||||||
--arg disk_total "$disk_total" \
|
|
||||||
--arg swap "$swap" \
|
|
||||||
--arg load "$load" \
|
|
||||||
--arg uptime "$uptime_str" \
|
|
||||||
'{$cpu,$ram_percent,$ram_used,$ram_total,$temp,$disk_percent,$disk_used,$disk_total,$swap,$load,$uptime}'
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# audio + brightness info for eww volume-popup
|
|
||||||
|
|
||||||
action="${1:-status}"
|
|
||||||
|
|
||||||
case "$action" in
|
|
||||||
status)
|
|
||||||
default_sink=$(pactl get-default-sink 2>/dev/null)
|
|
||||||
default_source=$(pactl get-default-source 2>/dev/null)
|
|
||||||
|
|
||||||
# get all sink+source info in one pactl call, extract volume/mute + device lists
|
|
||||||
all_data=$(pactl --format=json list sinks 2>/dev/null)
|
|
||||||
sinks=$(jq -c --arg d "$default_sink" \
|
|
||||||
'[.[] | {name: .description, sink_name: .name, active: (.name == $d)}]' <<< "$all_data" 2>/dev/null || echo '[]')
|
|
||||||
# extract default sink volume+mute
|
|
||||||
volume=$(jq --arg d "$default_sink" \
|
|
||||||
'[.[] | select(.name == $d)][0] | .volume | to_entries[0].value.value_percent | rtrimstr("%") | tonumber' <<< "$all_data" 2>/dev/null || echo 0)
|
|
||||||
muted=$(jq --arg d "$default_sink" \
|
|
||||||
'[.[] | select(.name == $d)][0].mute' <<< "$all_data" 2>/dev/null || echo false)
|
|
||||||
|
|
||||||
all_sources=$(pactl --format=json list sources 2>/dev/null)
|
|
||||||
sources=$(jq -c --arg d "$default_source" \
|
|
||||||
'[.[] | select(.name | test("monitor$") | not) | {name: .description, source_name: .name, active: (.name == $d)}]' <<< "$all_sources" 2>/dev/null || echo '[]')
|
|
||||||
mic_volume=$(jq --arg d "$default_source" \
|
|
||||||
'[.[] | select(.name == $d)][0] | .volume | to_entries[0].value.value_percent | rtrimstr("%") | tonumber' <<< "$all_sources" 2>/dev/null || echo 0)
|
|
||||||
mic_muted=$(jq --arg d "$default_source" \
|
|
||||||
'[.[] | select(.name == $d)][0].mute' <<< "$all_sources" 2>/dev/null || echo false)
|
|
||||||
|
|
||||||
brightness=$(brightnessctl -m 2>/dev/null | cut -d, -f5 | tr -d '%')
|
|
||||||
sink_count=$(jq 'length' <<< "$sinks" 2>/dev/null || echo 0)
|
|
||||||
source_count=$(jq 'length' <<< "$sources" 2>/dev/null || echo 0)
|
|
||||||
|
|
||||||
jq -nc \
|
|
||||||
--argjson volume "${volume:-0}" \
|
|
||||||
--argjson muted "${muted:-false}" \
|
|
||||||
--argjson mic_volume "${mic_volume:-0}" \
|
|
||||||
--argjson mic_muted "${mic_muted:-false}" \
|
|
||||||
--argjson brightness "${brightness:-0}" \
|
|
||||||
--argjson sinks "${sinks:-[]}" \
|
|
||||||
--argjson sources "${sources:-[]}" \
|
|
||||||
--argjson sink_count "${sink_count:-0}" \
|
|
||||||
--argjson source_count "${source_count:-0}" \
|
|
||||||
'{$volume,$muted,$mic_volume,$mic_muted,$brightness,$sinks,$sources,$sink_count,$source_count}'
|
|
||||||
;;
|
|
||||||
set-sink)
|
|
||||||
pactl set-default-sink "$2"
|
|
||||||
( data=$(~/.config/eww/scripts/volume.sh); eww update vol="$data" ) &
|
|
||||||
;;
|
|
||||||
set-source)
|
|
||||||
pactl set-default-source "$2"
|
|
||||||
( data=$(~/.config/eww/scripts/volume.sh); eww update vol="$data" ) &
|
|
||||||
;;
|
|
||||||
toggle-mute)
|
|
||||||
pamixer -t
|
|
||||||
( data=$(~/.config/eww/scripts/volume.sh); eww update vol="$data" ) &
|
|
||||||
;;
|
|
||||||
toggle-mic)
|
|
||||||
pamixer --default-source -t
|
|
||||||
( data=$(~/.config/eww/scripts/volume.sh); eww update vol="$data" ) &
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# vpn status as JSON for eww vpn-popup
|
|
||||||
|
|
||||||
action="${1:-status}"
|
|
||||||
|
|
||||||
case "$action" in
|
|
||||||
status)
|
|
||||||
ts_running=false
|
|
||||||
ts_ip=""
|
|
||||||
ts_hostname=""
|
|
||||||
ts_login=""
|
|
||||||
ts_exit_nodes="[]"
|
|
||||||
ts_exit_count=0
|
|
||||||
ts_peers="[]"
|
|
||||||
ts_peer_count=0
|
|
||||||
|
|
||||||
if command -v tailscale &>/dev/null; then
|
|
||||||
ts_json=$(tailscale status --json 2>/dev/null)
|
|
||||||
state=$(jq -r '.BackendState // empty' <<< "$ts_json")
|
|
||||||
|
|
||||||
if [[ "$state" == "Running" ]]; then
|
|
||||||
ts_running=true
|
|
||||||
ts_ip=$(jq -r '.TailscaleIPs[0] // empty' <<< "$ts_json")
|
|
||||||
ts_hostname=$(jq -r '.Self.HostName // empty' <<< "$ts_json")
|
|
||||||
ts_login=$(jq -r '.CurrentTailnet.Name // empty' <<< "$ts_json")
|
|
||||||
|
|
||||||
ts_exit_nodes=$(jq -c '[.Peer | to_entries[]? |
|
|
||||||
select(.value.ExitNodeOption) |
|
|
||||||
{id: .key, name: .value.HostName, ip: (.value.TailscaleIPs[0] // ""), active: (.value.ExitNode // false)}
|
|
||||||
] // []' <<< "$ts_json" 2>/dev/null || echo '[]')
|
|
||||||
ts_exit_count=$(jq 'length' <<< "$ts_exit_nodes" 2>/dev/null || echo 0)
|
|
||||||
|
|
||||||
ts_peers=$(jq -c '[.Peer | to_entries[]? |
|
|
||||||
{name: .value.HostName, ip: (.value.TailscaleIPs[0] // ""), online: .value.Online}
|
|
||||||
] // []' <<< "$ts_json" 2>/dev/null || echo '[]')
|
|
||||||
ts_peer_count=$(jq 'length' <<< "$ts_peers" 2>/dev/null || echo 0)
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
wg_active=false
|
|
||||||
wg_iface=""
|
|
||||||
if command -v wg &>/dev/null; then
|
|
||||||
wg_iface=$(wg show interfaces 2>/dev/null | head -1)
|
|
||||||
[[ -n "$wg_iface" ]] && wg_active=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
jq -nc \
|
|
||||||
--argjson ts_running "$ts_running" \
|
|
||||||
--arg ts_ip "$ts_ip" \
|
|
||||||
--arg ts_hostname "$ts_hostname" \
|
|
||||||
--arg ts_login "$ts_login" \
|
|
||||||
--argjson ts_exit_nodes "$ts_exit_nodes" \
|
|
||||||
--argjson ts_exit_count "$ts_exit_count" \
|
|
||||||
--argjson ts_peers "$ts_peers" \
|
|
||||||
--argjson ts_peer_count "$ts_peer_count" \
|
|
||||||
--argjson wg_active "$wg_active" \
|
|
||||||
--arg wg_iface "$wg_iface" \
|
|
||||||
'{tailscale:{running:$ts_running,ip:$ts_ip,hostname:$ts_hostname,login:$ts_login,
|
|
||||||
exit_nodes:$ts_exit_nodes,exit_count:$ts_exit_count,
|
|
||||||
peers:$ts_peers,peer_count:$ts_peer_count},
|
|
||||||
wireguard:{active:$wg_active,iface:$wg_iface}}'
|
|
||||||
;;
|
|
||||||
ts-up)
|
|
||||||
tailscale up 2>/dev/null
|
|
||||||
( sleep 1; data=$(~/.config/eww/scripts/vpn.sh); eww update vpn_data="$data" ) &
|
|
||||||
;;
|
|
||||||
ts-down)
|
|
||||||
tailscale down 2>/dev/null
|
|
||||||
( sleep 1; data=$(~/.config/eww/scripts/vpn.sh); eww update vpn_data="$data" ) &
|
|
||||||
;;
|
|
||||||
ts-exit)
|
|
||||||
if [[ -n "$2" ]]; then
|
|
||||||
tailscale set --exit-node="$2" 2>/dev/null
|
|
||||||
else
|
|
||||||
tailscale set --exit-node="" 2>/dev/null
|
|
||||||
fi
|
|
||||||
( sleep 0.5; data=$(~/.config/eww/scripts/vpn.sh); eww update vpn_data="$data" ) &
|
|
||||||
;;
|
|
||||||
wg-up)
|
|
||||||
sudo wg-quick up "${2:-wg0}" 2>/dev/null
|
|
||||||
( sleep 1; data=$(~/.config/eww/scripts/vpn.sh); eww update vpn_data="$data" ) &
|
|
||||||
;;
|
|
||||||
wg-down)
|
|
||||||
iface="${2:-$(wg show interfaces 2>/dev/null | head -1)}"
|
|
||||||
sudo wg-quick down "${iface:-wg0}" 2>/dev/null
|
|
||||||
( sleep 1; data=$(~/.config/eww/scripts/vpn.sh); eww update vpn_data="$data" ) &
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
[General]
|
|
||||||
contrastOpacity=188
|
|
||||||
contrastUiColor=#000000
|
|
||||||
disabledGrimWarning=true
|
|
||||||
drawColor=#00ffff
|
|
||||||
saveAfterCopy=true
|
|
||||||
savePath=/home/matej/screens
|
|
||||||
showHelp=false
|
|
||||||
showMagnifier=true
|
|
||||||
showSelectionGeometryHideTime=3000
|
|
||||||
uiColor=#ffffff
|
|
||||||
useGrimAdapter=true
|
|
||||||
|
|
||||||
[Shortcuts]
|
|
||||||
TYPE_ACCEPT=
|
|
||||||
TYPE_COPY=Space
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
[main]
|
[main]
|
||||||
font=JetBrainsMono Nerd Font:size=14
|
font=JetBrainsMono Nerd Font:size=10
|
||||||
prompt=" "
|
prompt=" "
|
||||||
icons=no
|
icons=no
|
||||||
terminal=ghostty -e
|
terminal=ghostty -e
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ CLAUDE.local.md
|
|||||||
|
|
||||||
# nix
|
# nix
|
||||||
.nix-venv
|
.nix-venv
|
||||||
.dev
|
|
||||||
.envrc
|
.envrc
|
||||||
.direnv/
|
.direnv/
|
||||||
|
|
||||||
|
|||||||
28
mako/config
Normal file
28
mako/config
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# gruvbox-material-soft-dark
|
||||||
|
font=JetBrainsMono Nerd Font 11
|
||||||
|
background-color=#252423
|
||||||
|
text-color=#d4be98
|
||||||
|
border-color=#45403d
|
||||||
|
border-size=2
|
||||||
|
border-radius=8
|
||||||
|
padding=12
|
||||||
|
|
||||||
|
width=450
|
||||||
|
height=300
|
||||||
|
margin=8
|
||||||
|
max-visible=3
|
||||||
|
|
||||||
|
default-timeout=10000
|
||||||
|
ignore-timeout=0
|
||||||
|
group-by=app-name
|
||||||
|
max-icon-size=32
|
||||||
|
icon-path=/usr/share/icons/Adwaita
|
||||||
|
layer=overlay
|
||||||
|
anchor=top-right
|
||||||
|
|
||||||
|
[urgency=low]
|
||||||
|
default-timeout=5000
|
||||||
|
|
||||||
|
[urgency=critical]
|
||||||
|
default-timeout=0
|
||||||
|
border-color=#ea6962
|
||||||
@@ -8,18 +8,15 @@ bindsym $mod+Return exec $term
|
|||||||
bindsym $mod+d exec $menu
|
bindsym $mod+d exec $menu
|
||||||
bindsym $mod+u exec $lockscreen
|
bindsym $mod+u exec $lockscreen
|
||||||
bindsym $mod+Shift+Ctrl+Alt+space exec 1password --quick-access
|
bindsym $mod+Shift+Ctrl+Alt+space exec 1password --quick-access
|
||||||
bindsym Print exec flameshot gui
|
bindsym Print exec ~/.config/sway/scripts/screenshot.sh
|
||||||
|
|
||||||
# notifications
|
# notifications
|
||||||
bindsym $mod+n exec swaync-client -t -sw
|
bindsym $mod+n exec makoctl dismiss
|
||||||
bindsym $mod+Shift+n exec swaync-client -C -sw
|
bindsym $mod+Shift+n exec makoctl dismiss --all
|
||||||
|
|
||||||
# clipboard
|
# clipboard
|
||||||
bindsym $mod+c exec cliphist list | fuzzel -d | cliphist decode | wl-copy
|
bindsym $mod+c exec cliphist list | fuzzel -d | cliphist decode | wl-copy
|
||||||
|
|
||||||
# eww popups
|
|
||||||
bindsym $mod+Escape exec ~/.config/eww/scripts/popup.sh close-all
|
|
||||||
|
|
||||||
# window
|
# window
|
||||||
bindsym $mod+Shift+q kill
|
bindsym $mod+Shift+q kill
|
||||||
|
|
||||||
@@ -59,16 +56,16 @@ bindsym $mod+Shift+8 move container to workspace number 8
|
|||||||
bindsym $mod+Shift+9 move container to workspace number 9
|
bindsym $mod+Shift+9 move container to workspace number 9
|
||||||
bindsym $mod+Shift+0 move container to workspace number 10
|
bindsym $mod+Shift+0 move container to workspace number 10
|
||||||
|
|
||||||
bindsym $mod+Ctrl+1 workspace number 1 output current
|
bindsym $mod+Ctrl+1 exec ~/.config/sway/scripts/ws-to-current.sh 1
|
||||||
bindsym $mod+Ctrl+2 workspace number 2 output current
|
bindsym $mod+Ctrl+2 exec ~/.config/sway/scripts/ws-to-current.sh 2
|
||||||
bindsym $mod+Ctrl+3 workspace number 3 output current
|
bindsym $mod+Ctrl+3 exec ~/.config/sway/scripts/ws-to-current.sh 3
|
||||||
bindsym $mod+Ctrl+4 workspace number 4 output current
|
bindsym $mod+Ctrl+4 exec ~/.config/sway/scripts/ws-to-current.sh 4
|
||||||
bindsym $mod+Ctrl+5 workspace number 5 output current
|
bindsym $mod+Ctrl+5 exec ~/.config/sway/scripts/ws-to-current.sh 5
|
||||||
bindsym $mod+Ctrl+6 workspace number 6 output current
|
bindsym $mod+Ctrl+6 exec ~/.config/sway/scripts/ws-to-current.sh 6
|
||||||
bindsym $mod+Ctrl+7 workspace number 7 output current
|
bindsym $mod+Ctrl+7 exec ~/.config/sway/scripts/ws-to-current.sh 7
|
||||||
bindsym $mod+Ctrl+8 workspace number 8 output current
|
bindsym $mod+Ctrl+8 exec ~/.config/sway/scripts/ws-to-current.sh 8
|
||||||
bindsym $mod+Ctrl+9 workspace number 9 output current
|
bindsym $mod+Ctrl+9 exec ~/.config/sway/scripts/ws-to-current.sh 9
|
||||||
bindsym $mod+Ctrl+0 workspace number 10 output current
|
bindsym $mod+Ctrl+0 exec ~/.config/sway/scripts/ws-to-current.sh 10
|
||||||
|
|
||||||
# layout
|
# layout
|
||||||
bindsym $mod+b splith
|
bindsym $mod+b splith
|
||||||
|
|||||||
@@ -2,18 +2,21 @@
|
|||||||
for_window [floating] border pixel $border
|
for_window [floating] border pixel $border
|
||||||
for_window [tiling] border normal $border
|
for_window [tiling] border normal $border
|
||||||
|
|
||||||
|
for_window [app_id="com.mitchellh.ghostty" title="fastfetch"] floating enable, focus, move position 50 50
|
||||||
|
|
||||||
# workspace assignment
|
# workspace assignment
|
||||||
assign [app_id="google-chrome"] workspace 2
|
assign [app_id="google-chrome"] workspace 2
|
||||||
assign [app_id="thunderbird"] workspace 3
|
assign [app_id="thunderbird"] workspace 3
|
||||||
assign [app_id="Cider"] workspace 4
|
assign [app_id="Cider"] workspace 4
|
||||||
assign [app_id=".*BoltLauncher.*"] workspace 5
|
assign [app_id=".*BoltLauncher.*"] workspace 5
|
||||||
assign [class="net-runelite.*"] workspace 5
|
assign [class="net-runelite.*"] workspace 5
|
||||||
assign [app_id="vesktop"] workspace 8
|
assign [app_id="discord"] workspace 8
|
||||||
assign [app_id="Slack"] workspace 8
|
assign [app_id="Slack"] workspace 8
|
||||||
assign [app_id="org.telegram.desktop"] workspace 8
|
assign [app_id="org.telegram.desktop"] workspace 8
|
||||||
assign [class="Rocket.Chat"] workspace 8
|
assign [app_id="Rocket.Chat"] workspace 8
|
||||||
|
|
||||||
# rules
|
# rules
|
||||||
|
for_window [app_id="com.gabm.satty"] fullscreen enable
|
||||||
for_window [class="net-runelite.*"] floating enable
|
for_window [class="net-runelite.*"] floating enable
|
||||||
no_focus [class="net-runelite.*"]
|
no_focus [class="net-runelite.*"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
# daemon
|
# daemon
|
||||||
exec eww daemon
|
exec mako
|
||||||
exec swayidle -w
|
exec swayidle -w
|
||||||
exec wlsunset -l 46.1 -L 14.5
|
exec wlsunset -l 46.1 -L 14.5
|
||||||
exec pkill -x wob; rm -f $wobs && mkfifo $wobs && tail -f $wobs | wob
|
exec pkill -x wob; rm -f $wobs && mkfifo $wobs && tail -f $wobs | wob
|
||||||
exec swaync
|
|
||||||
exec wl-paste --watch cliphist store
|
exec wl-paste --watch cliphist store
|
||||||
|
exec ~/.config/sway/scripts/power-aware-effects.sh
|
||||||
exec protonmail-bridge -n
|
exec protonmail-bridge -n
|
||||||
|
|
||||||
# traditional "start when os starts" programs
|
# traditional "start when os starts" programs
|
||||||
exec $term
|
exec ghostty --title=fastfetch --window-width=105 --window-height=27 -e bash -c 'nix run nixpkgs#fastfetch; read -rsn1' && ghostty
|
||||||
exec $browser
|
exec $browser
|
||||||
exec thunderbird
|
exec thunderbird
|
||||||
exec cider-2
|
exec cider-2
|
||||||
exec bolt-launcher
|
exec bolt-launcher
|
||||||
exec vesktop
|
exec discord
|
||||||
exec slack
|
exec slack
|
||||||
exec Telegram
|
exec Telegram
|
||||||
exec rocketchat-desktop
|
exec rocketchat-desktop
|
||||||
|
|||||||
2
sway/host.d/fw16.conf
Normal file
2
sway/host.d/fw16.conf
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
output DP-7 resolution 2560x1440 position 2560,-1200
|
||||||
|
output eDP-1 resolution 2560x1600@165Hz position 0,0 adaptive_sync on
|
||||||
16
sway/scripts/power-aware-effects.sh
Executable file
16
sway/scripts/power-aware-effects.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# toggle swayfx blur and shadows based on battery state
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
status=$(cat /sys/class/power_supply/BAT1/status 2>/dev/null || cat /sys/class/power_supply/BAT0/status)
|
||||||
|
|
||||||
|
if [ "$status" = "Discharging" ]; then
|
||||||
|
swaymsg blur off
|
||||||
|
swaymsg shadows off
|
||||||
|
else
|
||||||
|
swaymsg blur on
|
||||||
|
swaymsg shadows on
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 30
|
||||||
|
done
|
||||||
6
sway/scripts/screenshot.sh
Executable file
6
sway/scripts/screenshot.sh
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# screenshot focused monitor with satty
|
||||||
|
output=$(swaymsg -t get_outputs | jq -r '.[] | select(.focused) | .name')
|
||||||
|
grim -o "$output" - | satty -f - --fullscreen --early-exit
|
||||||
|
|
||||||
4
sway/scripts/ws-to-current.sh
Executable file
4
sway/scripts/ws-to-current.sh
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# switch to workspace and move it to the currently focused output
|
||||||
|
output=$(swaymsg -t get_outputs | jq -r '.[] | select(.focused) | .name')
|
||||||
|
swaymsg "workspace number $1, move workspace to output $output"
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
{
|
|
||||||
"positionX": "right",
|
|
||||||
"positionY": "top",
|
|
||||||
"layer": "overlay",
|
|
||||||
"control-center-layer": "overlay",
|
|
||||||
"cssPriority": "application",
|
|
||||||
|
|
||||||
"control-center-width": 400,
|
|
||||||
"control-center-height": 600,
|
|
||||||
"control-center-margin-top": 3,
|
|
||||||
"control-center-margin-right": 3,
|
|
||||||
"control-center-margin-bottom": 3,
|
|
||||||
|
|
||||||
"notification-window-width": 400,
|
|
||||||
"notification-icon-size": 48,
|
|
||||||
"notification-body-image-height": 100,
|
|
||||||
"notification-body-image-width": 200,
|
|
||||||
|
|
||||||
"timeout": 10,
|
|
||||||
"timeout-low": 5,
|
|
||||||
"timeout-critical": 0,
|
|
||||||
"transition-time": 200,
|
|
||||||
|
|
||||||
"notification-grouping": true,
|
|
||||||
"image-visibility": "when-available",
|
|
||||||
"relative-timestamps": true,
|
|
||||||
"keyboard-shortcuts": true,
|
|
||||||
"hide-on-clear": true,
|
|
||||||
"hide-on-action": true,
|
|
||||||
"fit-to-screen": true,
|
|
||||||
|
|
||||||
"widgets": [
|
|
||||||
"title",
|
|
||||||
"dnd",
|
|
||||||
"notifications"
|
|
||||||
],
|
|
||||||
"widget-config": {
|
|
||||||
"title": {
|
|
||||||
"text": "notifications",
|
|
||||||
"clear-all-button": true,
|
|
||||||
"button-text": "clear"
|
|
||||||
},
|
|
||||||
"dnd": {
|
|
||||||
"text": "do not disturb"
|
|
||||||
},
|
|
||||||
"notifications": {
|
|
||||||
"vexpand": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
163
swaync/style.css
163
swaync/style.css
@@ -1,163 +0,0 @@
|
|||||||
/* gruvbox-material-soft-dark */
|
|
||||||
@define-color bg #32302f;
|
|
||||||
@define-color bg_dim #252423;
|
|
||||||
@define-color bg_sel #45403d;
|
|
||||||
@define-color fg #d4be98;
|
|
||||||
@define-color red #ea6962;
|
|
||||||
@define-color green #a9b665;
|
|
||||||
@define-color yellow #d8a657;
|
|
||||||
@define-color blue #7daea3;
|
|
||||||
@define-color magenta #d3869b;
|
|
||||||
@define-color cyan #89b482;
|
|
||||||
@define-color gray #928374;
|
|
||||||
|
|
||||||
* {
|
|
||||||
font-family: "JetBrainsMono Nerd Font";
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* floating notifications */
|
|
||||||
.floating-notifications {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification {
|
|
||||||
background-color: @bg;
|
|
||||||
border: 2px solid @bg_sel;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin: 4px;
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification.critical {
|
|
||||||
border-color: @red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .summary {
|
|
||||||
color: @fg;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .body {
|
|
||||||
color: @gray;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .time {
|
|
||||||
color: @gray;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .image {
|
|
||||||
margin-right: 8px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-button {
|
|
||||||
background-color: @bg_sel;
|
|
||||||
color: @fg;
|
|
||||||
border-radius: 50%;
|
|
||||||
min-width: 24px;
|
|
||||||
min-height: 24px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-button:hover {
|
|
||||||
background-color: @red;
|
|
||||||
color: @bg_dim;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* action buttons */
|
|
||||||
.notification .text-button {
|
|
||||||
background-color: @bg_sel;
|
|
||||||
color: @fg;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 4px 8px;
|
|
||||||
margin: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .text-button:hover {
|
|
||||||
background-color: @blue;
|
|
||||||
color: @bg_dim;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* control center panel */
|
|
||||||
.control-center {
|
|
||||||
background-color: @bg_dim;
|
|
||||||
border: 2px solid @bg_sel;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-center .notification-row {
|
|
||||||
background-color: transparent;
|
|
||||||
margin: 2px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-center .notification-row .notification {
|
|
||||||
background-color: @bg;
|
|
||||||
border: 1px solid @bg_sel;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-center .notification-row:hover .notification {
|
|
||||||
border-color: @gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* title widget */
|
|
||||||
.widget-title {
|
|
||||||
color: @fg;
|
|
||||||
padding: 4px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-title button {
|
|
||||||
background-color: @bg_sel;
|
|
||||||
color: @fg;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 4px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-title button:hover {
|
|
||||||
background-color: @red;
|
|
||||||
color: @bg_dim;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* dnd toggle */
|
|
||||||
.widget-dnd {
|
|
||||||
color: @fg;
|
|
||||||
padding: 4px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-dnd > switch {
|
|
||||||
background-color: @bg_sel;
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-dnd > switch:checked {
|
|
||||||
background-color: @yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.widget-dnd > switch slider {
|
|
||||||
background-color: @fg;
|
|
||||||
border-radius: 50%;
|
|
||||||
min-width: 20px;
|
|
||||||
min-height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* empty state */
|
|
||||||
.widget-notifications > label {
|
|
||||||
color: @gray;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* notification group */
|
|
||||||
.notification-group {
|
|
||||||
background-color: @bg;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-group .notification-group-headers {
|
|
||||||
padding: 4px 8px;
|
|
||||||
color: @gray;
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ set -g default-terminal "tmux-256color"
|
|||||||
set -ga terminal-overrides ",xterm-ghostty:RGB,xterm-256color:RGB"
|
set -ga terminal-overrides ",xterm-ghostty:RGB,xterm-256color:RGB"
|
||||||
set -ga terminal-features ",xterm-ghostty:clipboard,xterm-256color:clipboard"
|
set -ga terminal-features ",xterm-ghostty:clipboard,xterm-256color:clipboard"
|
||||||
set -g set-clipboard on
|
set -g set-clipboard on
|
||||||
|
set -g allow-passthrough on
|
||||||
|
|
||||||
# general
|
# general
|
||||||
set -g prefix C-s
|
set -g prefix C-s
|
||||||
|
|||||||
@@ -11,12 +11,10 @@
|
|||||||
],
|
],
|
||||||
"modules-center": [
|
"modules-center": [
|
||||||
"custom/ssh",
|
"custom/ssh",
|
||||||
"custom/notification",
|
|
||||||
"systemd-failed-units"
|
"systemd-failed-units"
|
||||||
],
|
],
|
||||||
"modules-right": [
|
"modules-right": [
|
||||||
"mpris",
|
"mpris",
|
||||||
"custom/vpn",
|
|
||||||
"cpu",
|
"cpu",
|
||||||
"memory",
|
"memory",
|
||||||
"battery",
|
"battery",
|
||||||
@@ -25,7 +23,7 @@
|
|||||||
"backlight",
|
"backlight",
|
||||||
"sway/language",
|
"sway/language",
|
||||||
"bluetooth",
|
"bluetooth",
|
||||||
"network",
|
"custom/network",
|
||||||
"clock"
|
"clock"
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -40,8 +38,7 @@
|
|||||||
|
|
||||||
"sway/language": {
|
"sway/language": {
|
||||||
"format": "{short}",
|
"format": "{short}",
|
||||||
"on-click": "swaymsg input type:keyboard xkb_switch_layout next",
|
"on-click": "swaymsg input type:keyboard xkb_switch_layout next"
|
||||||
"on-click-right": "~/.config/eww/scripts/popup.sh keyboard-popup"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"custom/ssh": {
|
"custom/ssh": {
|
||||||
@@ -52,15 +49,6 @@
|
|||||||
"on-click": "~/.config/waybar/scripts/ssh-session.sh disconnect"
|
"on-click": "~/.config/waybar/scripts/ssh-session.sh disconnect"
|
||||||
},
|
},
|
||||||
|
|
||||||
"custom/notification": {
|
|
||||||
"format": "{}",
|
|
||||||
"return-type": "json",
|
|
||||||
"exec": "~/.config/waybar/scripts/notification-status.sh",
|
|
||||||
"on-click": "sleep 0.1 && swaync-client -t -sw",
|
|
||||||
"on-click-right": "sleep 0.1 && swaync-client -d -sw",
|
|
||||||
"escape": true
|
|
||||||
},
|
|
||||||
|
|
||||||
"systemd-failed-units": {
|
"systemd-failed-units": {
|
||||||
"hide-on-ok": true,
|
"hide-on-ok": true,
|
||||||
"format": "!{nr_failed}",
|
"format": "!{nr_failed}",
|
||||||
@@ -70,7 +58,6 @@
|
|||||||
"on-click": "ghostty -e sh -c 'systemctl --failed; systemctl --user --failed; read'"
|
"on-click": "ghostty -e sh -c 'systemctl --failed; systemctl --user --failed; read'"
|
||||||
},
|
},
|
||||||
|
|
||||||
// right: media
|
|
||||||
"mpris": {
|
"mpris": {
|
||||||
"format": "{artist} - {title}",
|
"format": "{artist} - {title}",
|
||||||
"format-paused": "{artist} - {title} [paused]",
|
"format-paused": "{artist} - {title} [paused]",
|
||||||
@@ -78,53 +65,35 @@
|
|||||||
"max-length": 35,
|
"max-length": 35,
|
||||||
"tooltip-format": "{player}: {artist} - {title} ({album})",
|
"tooltip-format": "{player}: {artist} - {title} ({album})",
|
||||||
"on-click": "playerctl play-pause",
|
"on-click": "playerctl play-pause",
|
||||||
"on-click-right": "~/.config/eww/scripts/popup.sh media-popup",
|
|
||||||
"on-scroll-up": "playerctl next",
|
"on-scroll-up": "playerctl next",
|
||||||
"on-scroll-down": "playerctl previous"
|
"on-scroll-down": "playerctl previous"
|
||||||
},
|
},
|
||||||
|
|
||||||
// right: connectivity
|
"custom/network": {
|
||||||
"network": {
|
"format": "{}",
|
||||||
|
"return-type": "json",
|
||||||
"interval": 5,
|
"interval": 5,
|
||||||
"format-ethernet": " {ipaddr}",
|
"exec": "~/.config/waybar/scripts/network-status.sh"
|
||||||
"format-wifi": " {ipaddr}",
|
|
||||||
"format-linked": " (no ip)",
|
|
||||||
"format-disconnected": "",
|
|
||||||
"tooltip-format": "{ifname} {ipaddr}/{cidr}\n{gwaddr}\n{bandwidthUpBits}up {bandwidthDownBits}down",
|
|
||||||
"on-click-right": "~/.config/eww/scripts/popup.sh network-popup"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"bluetooth": {
|
"bluetooth": {
|
||||||
"format": "",
|
"format": "",
|
||||||
"format-connected": " {num_connections}",
|
"format-connected": " {num_connections}",
|
||||||
"format-connected-battery": " {num_connections}",
|
"format-connected-battery": " {num_connections}",
|
||||||
"tooltip-format-connected": "{device_enumerate}",
|
"tooltip-format-connected": "{device_enumerate}"
|
||||||
"on-click-right": "~/.config/eww/scripts/popup.sh bluetooth-popup"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"custom/vpn": {
|
|
||||||
"format": "{}",
|
|
||||||
"return-type": "json",
|
|
||||||
"interval": 10,
|
|
||||||
"exec": "~/.config/waybar/scripts/vpn-status.sh",
|
|
||||||
"on-click-right": "~/.config/eww/scripts/popup.sh vpn-popup"
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
"cpu": {
|
"cpu": {
|
||||||
"format": " {usage}%",
|
"format": " {usage}%",
|
||||||
"tooltip": false,
|
"tooltip": false
|
||||||
"on-click-right": "~/.config/eww/scripts/popup.sh system-popup"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"memory": {
|
"memory": {
|
||||||
"interval": 10,
|
"interval": 10,
|
||||||
"format": " {percentage}%",
|
"format": " {percentage}%",
|
||||||
"tooltip-format": "total: {total:0.2f}GiB\nused: {used:0.2f}GiB\navailable: {avail:0.2f}GiB\nswap: {swapUsed:0.2f}/{swapTotal:0.2f}GiB",
|
"tooltip-format": "total: {total:0.2f}GiB\nused: {used:0.2f}GiB\navailable: {avail:0.2f}GiB\nswap: {swapUsed:0.2f}/{swapTotal:0.2f}GiB"
|
||||||
"on-click-right": "~/.config/eww/scripts/popup.sh system-popup"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// right: battery
|
|
||||||
"battery": {
|
"battery": {
|
||||||
"states": {
|
"states": {
|
||||||
"warning": 30,
|
"warning": 30,
|
||||||
@@ -136,16 +105,13 @@
|
|||||||
"format-full": " full",
|
"format-full": " full",
|
||||||
"format-alt": "{time}",
|
"format-alt": "{time}",
|
||||||
"tooltip-format": "{timeTo}\n{power}W",
|
"tooltip-format": "{timeTo}\n{power}W",
|
||||||
"on-click": "p=$(powerprofilesctl get); case $p in power-saver) n=balanced;; balanced) n=performance;; *) n=power-saver;; esac; powerprofilesctl set $n",
|
"on-click": "p=$(powerprofilesctl get); case $p in power-saver) n=balanced;; balanced) n=performance;; *) n=power-saver;; esac; powerprofilesctl set $n"
|
||||||
"on-click-right": "~/.config/eww/scripts/popup.sh battery-popup"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// right: audio
|
|
||||||
"wireplumber": {
|
"wireplumber": {
|
||||||
"format": " {volume}%",
|
"format": " {volume}%",
|
||||||
"format-muted": " muted",
|
"format-muted": " muted",
|
||||||
"on-click": "pamixer -t",
|
"on-click": "pamixer -t",
|
||||||
"on-click-right": "~/.config/eww/scripts/popup.sh volume-popup",
|
|
||||||
"on-scroll-up": "pamixer -i 5",
|
"on-scroll-up": "pamixer -i 5",
|
||||||
"on-scroll-down": "pamixer -d 5",
|
"on-scroll-down": "pamixer -d 5",
|
||||||
"tooltip-format": "{node_name}: {volume}%"
|
"tooltip-format": "{node_name}: {volume}%"
|
||||||
@@ -157,18 +123,15 @@
|
|||||||
"interval": 2,
|
"interval": 2,
|
||||||
"exec": "~/.config/waybar/scripts/mic-status.sh",
|
"exec": "~/.config/waybar/scripts/mic-status.sh",
|
||||||
"on-click": "pamixer --default-source -t",
|
"on-click": "pamixer --default-source -t",
|
||||||
"on-click-right": "~/.config/eww/scripts/popup.sh volume-popup",
|
|
||||||
"on-scroll-up": "pamixer --default-source -i 5",
|
"on-scroll-up": "pamixer --default-source -i 5",
|
||||||
"on-scroll-down": "pamixer --default-source -d 5"
|
"on-scroll-down": "pamixer --default-source -d 5"
|
||||||
},
|
},
|
||||||
|
|
||||||
"backlight": {
|
"backlight": {
|
||||||
"format": " {percent}%",
|
"format": " {percent}%",
|
||||||
"tooltip": false,
|
"tooltip": false
|
||||||
"on-click-right": "~/.config/eww/scripts/popup.sh volume-popup"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// right: clock
|
|
||||||
"clock": {
|
"clock": {
|
||||||
"interval": 1,
|
"interval": 1,
|
||||||
"format": "{:%d/%m %H:%M:%S}",
|
"format": "{:%d/%m %H:%M:%S}",
|
||||||
|
|||||||
73
waybar/scripts/network-status.sh
Executable file
73
waybar/scripts/network-status.sh
Executable file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# combined network + vpn status for waybar
|
||||||
|
# format: icon ip/cidr [vpn-name ...]
|
||||||
|
|
||||||
|
iface=$(ip route show default 2>/dev/null | awk '/default/ {print $5; exit}')
|
||||||
|
icon=""
|
||||||
|
class="disconnected"
|
||||||
|
net_text=""
|
||||||
|
vpn_names=()
|
||||||
|
tooltip_parts=()
|
||||||
|
|
||||||
|
# network
|
||||||
|
if [[ -n "$iface" ]]; then
|
||||||
|
cidr=$(ip -4 addr show "$iface" 2>/dev/null | awk '/inet / {print $2; exit}')
|
||||||
|
gw=$(ip route show default 2>/dev/null | awk '/default/ {print $3; exit}')
|
||||||
|
|
||||||
|
if [[ "$iface" == wl* ]]; then
|
||||||
|
icon=""
|
||||||
|
class="wifi"
|
||||||
|
ssid=$(nmcli -t -f active,ssid dev wifi 2>/dev/null | awk -F: '/^yes:/ {print $2}')
|
||||||
|
signal=$(nmcli -t -f active,signal dev wifi 2>/dev/null | awk -F: '/^yes:/ {print $2}')
|
||||||
|
tooltip_parts+=("$iface: $cidr via $gw")
|
||||||
|
[[ -n "$ssid" ]] && tooltip_parts+=("ssid: $ssid (${signal}%)")
|
||||||
|
else
|
||||||
|
icon=""
|
||||||
|
class="wired"
|
||||||
|
tooltip_parts+=("$iface: $cidr via $gw")
|
||||||
|
fi
|
||||||
|
net_text="$cidr"
|
||||||
|
else
|
||||||
|
tooltip_parts+=("no connection")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# wireguard — show config name (interface name, e.g. wg-office)
|
||||||
|
if command -v wg &>/dev/null; then
|
||||||
|
for wg_iface in $(wg show interfaces 2>/dev/null); do
|
||||||
|
wg_cidr=$(ip -4 addr show "$wg_iface" 2>/dev/null | awk '/inet / {print $2; exit}')
|
||||||
|
vpn_names+=("$wg_iface")
|
||||||
|
tooltip_parts+=("wg: $wg_iface $wg_cidr")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# tailscale — show exit node name if routed, otherwise tailnet name
|
||||||
|
if command -v tailscale &>/dev/null; then
|
||||||
|
ts_json=$(tailscale status --json 2>/dev/null)
|
||||||
|
ts_state=$(jq -r '.BackendState // empty' <<<"$ts_json")
|
||||||
|
if [[ "$ts_state" == "Running" ]]; then
|
||||||
|
ts_ip=$(jq -r '.TailscaleIPs[0] // empty' <<<"$ts_json")
|
||||||
|
ts_self=$(jq -r '.Self.HostName // empty' <<<"$ts_json")
|
||||||
|
ts_exit_id=$(jq -r '.ExitNodeStatus.ID // empty' <<<"$ts_json")
|
||||||
|
|
||||||
|
if [[ -n "$ts_exit_id" ]]; then
|
||||||
|
exit_name=$(jq -r --arg id "$ts_exit_id" '[.Peer[] | select(.ID == $id) | .HostName][0] // "exit"' <<<"$ts_json")
|
||||||
|
vpn_names+=("$exit_name")
|
||||||
|
tooltip_parts+=("ts: $ts_self $ts_ip → $exit_name")
|
||||||
|
else
|
||||||
|
tooltip_parts+=("ts: $ts_self $ts_ip")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# build text
|
||||||
|
text="$icon"
|
||||||
|
[[ -n "$net_text" ]] && text+=" $net_text"
|
||||||
|
if [[ ${#vpn_names[@]} -gt 0 ]]; then
|
||||||
|
text+=" [$(IFS=,; echo "${vpn_names[*]}")]"
|
||||||
|
fi
|
||||||
|
|
||||||
|
tooltip=$(printf '%s\n' "${tooltip_parts[@]}")
|
||||||
|
|
||||||
|
jq -nc --arg text "$text" --arg class "$class" --arg tooltip "$tooltip" \
|
||||||
|
'{$text,$class,$tooltip}'
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# wrap swaync subscription, hide when no notifications
|
|
||||||
|
|
||||||
swaync-client -swb 2>/dev/null | while read -r line; do
|
|
||||||
count=$(echo "$line" | jq -r '.text // "0"')
|
|
||||||
class=$(echo "$line" | jq -r '.class // "none"')
|
|
||||||
|
|
||||||
if [[ "$count" == "0" ]]; then
|
|
||||||
echo '{"text": "", "class": "none"}'
|
|
||||||
else
|
|
||||||
jq -nc --arg text " $count" --arg class "$class" '{$text,$class}'
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
@@ -8,13 +8,13 @@ if [[ "$1" == "disconnect" ]]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
count=$(pgrep -cf 'sshd-session:.*@' 2>/dev/null || echo 0)
|
count=$(pgrep -cf '[s]shd-session:.*@' 2>/dev/null)
|
||||||
|
count=${count:-0}
|
||||||
|
|
||||||
if [[ "$count" -gt 0 ]]; then
|
if [[ "$count" -gt 0 ]]; then
|
||||||
# get remote session details
|
|
||||||
sessions=$(who 2>/dev/null | awk '$NF ~ /\([0-9]/ {gsub(/[()]/, "", $NF); print $1 "@" $NF}')
|
sessions=$(who 2>/dev/null | awk '$NF ~ /\([0-9]/ {gsub(/[()]/, "", $NF); print $1 "@" $NF}')
|
||||||
tooltip=${sessions:-"$count remote sessions"}
|
label="session"; [[ "$count" -gt 1 ]] && label="sessions"
|
||||||
# replace newlines with \n for valid JSON
|
tooltip=${sessions:-"$count remote $label"}
|
||||||
tooltip=${tooltip//$'\n'/\\n}
|
tooltip=${tooltip//$'\n'/\\n}
|
||||||
tooltip=${tooltip//\"/\\\"}
|
tooltip=${tooltip//\"/\\\"}
|
||||||
printf '{"text": "●", "class": "active", "tooltip": "%s"}\n' "$tooltip"
|
printf '{"text": "●", "class": "active", "tooltip": "%s"}\n' "$tooltip"
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# check wireguard and tailscale status
|
|
||||||
# outputs waybar JSON; empty when inactive
|
|
||||||
|
|
||||||
parts=()
|
|
||||||
tooltip_parts=()
|
|
||||||
|
|
||||||
if command -v wg &>/dev/null; then
|
|
||||||
ifaces=$(wg show interfaces 2>/dev/null)
|
|
||||||
if [[ -n "$ifaces" ]]; then
|
|
||||||
parts+=("")
|
|
||||||
tooltip_parts+=("wireguard: $ifaces")
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if command -v tailscale &>/dev/null; then
|
|
||||||
ts_json=$(tailscale status --json 2>/dev/null)
|
|
||||||
state=$(jq -r '.BackendState // empty' <<<"$ts_json")
|
|
||||||
if [[ "$state" == "Running" ]]; then
|
|
||||||
ts_ip=$(jq -r '.TailscaleIPs[0] // empty' <<<"$ts_json")
|
|
||||||
ts_name=$(jq -r '.Self.HostName // empty' <<<"$ts_json")
|
|
||||||
ts_exit=$(jq -r '.ExitNodeStatus.ID // empty' <<<"$ts_json")
|
|
||||||
parts+=("")
|
|
||||||
tip="tailscale: ${ts_name} ${ts_ip}"
|
|
||||||
if [[ -n "$ts_exit" ]]; then
|
|
||||||
tip="$tip (exit node)"
|
|
||||||
fi
|
|
||||||
tooltip_parts+=("$tip")
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ${#parts[@]} -gt 0 ]]; then
|
|
||||||
text="${parts[*]}"
|
|
||||||
tip=$(printf '%s\\n' "${tooltip_parts[@]}")
|
|
||||||
# strip trailing \n
|
|
||||||
tip=${tip%\\n}
|
|
||||||
jq -nc --arg text "$text" --arg tooltip "$tip" --arg class "active" \
|
|
||||||
'{$text,$class,$tooltip}'
|
|
||||||
fi
|
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
window#waybar {
|
window#waybar {
|
||||||
background-color: @bg_dim;
|
background-color: @bg_dim;
|
||||||
color: @fg;
|
color: @fg;
|
||||||
|
min-height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
window#waybar.hidden {
|
window#waybar.hidden {
|
||||||
@@ -82,13 +83,12 @@ button:hover {
|
|||||||
#cpu,
|
#cpu,
|
||||||
#memory,
|
#memory,
|
||||||
#backlight,
|
#backlight,
|
||||||
#network,
|
|
||||||
#wireplumber,
|
#wireplumber,
|
||||||
#mpris,
|
#mpris,
|
||||||
#bluetooth,
|
#bluetooth,
|
||||||
#language,
|
#language,
|
||||||
#custom-mic,
|
#custom-mic,
|
||||||
#custom-vpn,
|
#custom-network,
|
||||||
#custom-ssh,
|
#custom-ssh,
|
||||||
#custom-notification,
|
#custom-notification,
|
||||||
#systemd-failed-units {
|
#systemd-failed-units {
|
||||||
@@ -102,7 +102,10 @@ button:hover {
|
|||||||
/* center: alert zone */
|
/* center: alert zone */
|
||||||
#custom-ssh.active {
|
#custom-ssh.active {
|
||||||
color: @cyan;
|
color: @cyan;
|
||||||
font-size: 20px;
|
font-size: 18px;
|
||||||
|
padding: 0 10px;
|
||||||
|
margin-top: -2px;
|
||||||
|
margin-bottom: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#custom-notification.notification {
|
#custom-notification.notification {
|
||||||
@@ -144,7 +147,7 @@ button:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* right: connectivity */
|
/* right: connectivity */
|
||||||
#network.disconnected {
|
#custom-network.disconnected {
|
||||||
color: @red;
|
color: @red;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,10 +155,6 @@ button:hover {
|
|||||||
color: @blue;
|
color: @blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
#custom-vpn {
|
|
||||||
color: @cyan;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* right: system */
|
/* right: system */
|
||||||
#cpu {
|
#cpu {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
|||||||
@@ -23,5 +23,14 @@ export NAME="Matej Janežič"
|
|||||||
# starship nest config into a folder, default is ~/.config/starship.toml
|
# starship nest config into a folder, default is ~/.config/starship.toml
|
||||||
export STARSHIP_CONFIG="$XDG_CONFIG_HOME/starship/config.toml"
|
export STARSHIP_CONFIG="$XDG_CONFIG_HOME/starship/config.toml"
|
||||||
|
|
||||||
|
# claude
|
||||||
|
export CLAUDE_CONFIG_DIR="$XDG_CONFIG_HOME/claude"
|
||||||
|
|
||||||
|
# cliphist
|
||||||
|
export CLIPHIST_DB_PATH="/tmp/cliphist-db"
|
||||||
|
|
||||||
# fzf
|
# fzf
|
||||||
export FZF_DEFAULT_OPTS="--cycle --bind 'tab:toggle-up,btab:toggle-down' --pointer '>' --color '16' --border 'double' --ansi --highlight-line --header 'Navigate with ARROW KEYS or TAB/S-TAB. Select with ENTER.'"
|
export FZF_DEFAULT_OPTS="--cycle --bind 'tab:toggle-up,btab:toggle-down' --pointer '>' --color '16' --border 'double' --ansi --highlight-line --header 'Navigate with ARROW KEYS or TAB/S-TAB. Select with ENTER.'"
|
||||||
|
|
||||||
|
# ephvm
|
||||||
|
export EPHVM_FLAKE="/home/matej/git/git.janezic.dev/janezicmatej/matej.nix"
|
||||||
|
|||||||
48
zsh/.zshrc
48
zsh/.zshrc
@@ -8,6 +8,7 @@ setopt EXTENDED_GLOB # enable extended glob operators (#, ~, ^)
|
|||||||
|
|
||||||
# history
|
# history
|
||||||
HISTFILE="$XDG_STATE_HOME/zsh/history"
|
HISTFILE="$XDG_STATE_HOME/zsh/history"
|
||||||
|
mkdir -p "${HISTFILE:h}"
|
||||||
HISTSIZE=10000
|
HISTSIZE=10000
|
||||||
SAVEHIST=10000
|
SAVEHIST=10000
|
||||||
setopt HIST_IGNORE_DUPS # skip consecutive duplicates
|
setopt HIST_IGNORE_DUPS # skip consecutive duplicates
|
||||||
@@ -15,35 +16,36 @@ setopt HIST_IGNORE_SPACE # skip commands starting with space
|
|||||||
setopt SHARE_HISTORY # share history across sessions
|
setopt SHARE_HISTORY # share history across sessions
|
||||||
setopt HIST_REDUCE_BLANKS # strip extra whitespace
|
setopt HIST_REDUCE_BLANKS # strip extra whitespace
|
||||||
|
|
||||||
# plugins
|
# -- plugins --
|
||||||
local _plugins="$XDG_DATA_HOME/zsh/plugins"
|
# auto-cloned on first launch, auto-updated weekly in background
|
||||||
local _stamp="$XDG_CACHE_HOME/zsh/plugins-updated"
|
PLUGIN_DIR="$XDG_DATA_HOME/zsh/plugins"
|
||||||
local _repos=(
|
_plugin_stamp="$XDG_CACHE_HOME/zsh/plugins-updated"
|
||||||
"https://github.com/zsh-users/zsh-completions"
|
_plugin_needs_update=0
|
||||||
"https://github.com/zsh-users/zsh-autosuggestions"
|
|
||||||
"https://github.com/romkatv/zsh-no-ps2"
|
|
||||||
)
|
|
||||||
local _needs_update=0
|
|
||||||
|
|
||||||
if [[ ! -f "$_stamp" ]] || [[ -n $_stamp(#qN.mh+168) ]]; then
|
# check if a week (168h) has passed since last update
|
||||||
mkdir -p "${_stamp:h}"
|
if [[ ! -f "$_plugin_stamp" ]] || [[ -n $_plugin_stamp(#qN.mh+168) ]]; then
|
||||||
touch "$_stamp"
|
mkdir -p "${_plugin_stamp:h}"
|
||||||
_needs_update=1
|
touch "$_plugin_stamp"
|
||||||
|
_plugin_needs_update=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for _repo in $_repos; do
|
function ensure_plugin() {
|
||||||
if [[ ! -d "$_plugins/${_repo:t}" ]]; then
|
local repo=$1 dir="$PLUGIN_DIR/${1##*/}"
|
||||||
git clone "$_repo" "$_plugins/${_repo:t}"
|
# clone if missing, pull if update is due
|
||||||
elif (( _needs_update )); then
|
if [[ ! -d "$dir" ]]; then
|
||||||
git -C "$_plugins/${_repo:t}" pull --quiet &
|
git clone --depth=1 "https://github.com/$repo.git" "$dir"
|
||||||
|
elif (( _plugin_needs_update )); then
|
||||||
|
git -C "$dir" pull --quiet &
|
||||||
fi
|
fi
|
||||||
done
|
fpath=("$dir" $fpath)
|
||||||
(( _needs_update )) && wait
|
source "$dir/${dir##*/}.plugin.zsh" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
fpath=("$_plugins/zsh-completions/src" $fpath)
|
ensure_plugin zsh-users/zsh-completions
|
||||||
|
ensure_plugin zsh-users/zsh-autosuggestions
|
||||||
|
ensure_plugin romkatv/zsh-no-ps2
|
||||||
|
(( _plugin_needs_update )) && wait
|
||||||
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#ffffff,bg=cyan,bold,underline"
|
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#ffffff,bg=cyan,bold,underline"
|
||||||
source "$_plugins/zsh-autosuggestions/zsh-autosuggestions.zsh"
|
|
||||||
source "$_plugins/zsh-no-ps2/zsh-no-ps2.plugin.zsh"
|
|
||||||
|
|
||||||
# line editing widgets
|
# line editing widgets
|
||||||
autoload -Uz up-line-or-beginning-search down-line-or-beginning-search bracketed-paste-magic
|
autoload -Uz up-line-or-beginning-search down-line-or-beginning-search bracketed-paste-magic
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
alias z="exec zsh"
|
alias z="exec zsh"
|
||||||
alias t=tmux_attach
|
alias t=tmux_attach
|
||||||
|
alias e="bash /home/matej/git/git.janezic.dev/janezicmatej/matej.nix/scripts/ephvm-run.sh"
|
||||||
|
|
||||||
alias n=nvim
|
alias n=nvim
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ alias gcm='git checkout $(git_main_branch)'
|
|||||||
alias gsw="git switch"
|
alias gsw="git switch"
|
||||||
|
|
||||||
# git fetch/pull/push
|
# git fetch/pull/push
|
||||||
alias gfa="git fetch --all --prune"
|
alias gfa="git fetch --all --prune --tags"
|
||||||
alias gl="git pull"
|
alias gl="git pull"
|
||||||
alias gp="git push"
|
alias gp="git push"
|
||||||
alias gpf="git push --force-with-lease"
|
alias gpf="git push --force-with-lease"
|
||||||
|
|||||||
Reference in New Issue
Block a user