aboutsummaryrefslogtreecommitdiff
path: root/.config
diff options
context:
space:
mode:
authordelta <darkussdelta@gmail.com>2025-07-04 00:38:29 +0200
committerdelta <darkussdelta@gmail.com>2025-07-04 00:38:29 +0200
commitb3530d7c4a102935fa26498a160ee1dc6c1e9c03 (patch)
treed7751206a694bc5de2d6b34b0c077cfcd1855798 /.config
parentdf75ec5ed5e3848c497f0439acb43ec9246ad3e7 (diff)
:3
Diffstat (limited to '.config')
-rw-r--r--.config/awesome/.nvim.lua2
-rw-r--r--.config/awesome/.styluaignore2
-rw-r--r--.config/awesome/assets/custom/init.lua6
-rw-r--r--.config/awesome/assets/custom/vinyl-record-fill.svg17
-rw-r--r--.config/awesome/assets/phosphor/browser-fill.svg2
-rw-r--r--.config/awesome/assets/phosphor/calendar-dots-fill.svg1
-rw-r--r--.config/awesome/assets/phosphor/caret-down-bold.svg1
-rw-r--r--.config/awesome/assets/phosphor/caret-up-bold.svg1
-rw-r--r--.config/awesome/assets/phosphor/init.lua12
-rw-r--r--.config/awesome/assets/phosphor/speaker-hifi-fill.svg1
-rw-r--r--.config/awesome/assets/phosphor/timer-fill.svg1
-rw-r--r--.config/awesome/assets/phosphor/waveform-fill.svg1
-rw-r--r--.config/awesome/assets/simpleicons/firefox.svg1
-rw-r--r--.config/awesome/assets/simpleicons/init.lua6
-rw-r--r--.config/awesome/assets/simpleicons/librewolf.svg1
-rw-r--r--.config/awesome/assets/simpleicons/spotify.svg1
-rw-r--r--.config/awesome/curious.lua36
m---------.config/awesome/lib/lit0
-rw-r--r--.config/awesome/misc/autostart.lua11
-rw-r--r--.config/awesome/misc/cfg.lua13
-rw-r--r--.config/awesome/misc/keys.lua157
-rw-r--r--.config/awesome/misc/rules.lua39
-rw-r--r--.config/awesome/prismite.lua45
-rw-r--r--.config/awesome/quarrel/animation/bezier.lua89
-rw-r--r--.config/awesome/quarrel/animation/init.lua249
-rw-r--r--.config/awesome/quarrel/animation/subscribable/init.lua47
-rw-r--r--.config/awesome/quarrel/animation/tween/init.lua554
-rw-r--r--.config/awesome/quarrel/bezier.lua343
-rw-r--r--.config/awesome/quarrel/bind/consts.lua27
-rw-r--r--.config/awesome/quarrel/bind/init.lua (renamed from .config/awesome/quarrel/bind.lua)31
-rw-r--r--.config/awesome/quarrel/color.lua64
-rw-r--r--.config/awesome/quarrel/const.lua22
-rw-r--r--.config/awesome/quarrel/debugger.lua865
-rw-r--r--.config/awesome/quarrel/delegate.lua10
-rw-r--r--.config/awesome/quarrel/fs.lua21
-rw-r--r--.config/awesome/quarrel/iconset.lua21
-rw-r--r--.config/awesome/quarrel/init.lua10
-rw-r--r--.config/awesome/quarrel/lua-rust.tar.gzbin18878 -> 0 bytes
-rw-r--r--.config/awesome/quarrel/markup.lua10
-rw-r--r--.config/awesome/quarrel/math/consts.lua5
-rw-r--r--.config/awesome/quarrel/math/init.lua (renamed from .config/awesome/quarrel/math.lua)14
-rw-r--r--.config/awesome/quarrel/native/Cargo.toml19
-rw-r--r--.config/awesome/quarrel/native/init.lua4
-rw-r--r--.config/awesome/quarrel/native/src/http.rs22
-rw-r--r--.config/awesome/quarrel/native/src/lenses/application.rs85
-rw-r--r--.config/awesome/quarrel/native/src/lenses/calculator.rs47
-rw-r--r--.config/awesome/quarrel/native/src/lenses/entry.rs19
-rw-r--r--.config/awesome/quarrel/native/src/lenses/mod.rs78
-rw-r--r--.config/awesome/quarrel/native/src/lib.rs14
-rw-r--r--.config/awesome/quarrel/native/src/moondrop.rs3
-rw-r--r--.config/awesome/quarrel/native/src/mpd.rs142
-rw-r--r--.config/awesome/quarrel/native/src/net/mod.rs4
-rw-r--r--.config/awesome/quarrel/native/src/util.rs3
-rw-r--r--.config/awesome/quarrel/persistent.lua62
-rw-r--r--.config/awesome/quarrel/store.lua4
-rw-r--r--.config/awesome/quarrel/ui.lua145
-rw-r--r--.config/awesome/quarrel/ui/consts.lua24
-rw-r--r--.config/awesome/quarrel/ui/init.lua167
-rw-r--r--.config/awesome/quarrel/vars.lua254
-rw-r--r--.config/awesome/rc.lua1
-rw-r--r--.config/awesome/services/backlight.lua6
-rw-r--r--.config/awesome/services/battery.lua8
-rw-r--r--.config/awesome/services/common.lua60
-rw-r--r--.config/awesome/services/mpris/init.lua100
-rw-r--r--.config/awesome/services/playerctl.lua612
-rw-r--r--.config/awesome/signals/client.lua10
-rw-r--r--.config/awesome/ui/conductor/init.lua60
-rw-r--r--.config/awesome/ui/decorations/wallpaper.lua16
-rw-r--r--.config/awesome/ui/fresnel/init.lua104
-rw-r--r--.config/awesome/ui/init.lua4
-rw-r--r--.config/awesome/ui/insightful/init.lua24
-rw-r--r--.config/awesome/ui/osd.lua102
-rw-r--r--.config/awesome/ui/osd/init.lua102
-rw-r--r--.config/awesome/ui/powermenu/init.lua29
-rw-r--r--.config/awesome/ui/statusbar/consts.lua8
-rw-r--r--.config/awesome/ui/statusbar/init.lua68
-rw-r--r--.config/awesome/ui/statusbar/panel/init.lua76
-rw-r--r--.config/awesome/ui/statusbar/panel/widgets/battery.lua1
-rw-r--r--.config/awesome/ui/statusbar/panel/widgets/calendar.lua172
-rw-r--r--.config/awesome/ui/statusbar/panel/widgets/displays.lua78
-rw-r--r--.config/awesome/ui/statusbar/panel/widgets/imagebox.lua720
-rw-r--r--.config/awesome/ui/statusbar/panel/widgets/mpris.lua429
-rw-r--r--.config/awesome/ui/statusbar/panel/widgets/music.old.lua (renamed from .config/awesome/ui/statusbar/panel/widgets/music.lua)35
-rw-r--r--.config/awesome/ui/statusbar/panel/widgets/power_menu.lua16
-rw-r--r--.config/awesome/ui/statusbar/panel/widgets/weather.lua0
-rw-r--r--.config/awesome/ui/statusbar/panel/widgets/wifi.lua27
-rw-r--r--.config/awesome/ui/statusbar/widgets/displays.lua24
-rw-r--r--.config/awesome/ui/statusbar/widgets/taglist.lua32
-rw-r--r--.config/awesome/ui/statusbar/widgets/taglist_new.lua147
-rw-r--r--.config/awesome/ui/statusbar/widgets/tasklist.lua30
-rw-r--r--.config/awesome/ui/wicked/consts.lua5
-rw-r--r--.config/awesome/ui/wicked/init.lua80
-rw-r--r--.config/fish/bindings.fish2
-rw-r--r--.config/fish/colors.fish10
-rw-r--r--.config/fish/completions/beet.fish523
-rw-r--r--.config/fish/config.fish79
-rw-r--r--.config/fish/fish_variables14
-rw-r--r--.config/fish/functions/fish_commandline_toggle.fish26
-rw-r--r--.config/fish/functions/fish_ssh_agent.fish32
-rw-r--r--.config/fish/path.fish20
-rw-r--r--.config/nvim/.luarc.json0
-rw-r--r--.config/nvim/.nvim.lua2
-rw-r--r--.config/nvim/init.lua11
-rw-r--r--.config/nvim/lazy-lock.json50
-rw-r--r--.config/nvim/lua/autocmd.lua34
-rw-r--r--.config/nvim/lua/binds.lua54
-rw-r--r--.config/nvim/lua/cmds.lua20
-rw-r--r--.config/nvim/lua/dash.lua147
-rw-r--r--.config/nvim/lua/icons.lua45
-rw-r--r--.config/nvim/lua/local_plugins/color_converter/lua/color_converter.lua14
-rw-r--r--.config/nvim/lua/local_plugins/color_converter/lua/ui.lua47
-rw-r--r--.config/nvim/lua/options.lua35
-rw-r--r--.config/nvim/lua/plugins.lua83
-rw-r--r--.config/nvim/lua/plugins/coding.lua150
-rw-r--r--.config/nvim/lua/plugins/colorscheme.lua57
-rw-r--r--.config/nvim/lua/plugins/editor.lua24
-rw-r--r--.config/nvim/lua/plugins/lsp.lua254
-rw-r--r--.config/nvim/lua/plugins/ui.lua477
-rw-r--r--.config/nvim/lua/plugins/utils.lua4
-rw-r--r--.config/rmpc/config.ron167
-rw-r--r--.config/rmpc/themes/prismite.ron195
-rw-r--r--.config/wezterm/.nvim.lua2
-rw-r--r--.config/wezterm/appearance.lua89
-rw-r--r--.config/wezterm/colors.lua78
-rw-r--r--.config/wezterm/inspect.lua512
-rw-r--r--.config/wezterm/keys.lua68
-rw-r--r--.config/wezterm/stylua.toml5
-rw-r--r--.config/wezterm/wezterm.lua155
128 files changed, 7684 insertions, 2822 deletions
diff --git a/.config/awesome/.nvim.lua b/.config/awesome/.nvim.lua
new file mode 100644
index 0000000..9c7049d
--- /dev/null
+++ b/.config/awesome/.nvim.lua
@@ -0,0 +1,2 @@
+vim.env.GIT_DIR = vim.fn.expand("~/.dots")
+vim.env.GIT_WORK_TREE = vim.fn.expand("~")
diff --git a/.config/awesome/.styluaignore b/.config/awesome/.styluaignore
new file mode 100644
index 0000000..cbf9e27
--- /dev/null
+++ b/.config/awesome/.styluaignore
@@ -0,0 +1,2 @@
+lib/bling
+lib/rubato
diff --git a/.config/awesome/assets/custom/init.lua b/.config/awesome/assets/custom/init.lua
new file mode 100644
index 0000000..fd1dca5
--- /dev/null
+++ b/.config/awesome/assets/custom/init.lua
@@ -0,0 +1,6 @@
+local gfs = require "gears.filesystem"
+local qiconset = require "quarrel.iconset"
+
+local simpleicons_dir = gfs.get_configuration_dir() .. "assets/custom/"
+
+return qiconset(simpleicons_dir)
diff --git a/.config/awesome/assets/custom/vinyl-record-fill.svg b/.config/awesome/assets/custom/vinyl-record-fill.svg
new file mode 100644
index 0000000..0d68cbf
--- /dev/null
+++ b/.config/awesome/assets/custom/vinyl-record-fill.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="28"
+ height="28"
+ fill="#000000"
+ viewBox="0 0 256 256"
+ version="1.1"
+ id="svg1"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs1" />
+ <path
+ d="M 128,0 A 128,128 0 1 0 256,128 128.13538,128.13538 0 0 0 128,0 Z M 59.076923,128 A 9.846154,9.8461541 0 0 1 39.384615,128 88.713846,88.713846 0 0 1 128,39.384615 9.846154,9.846154 0 0 1 128,59.076923 68.996923,68.996923 0 0 0 59.076923,128 Z m 39.384615,0 A 29.538462,29.538462 0 1 1 128,157.53846 29.538462,29.538462 0 0 1 98.461538,128 Z M 128,216.61538 a 9.8461538,9.8461538 0 0 1 0,-19.6923 A 68.996923,68.996923 0 0 0 196.92308,128 9.8461538,9.8461538 0 0 1 216.61538,128 88.713846,88.713846 0 0 1 128,216.61538 Z"
+ id="path1"
+ style="stroke-width:1.23077" />
+</svg>
diff --git a/.config/awesome/assets/phosphor/browser-fill.svg b/.config/awesome/assets/phosphor/browser-fill.svg
index 68d95e5..1f64b69 100644
--- a/.config/awesome/assets/phosphor/browser-fill.svg
+++ b/.config/awesome/assets/phosphor/browser-fill.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="#000000" viewBox="0 0 256 256"><path d="M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm0,16V88H40V56Z"></path></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm0,16V88H40V56Z"></path></svg> \ No newline at end of file
diff --git a/.config/awesome/assets/phosphor/calendar-dots-fill.svg b/.config/awesome/assets/phosphor/calendar-dots-fill.svg
new file mode 100644
index 0000000..1c25fff
--- /dev/null
+++ b/.config/awesome/assets/phosphor/calendar-dots-fill.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM84,184a12,12,0,1,1,12-12A12,12,0,0,1,84,184Zm44,0a12,12,0,1,1,12-12A12,12,0,0,1,128,184Zm0-40a12,12,0,1,1,12-12A12,12,0,0,1,128,144Zm44,40a12,12,0,1,1,12-12A12,12,0,0,1,172,184Zm0-40a12,12,0,1,1,12-12A12,12,0,0,1,172,144Zm36-64H48V48H72v8a8,8,0,0,0,16,0V48h80v8a8,8,0,0,0,16,0V48h24Z"></path></svg> \ No newline at end of file
diff --git a/.config/awesome/assets/phosphor/caret-down-bold.svg b/.config/awesome/assets/phosphor/caret-down-bold.svg
new file mode 100644
index 0000000..cc995f8
--- /dev/null
+++ b/.config/awesome/assets/phosphor/caret-down-bold.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M216.49,104.49l-80,80a12,12,0,0,1-17,0l-80-80a12,12,0,0,1,17-17L128,159l71.51-71.52a12,12,0,0,1,17,17Z"></path></svg> \ No newline at end of file
diff --git a/.config/awesome/assets/phosphor/caret-up-bold.svg b/.config/awesome/assets/phosphor/caret-up-bold.svg
new file mode 100644
index 0000000..eae0ae7
--- /dev/null
+++ b/.config/awesome/assets/phosphor/caret-up-bold.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M216.49,168.49a12,12,0,0,1-17,0L128,97,56.49,168.49a12,12,0,0,1-17-17l80-80a12,12,0,0,1,17,0l80,80A12,12,0,0,1,216.49,168.49Z"></path></svg> \ No newline at end of file
diff --git a/.config/awesome/assets/phosphor/init.lua b/.config/awesome/assets/phosphor/init.lua
index 421ceb2..2f0f6fc 100644
--- a/.config/awesome/assets/phosphor/init.lua
+++ b/.config/awesome/assets/phosphor/init.lua
@@ -1,14 +1,6 @@
local gfs = require "gears.filesystem"
-local qfs = require "quarrel.fs"
+local qiconset = require "quarrel.iconset"
----@type table<string, string>
-local icons = {}
local phosphor_dir = gfs.get_configuration_dir() .. "assets/phosphor/"
-for _, icon in ipairs(qfs.ls_files(phosphor_dir)) do
- if icon:match ".+%.(.+)" == "svg" then
- icons[icon:match("(.+)%..+"):gsub("-", "_")] = phosphor_dir .. icon
- end
-end
-
-return icons
+return qiconset(phosphor_dir)
diff --git a/.config/awesome/assets/phosphor/speaker-hifi-fill.svg b/.config/awesome/assets/phosphor/speaker-hifi-fill.svg
new file mode 100644
index 0000000..dce7932
--- /dev/null
+++ b/.config/awesome/assets/phosphor/speaker-hifi-fill.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M152,160a24,24,0,1,1-24-24A24,24,0,0,1,152,160ZM208,40V216a16,16,0,0,1-16,16H64a16,16,0,0,1-16-16V40A16,16,0,0,1,64,24H192A16,16,0,0,1,208,40ZM116,68a12,12,0,1,0,12-12A12,12,0,0,0,116,68Zm52,92a40,40,0,1,0-40,40A40,40,0,0,0,168,160Z"></path></svg> \ No newline at end of file
diff --git a/.config/awesome/assets/phosphor/timer-fill.svg b/.config/awesome/assets/phosphor/timer-fill.svg
new file mode 100644
index 0000000..530646d
--- /dev/null
+++ b/.config/awesome/assets/phosphor/timer-fill.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M128,40a96,96,0,1,0,96,96A96.11,96.11,0,0,0,128,40Zm45.66,61.66-40,40a8,8,0,0,1-11.32-11.32l40-40a8,8,0,0,1,11.32,11.32ZM96,16a8,8,0,0,1,8-8h48a8,8,0,0,1,0,16H104A8,8,0,0,1,96,16Z"></path></svg> \ No newline at end of file
diff --git a/.config/awesome/assets/phosphor/waveform-fill.svg b/.config/awesome/assets/phosphor/waveform-fill.svg
new file mode 100644
index 0000000..dd4d8ab
--- /dev/null
+++ b/.config/awesome/assets/phosphor/waveform-fill.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40ZM72,152a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm32,32a8,8,0,0,1-16,0V72a8,8,0,0,1,16,0Zm32-16a8,8,0,0,1-16,0V88a8,8,0,0,1,16,0Zm32-16a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm32,8a8,8,0,0,1-16,0V96a8,8,0,0,1,16,0Z"></path></svg> \ No newline at end of file
diff --git a/.config/awesome/assets/simpleicons/firefox.svg b/.config/awesome/assets/simpleicons/firefox.svg
new file mode 100644
index 0000000..3cbc89c
--- /dev/null
+++ b/.config/awesome/assets/simpleicons/firefox.svg
@@ -0,0 +1 @@
+<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Firefox Browser</title><path d="M8.824 7.287c.008 0 .004 0 0 0zm-2.8-1.4c.006 0 .003 0 0 0zm16.754 2.161c-.505-1.215-1.53-2.528-2.333-2.943.654 1.283 1.033 2.57 1.177 3.53l.002.02c-1.314-3.278-3.544-4.6-5.366-7.477-.091-.147-.184-.292-.273-.446a3.545 3.545 0 01-.13-.24 2.118 2.118 0 01-.172-.46.03.03 0 00-.027-.03.038.038 0 00-.021 0l-.006.001a.037.037 0 00-.01.005L15.624 0c-2.585 1.515-3.657 4.168-3.932 5.856a6.197 6.197 0 00-2.305.587.297.297 0 00-.147.37c.057.162.24.24.396.17a5.622 5.622 0 012.008-.523l.067-.005a5.847 5.847 0 011.957.222l.095.03a5.816 5.816 0 01.616.228c.08.036.16.073.238.112l.107.055a5.835 5.835 0 01.368.211 5.953 5.953 0 012.034 2.104c-.62-.437-1.733-.868-2.803-.681 4.183 2.09 3.06 9.292-2.737 9.02a5.164 5.164 0 01-1.513-.292 4.42 4.42 0 01-.538-.232c-1.42-.735-2.593-2.121-2.74-3.806 0 0 .537-2 3.845-2 .357 0 1.38-.998 1.398-1.287-.005-.095-2.029-.9-2.817-1.677-.422-.416-.622-.616-.8-.767a3.47 3.47 0 00-.301-.227 5.388 5.388 0 01-.032-2.842c-1.195.544-2.124 1.403-2.8 2.163h-.006c-.46-.584-.428-2.51-.402-2.913-.006-.025-.343.176-.389.206-.406.29-.787.616-1.136.974-.397.403-.76.839-1.085 1.303a9.816 9.816 0 00-1.562 3.52c-.003.013-.11.487-.19 1.073-.013.09-.026.181-.037.272a7.8 7.8 0 00-.069.667l-.002.034-.023.387-.001.06C.386 18.795 5.593 24 12.016 24c5.752 0 10.527-4.176 11.463-9.661.02-.149.035-.298.052-.448.232-1.994-.025-4.09-.753-5.844z"/></svg> \ No newline at end of file
diff --git a/.config/awesome/assets/simpleicons/init.lua b/.config/awesome/assets/simpleicons/init.lua
new file mode 100644
index 0000000..4aaa2d3
--- /dev/null
+++ b/.config/awesome/assets/simpleicons/init.lua
@@ -0,0 +1,6 @@
+local gfs = require "gears.filesystem"
+local qiconset = require "quarrel.iconset"
+
+local simpleicons_dir = gfs.get_configuration_dir() .. "assets/simpleicons/"
+
+return qiconset(simpleicons_dir)
diff --git a/.config/awesome/assets/simpleicons/librewolf.svg b/.config/awesome/assets/simpleicons/librewolf.svg
new file mode 100644
index 0000000..a6dab52
--- /dev/null
+++ b/.config/awesome/assets/simpleicons/librewolf.svg
@@ -0,0 +1 @@
+<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>LibreWolf</title><path d="M12 0C5.3726 0 0 5.3726 0 12s5.3726 12 12 12 12-5.3726 12-12A12 12 0 0 0 12 0m.0306 2.4174c2.539 0 4.7927.9097 6.619 2.6718.8799.849 1.47 1.654 1.9752 2.6953.6834 1.4085.96 2.6337.96 4.2503 0 2.1282-.6144 3.9841-1.891 5.7107-.3792.513-1.1202 1.2925-1.612 1.6955-1.3487 1.1057-3.068 1.8537-4.7739 2.0774-.5894.0772-1.982.0868-2.5232.0174v.0001c-1.681-.2156-3.2887-.8859-4.6919-1.9555-.4576-.3489-1.3163-1.213-1.6911-1.702-1.0497-1.3694-1.7025-2.9237-1.9179-4.5663-.0922-.7032-.0916-1.996.0011-2.6387.4635-3.213 2.4855-5.9688 5.389-7.3449 1.3505-.64 2.5868-.9111 4.1567-.9111m-.0315 1.4514c-1.4755-.0014-2.7693.3367-4.0497 1.0586-1.1657.6572-2.3708 1.8637-3.0308 3.0342-.486.862-.8417 1.8852-.9806 2.82-.091.613-.0929 1.8301-.0036 2.417.1786 1.1746.6241 2.3354 1.27 3.3091.3527.5317.3416.5259.6528.3479.5566-.3184.7018-.4726 1.005-1.0666.3546-.6946.7336-1.3016 1.0762-1.7232.3953-.4867.4496-.5732.648-1.0327.0997-.2312.2788-.5708.3978-.7546l.2165-.3341-.28-.2753c-.372-.3655-.6814-.7802-.7395-.991-.0595-.2152.0108-.3353.2902-.4967.2825-.1632.4845-.21 1.2396-.2877.3677-.0377.7668-.1069.8925-.1547.125-.0475.4988-.251.8305-.452.9577-.5807 1.0388-.6107 1.613-.5983.485.0106.4859.0103.7734-.1635.6026-.3642 1.4681-1.0215 2.607-1.9797.2183-.1838.4286-.328.4674-.3206.161.031.457.757.5019 1.2315.0272.2878-.0285.5725-.1992 1.018-.0466.1215-.068.2375-.0477.2577.0489.0486.2165-.1607.2165-.2703 0-.1397.0924-.1557.2182-.0377.0915.0859.1705.1105.3545.1105.201 0 .2507.0186.329.1232.1332.1775.1644.6616.0625.9682-.099.2983-.3986.6423-.6704.77-.2431.1143-.7354.5951-.9658.9432-.0882.1332-.2536.411-.3675.6172-.114.2063-.305.518-.4246.6925-.2354.3436-.437.823-.6316 1.5017-.1143.3987-.1238.5025-.1285 1.3918-.0057 1.0742-.0613 1.3378-.3518 1.6686-.3301.376-.6596 1.2018-.6873 1.7225-.0175.3286-.0682.649-.1401.8862-.0317.1045.0275.1032.38-.0079 1.027-.3237 1.8638-.7588 2.6718-1.3889.4296-.335 1.1722-1.0847 1.4835-1.4978.8445-1.1202 1.419-2.5235 1.5881-3.8785.0814-.6526.0342-2.0332-.0901-2.6325-.3293-1.5875-1.039-2.9284-2.1566-4.0759-.6968-.7154-1.3311-1.187-2.1473-1.5961-1.2384-.621-2.2994-.8718-3.6932-.8733m1.516 5.314-.6964.0976-.433.5544.6966-.0978z"/></svg> \ No newline at end of file
diff --git a/.config/awesome/assets/simpleicons/spotify.svg b/.config/awesome/assets/simpleicons/spotify.svg
new file mode 100644
index 0000000..8d4d095
--- /dev/null
+++ b/.config/awesome/assets/simpleicons/spotify.svg
@@ -0,0 +1 @@
+<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Spotify</title><path d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.66 0 12 0zm5.521 17.34c-.24.359-.66.48-1.021.24-2.82-1.74-6.36-2.101-10.561-1.141-.418.122-.779-.179-.899-.539-.12-.421.18-.78.54-.9 4.56-1.021 8.52-.6 11.64 1.32.42.18.479.659.301 1.02zm1.44-3.3c-.301.42-.841.6-1.262.3-3.239-1.98-8.159-2.58-11.939-1.38-.479.12-1.02-.12-1.14-.6-.12-.48.12-1.021.6-1.141C9.6 9.9 15 10.561 18.72 12.84c.361.181.54.78.241 1.2zm.12-3.36C15.24 8.4 8.82 8.16 5.16 9.301c-.6.179-1.2-.181-1.38-.721-.18-.601.18-1.2.72-1.381 4.26-1.26 11.28-1.02 15.721 1.621.539.3.719 1.02.419 1.56-.299.421-1.02.599-1.559.3z"/></svg> \ No newline at end of file
diff --git a/.config/awesome/curious.lua b/.config/awesome/curious.lua
index 3a0bf5d..66c33da 100644
--- a/.config/awesome/curious.lua
+++ b/.config/awesome/curious.lua
@@ -15,11 +15,11 @@ local interface = Gio.DBusInterfaceInfo {
out_args = {
Gio.DBusArgInfo {
name = "widget_tree",
- signature = "av"
- }
- }
- }
- }
+ signature = "av",
+ },
+ },
+ },
+ },
}
-- iterate with ipairs, get the length of the array, check if key value from pairs is a number lower than the length and if so, use as a dict key cause it's not gonna work with :add anyway
@@ -43,17 +43,17 @@ local function serialize(builder, t)
signature = "b"
data = v
elseif _type == "thread" or _type == "function" or (_type == "userdata" and not getmetatable(v).__tostring) then
- local address = tonumber(tostring(v):match("%a+: 0x(%w+)"), 16)
+ local address = tonumber(tostring(v):match "%a+: 0x(%w+)", 16)
signature = "(s(t))"
data = { _type, address }
elseif _type == "userdata" then
if getmetatable(v).emit_signal then -- One of the properties available on any awesome luaobject
- local _, _, content = tostring(v):match("%a+/(.*)")
+ local _, _, content = tostring(v):match "%a+/(.*)"
local stringified = tostring(v)
if content then
stringified = content
end
- local type, extra, address = stringified:match("(%a+)(.*): 0x(%w+)")
+ local type, extra, address = stringified:match "(%a+)(.*): 0x(%w+)"
if extra ~= "" then
extra:sub(2, -2)
signature = "(s(st))"
@@ -64,7 +64,7 @@ local function serialize(builder, t)
end
else
signature = "(s())"
- data = { _type, {}}
+ data = { _type, {} }
end
end
@@ -72,16 +72,16 @@ local function serialize(builder, t)
builder:add("v", GLib.Variant(signature, data))
else
print(type(signature), type(data))
- local variant = GLib.Variant("{sd}", { "test", 4})
+ local variant = GLib.Variant("{sd}", { "test", 4 })
builder:add_value(GLib.Variant("v", variant))
end
end
end
function methods.Select(parameters, invocation)
- local builder = GLib.VariantBuilder(GLib.VariantType("av"))
+ local builder = GLib.VariantBuilder(GLib.VariantType "av")
serialize(builder, { "test", some_key = "some_value" })
- local tuple_builder = GLib.VariantBuilder(GLib.VariantType("(av)"))
+ local tuple_builder = GLib.VariantBuilder(GLib.VariantType "(av)")
tuple_builder:add_value(builder:_end())
-- builder
-- invocation:return_value(GLib.Variant("(av)", { builder }))
@@ -89,7 +89,10 @@ function methods.Select(parameters, invocation)
end
local function handle_call(_, _, _, _, method, parameters, invocation)
- if not methods[method] then print("invalid"); return end
+ if not methods[method] then
+ print "invalid"
+ return
+ end
gpcall(methods[method], parameters, invocation)
end
@@ -97,4 +100,9 @@ local function acquire(connection)
connection:register_object("/com/twoexem/delta/Curious", interface, GObject.Closure(handle_call))
end
-local connection = Gio.bus_own_name(Gio.BusType.SESSION, "com.twoexem.delta.Curious", Gio.BusNameOwnerFlags.NONE, GObject.Closure(acquire))
+local connection = Gio.bus_own_name(
+ Gio.BusType.SESSION,
+ "com.twoexem.delta.Curious",
+ Gio.BusNameOwnerFlags.NONE,
+ GObject.Closure(acquire)
+)
diff --git a/.config/awesome/lib/lit b/.config/awesome/lib/lit
new file mode 160000
+Subproject 06b6581137c04520cc4ffccb8f1103b961a187f
diff --git a/.config/awesome/misc/autostart.lua b/.config/awesome/misc/autostart.lua
index b089068..291e39d 100644
--- a/.config/awesome/misc/autostart.lua
+++ b/.config/awesome/misc/autostart.lua
@@ -1,7 +1,7 @@
local awful = require "awful"
-local quarrel = require "quarrel"
+local qpersistent = require "quarrel.persistent"
-if quarrel.is_restart() then
+if qpersistent.is_restart() then
return
end
@@ -11,9 +11,10 @@ local programs = {
"clipcatd",
"wezterm",
"wezterm start --class code_term",
- "firefox",
- "discord",
- "LD_PRELOAD=/usr/lib/spotify-adblock.so spotify",
+ "librewolf",
+ "keepassxc",
+ "fractal",
+ -- "LD_PRELOAD=/usr/lib/spotify-adblock.so spotify",
}
for _, program in ipairs(programs) do
diff --git a/.config/awesome/misc/cfg.lua b/.config/awesome/misc/cfg.lua
index aceaa08..f62612f 100644
--- a/.config/awesome/misc/cfg.lua
+++ b/.config/awesome/misc/cfg.lua
@@ -3,21 +3,26 @@ local awful = require "awful"
local cfg = {
terminal = "wezterm",
tags = {
+ -- home
{
layout = awful.layout.suit.floating,
selected = true,
},
+ -- browser
{
+ -- layout = awful.layout.suit.fair,
layout = awful.layout.suit.floating,
},
+ -- messaging
{
layout = awful.layout.suit.tile.left,
master_width_factor = 0.7,
},
- {
- layout = awful.layout.suit.tile.top,
- master_width_factor = 0.2,
- },
+
+ -- {
+ -- layout = awful.layout.suit.tile.top,
+ -- master_width_factor = 0.2,
+ -- },
{
layout = awful.layout.suit.tile.right,
-- master_width_factor = 0.7,
diff --git a/.config/awesome/misc/keys.lua b/.config/awesome/misc/keys.lua
index 9d994ce..8d703b8 100644
--- a/.config/awesome/misc/keys.lua
+++ b/.config/awesome/misc/keys.lua
@@ -3,21 +3,22 @@ local backlight = require "services.backlight"
local beautiful = require "beautiful"
local cfg = require "misc.cfg"
local fresnel = require "ui.fresnel"
+local gtable = require "gears.table"
local gtimer = require "gears.timer"
local insightful = require "ui.insightful"
local naughty = require "naughty"
+-- local mpris = (require "services.mpris")
+local mpris = require "ui.statusbar.panel.widgets.mpris"
local playerctl = require "services.playerctl"
local powermenu = require "ui.powermenu"
local qbind = require "quarrel.bind"
-local qstore = require "quarrel.store"
-local qvars = require "quarrel.vars"
local recording = { false, "" }
client.connect_signal("request::default_mousebindings", function()
awful.mouse.append_client_mousebindings {
- qbind:new {
- triggers = qvars.btns.left,
+ qbind {
+ triggers = qbind.btns.left,
press = function(c)
c:activate {
context = "mouse_click",
@@ -26,9 +27,9 @@ client.connect_signal("request::default_mousebindings", function()
group = "client",
desc = "raise client",
},
- qbind:new {
- mods = qvars.mods.M,
- triggers = qvars.btns.left,
+ qbind {
+ mods = qbind.mods.M,
+ triggers = qbind.btns.left,
press = function(c)
c:activate {
context = "mouse_click",
@@ -38,9 +39,9 @@ client.connect_signal("request::default_mousebindings", function()
group = "client",
desc = "move client",
},
- qbind:new {
- mods = qvars.mods.M,
- triggers = qvars.btns.right,
+ qbind {
+ mods = qbind.mods.M,
+ triggers = qbind.btns.right,
press = function(c)
c:activate {
context = "mouse_click",
@@ -55,8 +56,8 @@ end)
client.connect_signal("request::default_keybindings", function()
awful.keyboard.append_client_keybindings {
- qbind:new {
- mods = qvars.mods.MC,
+ qbind {
+ mods = qbind.mods.MC,
triggers = "q",
press = function(c)
c:kill()
@@ -64,8 +65,8 @@ client.connect_signal("request::default_keybindings", function()
group = "client",
desc = "close",
},
- qbind:new {
- mods = qvars.mods.M,
+ qbind {
+ mods = qbind.mods.M,
triggers = "m",
press = function(c)
c.maximized = not c.maximized
@@ -73,8 +74,8 @@ client.connect_signal("request::default_keybindings", function()
group = "client",
desc = "(un)maximize",
},
- qbind:new {
- mods = qvars.mods.M,
+ qbind {
+ mods = qbind.mods.M,
triggers = "n",
press = function(c)
gtimer.delayed_call(function()
@@ -84,8 +85,8 @@ client.connect_signal("request::default_keybindings", function()
group = "client",
desc = "minimize",
},
- qbind:new {
- mods = qvars.mods.M,
+ qbind {
+ mods = qbind.mods.M,
triggers = "f",
press = function(c)
c.fullscreen = not c.fullscreen
@@ -97,15 +98,15 @@ client.connect_signal("request::default_keybindings", function()
end)
awful.keyboard.append_global_keybindings {
- qbind:new {
- mods = qvars.mods.MC,
+ qbind {
+ mods = qbind.mods.MC,
triggers = "r",
press = awesome.restart,
group = "awesome",
desc = "restart awesome",
},
- qbind:new {
- mods = qvars.mods.M,
+ qbind {
+ mods = qbind.mods.M,
triggers = "F1",
press = function()
insightful:toggle()
@@ -113,8 +114,8 @@ awful.keyboard.append_global_keybindings {
group = "awesome",
desc = "toggle insightful",
},
- qbind:new {
- mods = qvars.mods.M,
+ qbind {
+ mods = qbind.mods.M,
triggers = "space",
press = function()
fresnel:show()
@@ -122,17 +123,17 @@ awful.keyboard.append_global_keybindings {
group = "awesome",
desc = "toggle fresnel",
},
- qbind:new {
- mods = qvars.mods.M,
+ qbind {
+ mods = qbind.mods.M,
triggers = "p",
press = function()
- qstore.panel_toggle:press()
+ awful.screen.focused().bar:toggle()
end,
group = "awesome",
desc = "toggle bar panel",
},
- qbind:new {
- mods = qvars.mods.M,
+ qbind {
+ mods = qbind.mods.M,
triggers = "l",
press = function()
powermenu:toggle()
@@ -141,7 +142,7 @@ awful.keyboard.append_global_keybindings {
desc = "toggle powermenu",
},
- qbind:new {
+ qbind {
triggers = "XF86AudioMute",
press = function()
awful.spawn "wpctl set-mute @DEFAULT_SINK@ toggle"
@@ -149,46 +150,65 @@ awful.keyboard.append_global_keybindings {
group = "audio",
desc = "mute",
},
- qbind:new {
+ qbind {
triggers = {
{ "XF86AudioRaiseVolume", true },
{ "XF86AudioLowerVolume", false },
},
press = function(up)
if up then
- awful.spawn "wpctl set-volume @DEFAULT_SINK@ 5%+"
+ -- awful.spawn "wpctl set-volume @DEFAULT_SINK@ 5%+"
+ print "up"
else
- awful.spawn "wpctl set-volume @DEFAULT_SINK@ 5%-"
+ print "down"
+ -- awful.spawn "wpctl set-volume @DEFAULT_SINK@ 5%-"
end
end,
group = "audio",
desc = "increase/decrease volume",
},
- qbind:new {
+ qbind {
triggers = {
{ "XF86AudioNext", true },
{ "XF86AudioPrev", false },
},
press = function(next)
+ local active_player = playerctl:list()[mpris.active_player_index].instance
if next then
- playerctl:next()
+ playerctl:next(active_player)
else
- playerctl:previous()
+ playerctl:previous(active_player)
end
end,
group = "audio",
desc = "previous/next song",
},
- qbind:new {
+ qbind {
triggers = "XF86AudioPlay",
press = function()
- playerctl:play_pause()
+ playerctl:play_pause(playerctl:list()[mpris.active_player_index].instance)
end,
group = "audio",
desc = "(un)pause song",
},
+ qbind {
+ mods = qbind.mods.M,
+ triggers = {
+ { "Next", true },
+ { "Prior", false },
+ },
+ press = function(next)
+ if next then
+ mpris.next_player()
+ else
+ mpris.previous_player()
+ end
+ end,
+ group = "audio",
+ desc = "previous/next player",
+ },
- qbind:new {
+ qbind {
triggers = {
{ "XF86MonBrightnessUp", true },
{ "XF86MonBrightnessDown", false },
@@ -204,8 +224,8 @@ awful.keyboard.append_global_keybindings {
desc = "increase/decrease brightness",
},
- qbind:new {
- mods = qvars.mods.M,
+ qbind {
+ mods = qbind.mods.M,
triggers = "Return",
press = function()
awful.spawn(cfg.terminal)
@@ -214,7 +234,7 @@ awful.keyboard.append_global_keybindings {
desc = "launch terminal",
},
- qbind:new {
+ qbind {
triggers = "Print",
press = function()
local date = os.date "%Y%m%d_%H%M%S"
@@ -235,8 +255,8 @@ awful.keyboard.append_global_keybindings {
group = "screenshot",
desc = "take fullscreen screenshot",
},
- qbind:new {
- mods = qvars.mods.S,
+ qbind {
+ mods = qbind.mods.S,
triggers = "Print",
press = function()
local date = os.date "%Y%m%d_%H%M%S"
@@ -256,8 +276,8 @@ awful.keyboard.append_global_keybindings {
desc = "take region screenshot",
},
- qbind:new {
- mods = qvars.mods.M,
+ qbind {
+ mods = qbind.mods.M,
triggers = "Print",
press = function()
if recording[1] then
@@ -283,8 +303,8 @@ awful.keyboard.append_global_keybindings {
desc = "toggle recording",
},
- qbind:new {
- mods = qvars.mods.M,
+ qbind {
+ mods = qbind.mods.M,
triggers = "k",
press = function()
awful.spawn "xkblayout-state set +1"
@@ -292,8 +312,8 @@ awful.keyboard.append_global_keybindings {
group = "keyboard",
desc = "next keyboard layout",
},
- qbind:new {
- mods = qvars.mods.MS,
+ qbind {
+ mods = qbind.mods.MS,
triggers = "k",
press = function()
awful.spawn "xkblayout-state set -1"
@@ -302,22 +322,35 @@ awful.keyboard.append_global_keybindings {
desc = "previous keyboard layout",
},
- qbind:new {
- mods = qvars.mods.M,
+ qbind {
+ mods = qbind.mods.M,
triggers = "Up",
press = awful.tag.viewprev,
group = "tag",
desc = "switch to previous",
},
- qbind:new {
- mods = qvars.mods.M,
+ qbind {
+ mods = qbind.mods.M,
triggers = "Down",
press = awful.tag.viewnext,
group = "tag",
desc = "switch to next",
},
- qbind:new {
- mods = qvars.mods.MC,
+ qbind {
+ mods = qbind.mods.M,
+ triggers = gtable.join(awful.key.keygroups.numrow, awful.key.keygroups.numpad),
+ press = function(idx)
+ local tag = awful.screen.focused().tags[idx]
+ if not tag then
+ return
+ end
+ tag:view_only()
+ end,
+ group = "tag",
+ desc = "switch to the specified tag (if it exists)",
+ },
+ qbind {
+ mods = qbind.mods.MC,
triggers = "x",
press = function()
local tag = awful.screen.focused().selected_tag
@@ -326,4 +359,16 @@ awful.keyboard.append_global_keybindings {
group = "tag",
desc = "reset master width",
},
+ qbind {
+ mods = qbind.mods.M,
+ triggers = {
+ { "XF86AudioRaiseVolume", true }, -- volume roller produces these events
+ { "XF86AudioLowerVolume", false },
+ },
+ press = function(up)
+ awful.spawn("xdotool click " .. (up and "4" or "5"))
+ end,
+ group = "misc",
+ desc = "scroll up/down",
+ },
}
diff --git a/.config/awesome/misc/rules.lua b/.config/awesome/misc/rules.lua
index 92ac596..18b11f1 100644
--- a/.config/awesome/misc/rules.lua
+++ b/.config/awesome/misc/rules.lua
@@ -20,6 +20,17 @@ ruled.client.connect_signal("request::rules", function()
},
{
+ id = "unlock_db",
+ rule = {
+ name = "Unlock Database - KeePassXC",
+ },
+ properties = {
+ sticky = true,
+ floating = true,
+ },
+ },
+
+ {
id = "titlebars",
rule_any = {
type = {
@@ -56,7 +67,7 @@ ruled.client.connect_signal("request::rules", function()
{
id = "chat_tag",
rule_any = {
- class = { "discord" },
+ class = { "fractal" },
},
properties = {
screen = 1,
@@ -64,17 +75,17 @@ ruled.client.connect_signal("request::rules", function()
},
},
- {
- id = "music_tag",
- rule_any = {
- class = { "Spotify" },
- },
- properties = {
- screen = 1,
- tag = awful.screen.focused().tags[4],
- titlebars_enabled = false,
- },
- },
+ -- {
+ -- id = "music_tag",
+ -- rule_any = {
+ -- class = { "Spotify" },
+ -- },
+ -- properties = {
+ -- screen = 1,
+ -- tag = awful.screen.focused().tags[4],
+ -- titlebars_enabled = false,
+ -- },
+ -- },
{
id = "code_tag",
@@ -83,7 +94,7 @@ ruled.client.connect_signal("request::rules", function()
},
properties = {
screen = 1,
- tag = awful.screen.focused().tags[5],
+ tag = awful.screen.focused().tags[4],
},
},
@@ -94,7 +105,7 @@ ruled.client.connect_signal("request::rules", function()
},
properties = {
screen = 1,
- tag = awful.screen.focused().tags[6],
+ tag = awful.screen.focused().tags[5],
},
},
}
diff --git a/.config/awesome/prismite.lua b/.config/awesome/prismite.lua
index 5de089f..497a1b9 100644
--- a/.config/awesome/prismite.lua
+++ b/.config/awesome/prismite.lua
@@ -1,39 +1,42 @@
local naughty = require "naughty"
-local qvars = require "quarrel.vars"
+local qcolor = require "quarrel.color"
+local qui = require "quarrel.ui"
local xresources = require "beautiful.xresources"
local dpi = xresources.apply_dpi
local theme = {}
-theme.font = qvars.font
+theme.font = qui.font()
-theme.bg_normal = qvars.colors.bg
-theme.bg_focus = qvars.colors.bg
-theme.bg_urgent = qvars.colors.bg
-theme.bg_minimize = qvars.colors.bg
-theme.bg_systray = qvars.colors.bg
+theme.bg_normal = qcolor.palette.bg()
+theme.bg_focus = qcolor.palette.bg()
+theme.bg_urgent = qcolor.palette.bg()
+theme.bg_minimize = qcolor.palette.bg()
+theme.bg_systray = qcolor.palette.bg()
-theme.fg_normal = qvars.colors.fg
-theme.fg_focus = qvars.colors.fg
-theme.fg_urgent = qvars.colors.fg
-theme.fg_minimize = qvars.colors.fg
+theme.fg_normal = qcolor.palette.fg()
+theme.fg_focus = qcolor.palette.fg()
+theme.fg_urgent = qcolor.palette.fg()
+theme.fg_minimize = qcolor.palette.fg()
-theme.useless_gap = dpi(2)
-theme.border_width = qvars.border_width
-theme.border_normal = qvars.colors.bright.black
-theme.border_focus = qvars.colors.bright.black
-theme.border_marked = qvars.colors.bright.black
+-- theme.useless_gap = dpi(2)
+theme.useless_gap = qui.PADDING / 2
+theme.border_width = qui.BORDER_WIDTH
+theme.border_normal = qcolor.palette.border()
+theme.border_focus = qcolor.palette.border()
+theme.border_marked = qcolor.palette.border()
theme.notification_icon_size = dpi(32)
-theme.notification_border_width = qvars.border_width
+theme.notification_border_width = qui.BORDER_WIDTH
theme.notification_border_color = theme.border_normal
-theme.notification_max_width = qvars.char_width * 48
-theme.notification_shape = qvars.shape
+theme.notification_max_width = qui.CHAR_WIDTH * 48
+-- theme.notification_max_width = qui.CHAR_WIDTH * 48 * 4
+theme.notification_shape = qui.shape
theme.notification_spacing = theme.useless_gap * 2
-naughty.config.defaults.timeout = qvars.notif_timeout
+-- naughty.config.defaults.timeout = qvars.notif_timeout
naughty.config.defaults.position = "bottom_right"
-naughty.config.defaults.border_width = qvars.border_width
+-- naughty.config.defaults.border_width = qui.BORDER_WIDTH
theme.tasklist_plain_task_name = true
theme.enable_spawn_cursor = false
diff --git a/.config/awesome/quarrel/animation/bezier.lua b/.config/awesome/quarrel/animation/bezier.lua
new file mode 100644
index 0000000..a505e48
--- /dev/null
+++ b/.config/awesome/quarrel/animation/bezier.lua
@@ -0,0 +1,89 @@
+local gtable = require "gears.table"
+
+-- port of https://github.com/WebKit/WebKit/blob/da934454c84ac2dcbf9fca9e5f4ac2644ef25d72/Source/WebCore/platform/graphics/UnitBezier.h
+
+local bezier = {}
+
+function bezier:sample_x(t)
+ -- `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
+ return ((self.ax * t + self.bx) * t + self.cx) * t
+end
+
+function bezier:sample_y(t)
+ return ((self.ay * t + self.by) * t + self.cy) * t
+end
+
+function bezier:sample_derivative_x(t)
+ return (3.0 * self.ax * t + 2.0 * self.bx) * t + self.cx
+end
+
+function bezier:solve_x(x, epsilon)
+ local x2, d2
+ local t2 = x
+
+ -- First try a few iterations of Newton's method -- normally very fast.
+ for _ = 1, 8 do
+ x2 = self:sample_x(t2) - x
+ if math.abs(x2) < epsilon then
+ return t2
+ end
+ d2 = self:sample_derivative_x(t2)
+ if math.abs(d2) < 1e-6 then
+ break
+ end
+ t2 = t2 - x2 / d2
+ end
+
+ -- Fall back to the bisection method for reliability.
+ local t0 = 0
+ local t1 = 1
+ t2 = x
+
+ if t2 < t0 then
+ return t0
+ end
+ if t2 > t1 then
+ return t1
+ end
+
+ while t0 < t1 do
+ x2 = self:sample_x(t2)
+ if math.abs(x2 - x) < epsilon then
+ return t2
+ end
+ if x > x2 then
+ t0 = t2
+ else
+ t1 = t2
+ end
+ t2 = (t1 - t0) * 0.5 + t0
+ end
+
+ -- Failure.
+ return t2
+end
+
+function bezier:solve(x, epsilon)
+ return self:sample_y(self:solve_x(x, epsilon))
+end
+
+local function new(x1, y1, x2, y2)
+ local obj = gtable.crush({}, bezier)
+
+ -- Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
+ obj.cx = 3.0 * x1
+ obj.bx = 3.0 * (x2 - x1) - obj.cx
+ obj.ax = 1.0 - obj.cx - obj.bx
+
+ obj.cy = 3.0 * y1
+ obj.by = 3.0 * (y2 - y1) - obj.cy
+ obj.ay = 1.0 - obj.cy - obj.by
+
+ return obj
+end
+
+return setmetatable(bezier, {
+ __call = function(_, ...)
+ return new(...)
+ end,
+})
diff --git a/.config/awesome/quarrel/animation/init.lua b/.config/awesome/quarrel/animation/init.lua
new file mode 100644
index 0000000..c57b01a
--- /dev/null
+++ b/.config/awesome/quarrel/animation/init.lua
@@ -0,0 +1,249 @@
+-------------------------------------------
+-- @author https://github.com/Kasper24
+-- @copyright 2021-2025 Kasper24
+-------------------------------------------
+local GLib = require("lgi").GLib
+local gobject = require "gears.object"
+local gpcall = require "gears.protected_call"
+local gtable = require "gears.table"
+local gtimer = require "gears.timer"
+local subscribable = require "quarrel.animation.subscribable"
+-- local qconsts = require "quarrel.consts"
+local qtween = require "quarrel.animation.tween"
+-- local qbezier = require "quarrel.animation.bezier"
+local ipairs = ipairs
+local table = table
+local pairs = pairs
+
+local animation_manager = {}
+
+-- local easing_bezier = qbezier(0.2, 0, 0, 1)
+-- animation_manager.consts = qconsts.protect(function()
+-- local duration = 0.3
+-- return {
+-- DURATION = duration,
+-- INTRO = duration / 4,
+-- EASING = function(t, b, c, d)
+-- local epsilon = 1000 / d
+-- return c * easing_bezier:solve(t/d, epsilon) + b
+-- end
+-- }
+-- end)
+
+local animation = {}
+
+local function second_to_micro(sec)
+ return sec * 1000000
+end
+
+local function framerate_tomilli(framerate)
+ return 1000 / framerate
+end
+
+local function on_no_running_animations(self, callback)
+ gtimer.start_new(0.1, function()
+ if #self._private.animations <= 0 then
+ callback()
+ return false
+ else
+ local has_non_looped_anim = false
+ for _, animation in ipairs(self._private.animations) do
+ if animation.loop == false then
+ has_non_looped_anim = true
+ end
+ end
+
+ if has_non_looped_anim == false then
+ callback()
+ return false
+ end
+ end
+
+ return true
+ end)
+end
+
+local function animation_loop(self)
+ self._private.source_id = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ framerate_tomilli(self._private.framerate),
+ function()
+ for index, animation in ipairs(self._private.animations) do
+ if animation.state == true then
+ -- compute delta time
+ local time = GLib.get_monotonic_time()
+ local delta = time - animation.last_elapsed
+ animation.last_elapsed = time
+
+ -- If pos is true, the animation has ended
+ local pos = gpcall(animation.tween.update, animation.tween, delta)
+ if pos == true then
+ -- Loop the animation, don't end it.
+ -- Useful for widgets like the spinning cicle
+ if animation.loop == true then
+ animation.tween:reset()
+ else
+ animation.state = false
+
+ -- Snap to end
+ animation.pos = animation.tween.target
+
+ gpcall(animation.emit_signal, animation, "update", animation.pos)
+ gpcall(animation.fire, animation, animation.pos)
+
+ gpcall(animation.emit_signal, animation, "ended", animation.pos)
+ gpcall(animation.ended.fire, animation, animation.pos)
+
+ table.remove(self._private.animations, index)
+ end
+ -- Animation in process, keep updating
+ else
+ animation.pos = pos
+
+ gpcall(animation.emit_signal, animation, "update", animation.pos)
+ gpcall(animation.fire, animation, animation.pos)
+ end
+ else
+ table.remove(self._private.animations, index)
+ end
+ end
+
+ -- call again the function after cooldown
+ return true
+ end
+ )
+end
+
+function animation:set(args)
+ args = args or {}
+
+ -- Awestoer/Rubbto compatibility
+ -- I'd rather this always be a table, but Awestore/Rubbto
+ -- except the :set() method to have 1 number value parameter
+ -- used to set the target
+ local is_table = type(args) == "table"
+ local initial = is_table and (args.pos or self.pos) or self.pos
+ local subject = is_table and (args.subject or self.subject) or self.subject
+ local target = is_table and (args.target or self.target) or args
+ local duration = is_table and (args.duration or self.duration) or self.duration
+ local easing = is_table and (args.easing or self.easing) or self.easing
+
+ if self.tween == nil or self.reset_on_stop == true then
+ self.tween = qtween.new {
+ initial = initial,
+ subject = subject,
+ target = target,
+ duration = second_to_micro(duration),
+ easing = easing,
+ }
+ end
+
+ if self._private.anim_manager._private.instant and self.override_instant ~= true then
+ self.pos = self.tween.target
+ self:fire(self.pos)
+ self:emit_signal("update", self.pos)
+
+ self.state = false
+ self.ended:fire(self.pos)
+ self:emit_signal("ended", self.pos)
+ return
+ end
+
+ if self._private.anim_manager._private.animations[self.index] == nil then
+ table.insert(self._private.anim_manager._private.animations, self)
+ end
+
+ self.state = true
+ self.last_elapsed = GLib.get_monotonic_time()
+
+ self.started:fire()
+ self:emit_signal "started"
+end
+
+-- Rubato compatibility
+function animation:abort()
+ self.state = false
+end
+
+function animation:stop()
+ self.state = false
+end
+
+function animation:initial()
+ return self._private.initial
+end
+
+function animation_manager:set_instant(value)
+ if value == true and self._private.instant == false then
+ on_no_running_animations(self, function()
+ -- GLib.source_remove(self._private.source_id)
+ self._private.instant = true
+ end)
+ elseif self._private.instant == true then
+ self._private.instant = false
+ -- animation_loop(self)
+ end
+end
+
+function animation_manager:set_framerate(value)
+ self._private.framerate = value
+ -- if self._private.instant == false then
+ on_no_running_animations(self, function()
+ GLib.source_remove(self._private.source_id)
+ animation_loop(self)
+ end)
+ -- end
+end
+
+function animation_manager:new(args)
+ args = args or {}
+
+ args.pos = args.pos or 0
+ args.subject = args.subject or nil
+ args.target = args.target or nil
+ args.duration = args.duration or 0
+ args.easing = args.easing or nil
+ args.loop = args.loop or false
+ args.signals = args.signals or {}
+ args.update = args.update or nil
+ args.reset_on_stop = args.reset_on_stop == nil and true or args.reset_on_stop
+
+ -- Awestoer/Rubbto compatibility
+ args.subscribed = args.subscribed or nil
+ local ret = subscribable()
+ ret.started = subscribable()
+ ret.ended = subscribable()
+ if args.subscribed ~= nil then
+ ret:subscribe(args.subscribed)
+ end
+
+ for sig, sigfun in pairs(args.signals) do
+ ret:connect_signal(sig, sigfun)
+ end
+ if args.update ~= nil then
+ ret:connect_signal("update", args.update)
+ end
+
+ gtable.crush(ret, args, true)
+ gtable.crush(ret, animation, true)
+
+ ret._private = {}
+ ret._private.anim_manager = self
+ ret._private.initial = args.pos
+ -- Can't have it private for rubato compatibility
+ ret.state = false
+
+ return ret
+end
+
+local instance = gobject {}
+gtable.crush(instance, animation_manager, true)
+
+instance._private = {}
+instance._private.animations = {}
+instance._private.instant = false
+instance._private.framerate = 60
+
+animation_loop(instance)
+
+return instance
diff --git a/.config/awesome/quarrel/animation/subscribable/init.lua b/.config/awesome/quarrel/animation/subscribable/init.lua
new file mode 100644
index 0000000..9542704
--- /dev/null
+++ b/.config/awesome/quarrel/animation/subscribable/init.lua
@@ -0,0 +1,47 @@
+-------------------------------------------
+-- @author https://github.com/Kasper24
+-- @copyright 2021-2025 Kasper24
+-- Adopted from rubato
+-------------------------------------------
+local gobject = require "gears.object"
+
+-- Kidna copying awesotre's stores on a surface level for added compatibility
+local function subscribable(args)
+ local ret = gobject {}
+ local subscribed = {}
+
+ -- Subscrubes a function to the object so that it's called when `fire` is
+ -- Calls subscribe_callback if it exists as well
+ function ret:subscribe(func)
+ local id = tostring(func):gsub("function: ", "")
+ subscribed[id] = func
+
+ if self.subscribe_callback then
+ self.subscribe_callback(func)
+ end
+ end
+
+ -- Unsubscribes a function and calls unsubscribe_callback if it exists
+ function ret:unsubscribe(func)
+ if not func then
+ subscribed = {}
+ else
+ local id = tostring(func):gsub("function: ", "")
+ subscribed[id] = nil
+ end
+
+ if self.unsubscribe_callback then
+ self.unsubscribe_callback(func)
+ end
+ end
+
+ function ret:fire(...)
+ for _, func in pairs(subscribed) do
+ func(...)
+ end
+ end
+
+ return ret
+end
+
+return subscribable
diff --git a/.config/awesome/quarrel/animation/tween/init.lua b/.config/awesome/quarrel/animation/tween/init.lua
new file mode 100644
index 0000000..5f4ce51
--- /dev/null
+++ b/.config/awesome/quarrel/animation/tween/init.lua
@@ -0,0 +1,554 @@
+-------------------------------------------
+-- @author https://github.com/Kasper24
+-- @copyright 2021-2025 Kasper24
+-------------------------------------------
+-- easing
+-- Adapted from https://github.com/EmmanuelOga/easing. See LICENSE.txt for credits.
+-- For all easing functions:
+-- t = time == how much time has to pass for the tweening to complete
+-- b = begin == starting property value
+-- c = change == ending - beginning
+-- d = duration == running time. How much time has passed *right now*
+-- local Color = require "external.lua-color"
+local gobject = require "gears.object"
+local gtable = require "gears.table"
+local tostring = tostring
+local assert = assert
+local table = table
+local pairs = pairs
+local error = error
+
+local type = type
+
+local tween = {
+ _VERSION = "tween 2.1.1",
+ _DESCRIPTION = "tweening for lua",
+ _URL = "https://github.com/kikito/tween.lua",
+ _LICENSE = [[
+ MIT LICENSE
+ Copyright (c) 2014 Enrique García Cota, Yuichi Tateno, Emmanuel Oga
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ]],
+}
+
+local pow, sin, cos, pi, sqrt, abs, asin = math.pow, math.sin, math.cos, math.pi, math.sqrt, math.abs, math.asin
+
+-- linear
+local function linear(t, b, c, d)
+ return c * t / d + b
+end
+
+-- quad
+local function inQuad(t, b, c, d)
+ return c * pow(t / d, 2) + b
+end
+local function outQuad(t, b, c, d)
+ t = t / d
+ return -c * t * (t - 2) + b
+end
+local function inOutQuad(t, b, c, d)
+ t = t / d * 2
+ if t < 1 then
+ return c / 2 * pow(t, 2) + b
+ end
+ return -c / 2 * ((t - 1) * (t - 3) - 1) + b
+end
+local function outInQuad(t, b, c, d)
+ if t < d / 2 then
+ return outQuad(t * 2, b, c / 2, d)
+ end
+ return inQuad((t * 2) - d, b + c / 2, c / 2, d)
+end
+
+-- cubic
+local function inCubic(t, b, c, d)
+ return c * pow(t / d, 3) + b
+end
+local function outCubic(t, b, c, d)
+ return c * (pow(t / d - 1, 3) + 1) + b
+end
+local function inOutCubic(t, b, c, d)
+ t = t / d * 2
+ if t < 1 then
+ return c / 2 * t * t * t + b
+ end
+ t = t - 2
+ return c / 2 * (t * t * t + 2) + b
+end
+local function outInCubic(t, b, c, d)
+ if t < d / 2 then
+ return outCubic(t * 2, b, c / 2, d)
+ end
+ return inCubic((t * 2) - d, b + c / 2, c / 2, d)
+end
+
+-- quart
+local function inQuart(t, b, c, d)
+ return c * pow(t / d, 4) + b
+end
+local function outQuart(t, b, c, d)
+ return -c * (pow(t / d - 1, 4) - 1) + b
+end
+local function inOutQuart(t, b, c, d)
+ t = t / d * 2
+ if t < 1 then
+ return c / 2 * pow(t, 4) + b
+ end
+ return -c / 2 * (pow(t - 2, 4) - 2) + b
+end
+local function outInQuart(t, b, c, d)
+ if t < d / 2 then
+ return outQuart(t * 2, b, c / 2, d)
+ end
+ return inQuart((t * 2) - d, b + c / 2, c / 2, d)
+end
+
+-- quint
+local function inQuint(t, b, c, d)
+ return c * pow(t / d, 5) + b
+end
+local function outQuint(t, b, c, d)
+ return c * (pow(t / d - 1, 5) + 1) + b
+end
+local function inOutQuint(t, b, c, d)
+ t = t / d * 2
+ if t < 1 then
+ return c / 2 * pow(t, 5) + b
+ end
+ return c / 2 * (pow(t - 2, 5) + 2) + b
+end
+local function outInQuint(t, b, c, d)
+ if t < d / 2 then
+ return outQuint(t * 2, b, c / 2, d)
+ end
+ return inQuint((t * 2) - d, b + c / 2, c / 2, d)
+end
+
+-- sine
+local function inSine(t, b, c, d)
+ return -c * cos(t / d * (pi / 2)) + c + b
+end
+local function outSine(t, b, c, d)
+ return c * sin(t / d * (pi / 2)) + b
+end
+local function inOutSine(t, b, c, d)
+ return -c / 2 * (cos(pi * t / d) - 1) + b
+end
+local function outInSine(t, b, c, d)
+ if t < d / 2 then
+ return outSine(t * 2, b, c / 2, d)
+ end
+ return inSine((t * 2) - d, b + c / 2, c / 2, d)
+end
+
+-- expo
+local function inExpo(t, b, c, d)
+ if t == 0 then
+ return b
+ end
+ return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001
+end
+local function outExpo(t, b, c, d)
+ if t == d then
+ return b + c
+ end
+ return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b
+end
+local function inOutExpo(t, b, c, d)
+ if t == 0 then
+ return b
+ end
+ if t == d then
+ return b + c
+ end
+ t = t / d * 2
+ if t < 1 then
+ return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005
+ end
+ return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b
+end
+local function outInExpo(t, b, c, d)
+ if t < d / 2 then
+ return outExpo(t * 2, b, c / 2, d)
+ end
+ return inExpo((t * 2) - d, b + c / 2, c / 2, d)
+end
+
+-- circ
+local function inCirc(t, b, c, d)
+ return (-c * (sqrt(1 - pow(t / d, 2)) - 1) + b)
+end
+local function outCirc(t, b, c, d)
+ return (c * sqrt(1 - pow(t / d - 1, 2)) + b)
+end
+local function inOutCirc(t, b, c, d)
+ t = t / d * 2
+ if t < 1 then
+ return -c / 2 * (sqrt(1 - t * t) - 1) + b
+ end
+ t = t - 2
+ return c / 2 * (sqrt(1 - t * t) + 1) + b
+end
+local function outInCirc(t, b, c, d)
+ if t < d / 2 then
+ return outCirc(t * 2, b, c / 2, d)
+ end
+ return inCirc((t * 2) - d, b + c / 2, c / 2, d)
+end
+
+-- elastic
+local function calculatePAS(p, a, c, d)
+ p, a = p or d * 0.3, a or 0
+ if a < abs(c) then
+ return p, c, p / 4
+ end -- p, a, s
+ return p, a, p / (2 * pi) * asin(c / a) -- p,a,s
+end
+local function inElastic(t, b, c, d, a, p)
+ local s
+ if t == 0 then
+ return b
+ end
+ t = t / d
+ if t == 1 then
+ return b + c
+ end
+ p, a, s = calculatePAS(p, a, c, d)
+ t = t - 1
+ return -(a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b
+end
+local function outElastic(t, b, c, d, a, p)
+ local s
+ if t == 0 then
+ return b
+ end
+ t = t / d
+ if t == 1 then
+ return b + c
+ end
+ p, a, s = calculatePAS(p, a, c, d)
+ return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p) + c + b
+end
+local function inOutElastic(t, b, c, d, a, p)
+ local s
+ if t == 0 then
+ return b
+ end
+ t = t / d * 2
+ if t == 2 then
+ return b + c
+ end
+ p, a, s = calculatePAS(p, a, c, d)
+ t = t - 1
+ if t < 0 then
+ return -0.5 * (a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b
+ end
+ return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p) * 0.5 + c + b
+end
+local function outInElastic(t, b, c, d, a, p)
+ if t < d / 2 then
+ return outElastic(t * 2, b, c / 2, d, a, p)
+ end
+ return inElastic((t * 2) - d, b + c / 2, c / 2, d, a, p)
+end
+
+-- back
+local function inBack(t, b, c, d, s)
+ s = s or 1.70158
+ t = t / d
+ return c * t * t * ((s + 1) * t - s) + b
+end
+local function outBack(t, b, c, d, s)
+ s = s or 1.70158
+ t = t / d - 1
+ return c * (t * t * ((s + 1) * t + s) + 1) + b
+end
+local function inOutBack(t, b, c, d, s)
+ s = (s or 1.70158) * 1.525
+ t = t / d * 2
+ if t < 1 then
+ return c / 2 * (t * t * ((s + 1) * t - s)) + b
+ end
+ t = t - 2
+ return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b
+end
+local function outInBack(t, b, c, d, s)
+ if t < d / 2 then
+ return outBack(t * 2, b, c / 2, d, s)
+ end
+ return inBack((t * 2) - d, b + c / 2, c / 2, d, s)
+end
+
+-- bounce
+local function outBounce(t, b, c, d)
+ t = t / d
+ if t < 1 / 2.75 then
+ return c * (7.5625 * t * t) + b
+ end
+ if t < 2 / 2.75 then
+ t = t - (1.5 / 2.75)
+ return c * (7.5625 * t * t + 0.75) + b
+ elseif t < 2.5 / 2.75 then
+ t = t - (2.25 / 2.75)
+ return c * (7.5625 * t * t + 0.9375) + b
+ end
+ t = t - (2.625 / 2.75)
+ return c * (7.5625 * t * t + 0.984375) + b
+end
+local function inBounce(t, b, c, d)
+ return c - outBounce(d - t, 0, c, d) + b
+end
+local function inOutBounce(t, b, c, d)
+ if t < d / 2 then
+ return inBounce(t * 2, 0, c, d) * 0.5 + b
+ end
+ return outBounce(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b
+end
+local function outInBounce(t, b, c, d)
+ if t < d / 2 then
+ return outBounce(t * 2, b, c / 2, d)
+ end
+ return inBounce((t * 2) - d, b + c / 2, c / 2, d)
+end
+
+tween.easing = {
+ linear = linear,
+ inQuad = inQuad,
+ outQuad = outQuad,
+ inOutQuad = inOutQuad,
+ outInQuad = outInQuad,
+ inCubic = inCubic,
+ outCubic = outCubic,
+ inOutCubic = inOutCubic,
+ outInCubic = outInCubic,
+ inQuart = inQuart,
+ outQuart = outQuart,
+ inOutQuart = inOutQuart,
+ outInQuart = outInQuart,
+ inQuint = inQuint,
+ outQuint = outQuint,
+ inOutQuint = inOutQuint,
+ outInQuint = outInQuint,
+ inSine = inSine,
+ outSine = outSine,
+ inOutSine = inOutSine,
+ outInSine = outInSine,
+ inExpo = inExpo,
+ outExpo = outExpo,
+ inOutExpo = inOutExpo,
+ outInExpo = outInExpo,
+ inCirc = inCirc,
+ outCirc = outCirc,
+ inOutCirc = inOutCirc,
+ outInCirc = outInCirc,
+ inElastic = inElastic,
+ outElastic = outElastic,
+ inOutElastic = inOutElastic,
+ outInElastic = outInElastic,
+ inBack = inBack,
+ outBack = outBack,
+ inOutBack = inOutBack,
+ outInBack = outInBack,
+ inBounce = inBounce,
+ outBounce = outBounce,
+ inOutBounce = inOutBounce,
+ outInBounce = outInBounce,
+}
+
+-- Private interface
+local function copyTables(destination, keysTable, valuesTable)
+ valuesTable = valuesTable or keysTable
+ local mt = getmetatable(keysTable)
+ if mt and getmetatable(destination) == nil then
+ setmetatable(destination, mt)
+ end
+
+ for k, v in pairs(keysTable) do
+ if type(v) == "table" then
+ destination[k] = copyTables({}, v, valuesTable[k])
+ else
+ destination[k] = valuesTable[k]
+ end
+ end
+ return destination
+end
+
+local function checkSubjectAndTargetRecursively(subject, target, path)
+ path = path or {}
+ local targetType, newPath
+ for k, targetValue in pairs(target) do
+ targetType, newPath = type(targetValue), copyTables({}, path)
+ table.insert(newPath, tostring(k))
+ if targetType == "number" then
+ assert(
+ type(subject[k]) == "number",
+ "Parameter '" .. table.concat(newPath, "/") .. "' is missing from subject or isn't a number"
+ )
+ elseif targetType == "table" then
+ checkSubjectAndTargetRecursively(subject[k], targetValue, newPath)
+ else
+ assert(
+ targetType == "number",
+ "Parameter '" .. table.concat(newPath, "/") .. "' must be a number or table of numbers"
+ )
+ end
+ end
+end
+
+local function checkNewParams(initial, duration, subject, target, easing)
+ -- assert(type(initial) == 'number' and duration > 0, "duration must be a positive number. Was " .. tostring(duration))
+ -- assert(type(duration) == 'number' and duration > 0, "duration must be a positive number. Was " .. tostring(duration))
+ assert(type(easing) == "function", "easing must be a function. Was " .. tostring(easing))
+
+ if subject and target then
+ local tsubject = type(subject)
+ assert(
+ tsubject == "table" or tsubject == "userdata",
+ "subject must be a table or userdata. Was " .. tostring(subject)
+ )
+ assert(type(target) == "table", "target must be a table. Was " .. tostring(target))
+ checkSubjectAndTargetRecursively(subject, target)
+ end
+end
+
+local function getEasingFunction(easing)
+ easing = easing or "linear"
+ if type(easing) == "string" then
+ local name = easing
+ easing = tween.easing[name]
+ if type(easing) ~= "function" then
+ error("The easing function name '" .. name .. "' is invalid")
+ end
+ end
+ return easing
+end
+
+local function performEasingOnSubject(subject, target, initial, clock, duration, easing)
+ local t, b, c, d
+ for k, v in pairs(target) do
+ if type(v) == "table" then
+ performEasingOnSubject(subject[k], v, initial[k], clock, duration, easing)
+ else
+ t, b, c, d = clock, initial[k], v - initial[k], duration
+ subject[k] = easing(t, b, c, d)
+ end
+ end
+end
+
+-- local function performEasingOnColor(initial, target, clock, duration, easing)
+-- initial = Color(initial)
+-- target = Color(target)
+--
+-- local r = easing(clock, initial.r, target.r - initial.r, duration)
+-- local g = easing(clock, initial.g, target.g - initial.g, duration)
+-- local b = easing(clock, initial.b, target.b - initial.b, duration)
+-- local a = easing(clock, initial.a, target.a - initial.a, duration)
+--
+-- return tostring(Color { r = r, g = g, b = b, a = a })
+-- end
+
+local function performEasing(table, initial, target, clock, duration, easing)
+ if type(target) == "table" then
+ local t, b, c, d
+ for k, target in pairs(target) do
+ if type(target) == "table" then
+ table[k] = {}
+ performEasing(table[k], initial[k], target, clock, duration, easing)
+ -- elseif type(target) == "string" and target:sub(1, 1) == "#" then
+ -- table[k] = performEasingOnColor(initial[k], target, clock, duration, easing)
+ else
+ t, b, c, d = clock, initial[k], target - initial[k], duration
+ table[k] = easing(t, b, c, d)
+ end
+ end
+
+ return table
+ -- elseif type(target) == "string" and target:sub(1, 1) == "#" then
+ -- return performEasingOnColor(initial, target, clock, duration, easing)
+ else
+ local t, b, c, d = clock, initial, target - initial, duration
+ return easing(t, b, c, d)
+ end
+end
+
+-- Public interface
+local Tween = {}
+
+function Tween:set(clock)
+ assert(type(clock) == "number", "clock must be a positive number or 0")
+
+ if self.subject and self.initial == 0 then
+ self.initial = copyTables({}, self.target, self.subject)
+ end
+
+ self.clock = clock
+
+ if self.clock <= 0 then
+ self.clock = 0
+ if self.subject then
+ copyTables(self.subject, self.initial)
+ end
+ elseif self.clock >= self.duration then -- the tween has expired
+ self.clock = self.duration
+
+ if self.subject then
+ copyTables(self.subject, self.target)
+ end
+ else
+ if self.subject then
+ performEasingOnSubject(self.subject, self.target, self.initial, self.clock, self.duration, self.easing)
+ else
+ local pos = {}
+ return performEasing(pos, self.initial, self.target, self.clock, self.duration, self.easing)
+ end
+ end
+
+ return self.clock >= self.duration
+end
+
+function Tween:update(dt)
+ assert(type(dt) == "number", "dt must be a number")
+ return self:set(self.clock + dt)
+end
+
+function Tween:reset()
+ return self:set(0)
+end
+
+function tween.new(args)
+ args = args or {}
+
+ args.initial = args.initial or 0
+ args.subject = args.subject or nil
+ args.target = args.target or nil
+ args.duration = args.duration or 0
+ args.easing = args.easing or nil
+
+ args.easing = getEasingFunction(args.easing)
+ checkNewParams(args.initial, args.duration, args.subject, args.target, args.easing)
+
+ local ret = gobject {}
+ ret.clock = 0
+
+ gtable.crush(ret, args, true)
+ gtable.crush(ret, Tween, true)
+
+ return ret
+end
+
+return tween
diff --git a/.config/awesome/quarrel/bezier.lua b/.config/awesome/quarrel/bezier.lua
deleted file mode 100644
index 4229961..0000000
--- a/.config/awesome/quarrel/bezier.lua
+++ /dev/null
@@ -1,343 +0,0 @@
----------------------------------------------------------------------------
---- A helper module for computations involving Bézier curves
---
--- @author Alex Belykh &lt;albel727@ngs.ru&gt;
--- @copyright 2021 Alex Belykh
--- @submodule gears.math
----------------------------------------------------------------------------
-
-local table_insert = table.insert
-
-local bezier = {}
-
---- Compute the value of a Bézier curve at a given value of the t parameter.
---
--- This function evaluates the given curve `B` of an arbitrary degree
--- at a given point t.
---
--- @tparam {number,...} c The table of control points of the curve.
--- @tparam number t The value of the t parameter to evaluate the curve at.
--- @treturn[1] number The value of `B(t)`.
--- @treturn[2] nil `nil`, if c is empty.
--- @staticfct gears.math.bezier.curve_evaluate_at
--- @see wibox.widget.graph.step_hook
-function bezier.curve_evaluate_at(c, t)
- local from = c
- local tmp = { nil, nil, nil, nil }
- while #from > 1 do
- for i = 1, #from - 1 do
- tmp[i] = from[i] * (1 - t) + from[i + 1] * t
- end
- tmp[#from] = nil
- from = tmp
- end
-
- return from[1]
-end
-
---- Split a Bézier curve into two curves at a given value of the t parameter.
---
--- This function splits the given curve `B` of an arbitrary degree at a point t
--- into two curves of the same degree `B_left` and `B_right`, such that
--- `B_left(0)=B(0)`, `B_left(1)=B(t)=B_right(0)`, `B_right(1)=B(1)`.
---
--- @tparam {number,...} c The table of control points of the curve.
--- @tparam number t The value of the t parameter to split the curve at.
--- @treturn {number,...} The table of control points for `B_left`.
--- @treturn {number,...} The table of control points for `B_right`.
--- @staticfct gears.math.bezier.curve_split_at
--- @see wibox.widget.graph.step_hook
-function bezier.curve_split_at(c, t)
- local coefs_left, coefs_right = {}, {}
- local from = c
- local tmp = { nil, nil, nil, nil }
- while #from > 0 do
- table_insert(coefs_left, from[1])
- table_insert(coefs_right, 1, from[#from])
- for i = 1, #from - 1 do
- tmp[i] = from[i] * (1 - t) + from[i + 1] * t
- end
- tmp[#from] = nil
- from = tmp
- end
-
- return coefs_left, coefs_right
-end
-
---- Get the n-th derivative Bézier curve of a Bézier curve.
---
--- This function computes control points for the curve that is
--- the derivative of order `n` in `t`, i.e. `B^(n)(t)`,
--- of the given curve `B(t)` of an arbitrary degree.
---
--- @tparam {number,...} c The table of control points of the curve.
--- @tparam[opt=1] integer n The order of the derivative to take.
--- @treturn[1] {number,...} The table of control points of `B^(n)(t)`.
--- @treturn[2] nil If n is less than 0.
--- @staticfct gears.math.bezier.curve_derivative
--- @see wibox.widget.graph.step_hook
-function bezier.curve_derivative(c, n)
- n = n or 1
- if n < 0 then
- return
- end
- if n < 1 then
- return c
- end
- local c_len = #c
- if c_len < n + 1 then
- return {}
- end
-
- local from = c
- local tmp = {}
-
- for l = c_len - 1, c_len - n, -1 do
- for i = 1, l do
- tmp[i] = (from[i + 1] - from[i]) * l
- end
- tmp[l + 1] = nil
- from = tmp
- end
-
- return from
-end
-
--- This is used instead of plain 0 to try and be compatible
--- with objects that implement their own arithmetic via metatables.
-local function get_zero(c, zero)
- return c and c * 0 or zero
-end
-
---- Compute the value of the n-th derivative of a Bézier curve
---- at a given value of the t parameter.
---
--- This is roughly the same as
--- `curve_evaluate_at(curve_derivative(c, n), t)`, but the latter
--- would throw errors or return nil instead of 0 in some cases.
---
--- @tparam {number,...} c The table of control points of the curve.
--- @tparam number t The value of the t parameter to compute the derivative at.
--- @tparam[opt=1] integer n The order of the derivative to take.
--- @tparam[opt=nil] number|nil zero The value to return if c is empty.
--- @treturn[1] number The value of `B^(n)(t)`.
--- @treturn[2] nil nil, if n is less than 0.
--- @treturn[3] number|nil The value of the zero parameter, if c is empty.
--- @staticfct gears.math.bezier.curve_derivative_at
--- @see wibox.widget.graph.step_hook
-function bezier.curve_derivative_at(c, t, n, zero)
- local d = bezier.curve_derivative(c, n)
- if not d then
- return
- end
-
- return bezier.curve_evaluate_at(d, t) or get_zero(c[1], zero)
-end
-
---- Compute the value of the 1-st derivative of a Bézier curve at t=0.
---
--- This is the same as `curve_derivative_at(c, 0)`, but since it's particularly
--- computationally simple and useful in practice, it has its own function.
---
--- @tparam {number,...} c The table of control points of the curve.
--- @tparam[opt=nil] number|nil zero The value to return if c is empty.
--- @treturn[1] number The value of `B'(0)`.
--- @treturn[2] number|nil The value of the zero parameter, if c is empty.
--- @staticfct gears.math.bezier.curve_derivative_at_zero
--- @see wibox.widget.graph.step_hook
-function bezier.curve_derivative_at_zero(c, zero)
- local l = #c
- if l < 2 then
- return get_zero(c[1], zero)
- end
- return (c[2] - c[1]) * (l - 1)
-end
-
---- Compute the value of the 1-st derivative of a Bézier curve at t=1.
---
--- This is the same as `curve_derivative_at(c, 1)`, but since it's particularly
--- computationally simple and useful in practice, it has its own function.
---
--- @tparam {number,...} c The table of control points of the curve.
--- @tparam[opt=nil] number|nil zero The value to return if c is empty.
--- @treturn[1] number The value of `B'(1)`.
--- @treturn[2] number|nil The value of the zero parameter, if c is empty.
--- @staticfct gears.math.bezier.curve_derivative_at_one
--- @see wibox.widget.graph.step_hook
-function bezier.curve_derivative_at_one(c, zero)
- local l = #c
- if l < 2 then
- return get_zero(c[1], zero)
- end
- return (c[l] - c[l - 1]) * (l - 1)
-end
-
---- Get the (n+1)-th degree Bézier curve, that has the same shape as
--- a given n-th degree Bézier curve.
---
--- Given the control points of a curve B of degree n, this function computes
--- the control points for the curve Q, such that `Q(t) = B(t)`, and
--- Q has the degree n+1, i.e. it has one control point more.
---
--- @tparam {number,...} c The table of control points of the curve B.
--- @treturn {number,...} The table of control points of the curve Q.
--- @staticfct gears.math.bezier.curve_elevate_degree
--- @see wibox.widget.graph.step_hook
-function bezier.curve_elevate_degree(c)
- local ret = { c[1] }
- local len = #c
-
- for i = 1, len - 1 do
- ret[i + 1] = (i * c[i] + (len - i) * c[i + 1]) / len
- end
-
- ret[len + 1] = c[len]
- return ret
-end
-
---- Get a cubic Bézier curve that passes through given points (up to 4).
---
--- This function takes up to 4 values and returns the 4 control points
--- for a cubic curve
---
--- `B(t) = c0\*(1-t)^3 + 3\*c1\*t\*(1-t)^2 + 3\*c2\*t^2\*(1-t) + c3\*t^3`,
--- that takes on these values at equidistant values of the t parameter.
---
--- If only p0 is given, `B(0)=B(1)=B(for all t)=p0`.
---
--- If p0 and p1 are given, `B(0)=p0` and `B(1)=p1`.
---
--- If p0, p1 and p2 are given, `B(0)=p0`, `B(1/2)=p1` and `B(1)=p2`.
---
--- For 4 points given, `B(0)=p0`, `B(1/3)=p1`, `B(2/3)=p2`, `B(1)=p3`.
---
--- @tparam number p0
--- @tparam[opt] number p1
--- @tparam[opt] number p2
--- @tparam[opt] number p3
--- @treturn number c0
--- @treturn number c1
--- @treturn number c2
--- @treturn number c3
--- @staticfct gears.math.bezier.cubic_through_points
--- @see wibox.widget.graph.step_hook
-function bezier.cubic_through_points(p0, p1, p2, p3)
- if not p1 then
- return p0, p0, p0, p0
- end
- if not p2 then
- local c1 = (2 * p0 + p1) / 3
- local c2 = (2 * p1 + p0) / 3
- return p0, c1, c2, p1
- end
- if not p3 then
- local c1 = (4 * p1 - p2) / 3
- local c2 = (4 * p1 - p0) / 3
- return p0, c1, c2, p2
- end
- local c1 = (-5 * p0 + 18 * p1 - 9 * p2 + 2 * p3) / 6
- local c2 = (-5 * p3 + 18 * p2 - 9 * p1 + 2 * p0) / 6
- return p0, c1, c2, p3
-end
-
---- Get a cubic Bézier curve with given values and derivatives at endpoints.
---
--- This function computes the 4 control points for the cubic curve B, such that
--- `B(0)=p0`, `B'(0)=d0`, `B(1)=p3`, `B'(1)=d3`.
---
--- @tparam number d0 The value of the derivative at t=0.
--- @tparam number p0 The value of the curve at t=0.
--- @tparam number p3 The value of the curve at t=1.
--- @tparam number d3 The value of the derivative at t=1.
--- @treturn number c0
--- @treturn number c1
--- @treturn number c2
--- @treturn number c3
--- @staticfct gears.math.bezier.cubic_from_points_and_derivatives
--- @see wibox.widget.graph.step_hook
-function bezier.cubic_from_points_and_derivatives(d0, p0, p3, d3)
- local c1 = p0 + d0 / 3
- local c2 = p3 - d3 / 3
- return p0, c1, c2, p3
-end
-
---- Get a cubic Bézier curve with given values at endpoints and starting
---- derivative, while minimizing (an approximation of) the stretch energy.
---
--- This function computes the 4 control points for the cubic curve B, such that
--- `B(0)=p0`, `B'(0)=d0`, `B(1)=p3`, and
--- the integral of `(B'(t))^2` on `t=[0,1]` is minimal.
--- (The actual stretch energy is the integral of `|B'(t)|`)
---
--- In practical terms this is almost the same as "the curve of shortest length
--- connecting given points and having the given starting speed".
---
--- @tparam number d0 The value of the derivative at t=0.
--- @tparam number p0 The value of the curve at t=0.
--- @tparam number p3 The value of the curve at t=1.
--- @treturn number c0
--- @treturn number c1
--- @treturn number c2
--- @treturn number c3
--- @staticfct gears.math.bezier.cubic_from_derivative_and_points_min_stretch
--- @see wibox.widget.graph.step_hook
-function bezier.cubic_from_derivative_and_points_min_stretch(d0, p0, p3)
- local c1 = p0 + d0 / 3
- local c2 = (2 * p0 - c1 + 3 * p3) / 4
- return p0, c1, c2, p3
-end
-
---- Get a cubic Bézier curve with given values at endpoints and starting
---- derivative, while minimizing (an approximation of) the strain energy.
---
--- This function computes the 4 control points for the cubic curve B, such that
--- `B(0)=p0`, `B'(0)=d0`, `B(1)=p3`, and
--- the integral of `(B''(t))^2` on `t=[0,1]` is minimal.
---
--- In practical terms this is almost the same as "the curve of smallest
--- speed change connecting given points and having the given starting speed".
---
--- @tparam number d0 The value of the derivative at t=0.
--- @tparam number p0 The value of the curve at t=0.
--- @tparam number p3 The value of the curve at t=1.
--- @treturn number c0
--- @treturn number c1
--- @treturn number c2
--- @treturn number c3
--- @staticfct gears.math.bezier.cubic_from_derivative_and_points_min_strain
--- @see wibox.widget.graph.step_hook
-function bezier.cubic_from_derivative_and_points_min_strain(d, p0, p3)
- local c1 = p0 + d / 3
- local c2 = (c1 + p3) / 2
- return p0, c1, c2, p3
-end
-
---- Get a cubic Bézier curve with given values at endpoints and starting
---- derivative, while minimizing the jerk energy.
---
--- This function computes the 4 control points for the cubic curve B, such that
--- `B(0)=p0`, `B'(0)=d0`, `B(1)=p3`, and
--- the integral of `(B'''(t))^2` on `t=[0,1]` is minimal.
---
--- In practical terms this is almost the same as "the curve of smallest
--- acceleration change connecting given points and having the given
--- starting speed".
---
--- @tparam number d0 The value of the derivative at t=0.
--- @tparam number p0 The value of the curve at t=0.
--- @tparam number p3 The value of the curve at t=1.
--- @treturn number c0
--- @treturn number c1
--- @treturn number c2
--- @treturn number c3
--- @staticfct gears.math.bezier.cubic_from_derivative_and_points_min_jerk
--- @see wibox.widget.graph.step_hook
-function bezier.cubic_from_derivative_and_points_min_jerk(d, p0, p3)
- local c1 = p0 + d / 3
- local c2 = c1 + (p3 - p0) / 3
- return p0, c1, c2, p3
-end
-
-return bezier
-
--- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/.config/awesome/quarrel/bind/consts.lua b/.config/awesome/quarrel/bind/consts.lua
new file mode 100644
index 0000000..5a91d84
--- /dev/null
+++ b/.config/awesome/quarrel/bind/consts.lua
@@ -0,0 +1,27 @@
+local awful = require "awful"
+
+local C = {}
+
+-- taken from https://github.com/bew/dotfiles/blob/ab9bb1935783f7a31ef777b1d7e26d53f35df864/gui/wezterm/cfg_utils.lua
+C.mods = setmetatable({ _SHORT_MAP = { C = "Control", S = "Shift", A = "Mod1", M = "Mod4" } }, {
+ __index = function(self, key)
+ local resolved_mods = {}
+ for i = 1, #key do
+ resolved_mods[i] = self._SHORT_MAP[key:sub(i, i)]
+ end
+ return resolved_mods
+ end,
+})
+
+local btns = awful.button.names
+
+---@enum buttons
+C.btns = {
+ left = btns.LEFT,
+ right = btns.RIGHT,
+ middle = btns.MIDDLE,
+ up = btns.SCROLL_UP,
+ down = btns.SCROLL_DOWN,
+}
+
+return C
diff --git a/.config/awesome/quarrel/bind.lua b/.config/awesome/quarrel/bind/init.lua
index a1abf29..a851440 100644
--- a/.config/awesome/quarrel/bind.lua
+++ b/.config/awesome/quarrel/bind/init.lua
@@ -1,11 +1,10 @@
+local C = require "quarrel.bind.consts"
local awful = require "awful"
local gtable = require "gears.table"
-local qstore = require "quarrel.store"
----@class QuarrelBind
-local qbind = {}
-
-qstore.bindings = {}
+local M = gtable.crush({
+ bindings = {},
+}, C)
---@alias mouse_button
---| 0 Left mouse button
@@ -35,7 +34,7 @@ qstore.bindings = {}
---@field press fun(...) | fun(any, ...) Function to run when the trigger is pressed
---@field desc string? Description
---@field group string? What group the binding will show up in
----@field triggers Trigger[] | bind
+---@field triggers Trigger[] | bind[] | bind
---Get the corresponding binding creation function for a trigger
---@param bind bind
@@ -83,24 +82,30 @@ end
--- Create a new binding
---@param binding Binding
---@return awful.key[]
-function qbind:new(binding)
+function M:new(binding)
if not binding.hidden then
- table.insert(qstore.bindings, binding)
+ table.insert(self.bindings, binding)
end
binding.mods = binding.mods or {}
local awful_bindings = {}
if type(binding.triggers) == "table" then
- for _, trigger in
- ipairs(binding.triggers --[[@as Trigger[]])
+ for _, _trigger in
+ ipairs(binding.triggers --[[@as bind[] | Trigger[]])
do
+ local trigger
+ if type(_trigger) == "table" then
+ trigger = _trigger
+ elseif type(_trigger) == "string" or type(_trigger) == "number" then
+ trigger = { _trigger, _trigger }
+ end
table.insert(awful_bindings, translate_binding(binding, trigger, true))
end
elseif type(binding.triggers) == "string" or type(binding.triggers) == "number" then
return translate_binding(binding, binding.triggers --[[@as bind]], false)
else
- error "binding.triggers can only be a string or a table"
+ error("binding.triggers can only be a string or a table (passed in " .. type(binding.triggers) .. ")")
end
-- for some reason multi-trigger bindings only work if i do this
@@ -109,4 +114,6 @@ function qbind:new(binding)
return gtable.join(table.unpack(awful_bindings))
end
-return qbind
+return setmetatable(M, {
+ __call = M.new,
+})
diff --git a/.config/awesome/quarrel/color.lua b/.config/awesome/quarrel/color.lua
new file mode 100644
index 0000000..b96ef57
--- /dev/null
+++ b/.config/awesome/quarrel/color.lua
@@ -0,0 +1,64 @@
+-- autogenerated from palette.json
+local M = {}
+
+local function tone_set(colors)
+ return setmetatable(colors, {
+ __call = function(self)
+ return self[1]
+ end,
+ })
+end
+
+M.palette = {
+ bg = tone_set {
+ high = "#171c22",
+ highest = "#1d2228",
+ low = "#0c1116",
+ lowest = "#070c11",
+ "#12161c",
+ },
+ blue = tone_set {
+ bright = "#abe4ff",
+ "#8bc3fc",
+ },
+ border = tone_set {
+ "#323b46",
+ variant = "#262f39",
+ },
+ cyan = tone_set {
+ bright = "#8ef4e2",
+ "#6dd3c2",
+ },
+ fg = tone_set {
+ high = "#e8eff8",
+ low = "#a8afb7",
+ "#c8ced7",
+ },
+ green = tone_set {
+ bright = "#abf3b3",
+ "#8bd294",
+ },
+ orange = tone_set {
+ bright = "#ffc08e",
+ "#ff9f6f",
+ },
+ pink = tone_set {
+ bright = "#ffccd4",
+ "#e5acb4",
+ },
+ purple = tone_set {
+ bright = "#e4d1ff",
+ "#c4b1f6",
+ },
+ red = tone_set {
+ bright = "#ffb2a9",
+ "#ff928a",
+ },
+ yellow = tone_set {
+ bright = "#ffd278",
+ "#ecb256",
+ },
+ transparent = "#00000000",
+}
+
+return M
diff --git a/.config/awesome/quarrel/const.lua b/.config/awesome/quarrel/const.lua
new file mode 100644
index 0000000..342ae41
--- /dev/null
+++ b/.config/awesome/quarrel/const.lua
@@ -0,0 +1,22 @@
+local M = {}
+
+---@param protected (fun(): table) | table
+function M.protect(protected)
+ ---@type table
+ local tbl
+ if type(protected) == "table" then
+ tbl = protected
+ elseif type(protected) == "function" then
+ tbl = protected()
+ else
+ error("expected a table or a function that returns a table, got " .. type(protected), 2)
+ end
+ return setmetatable({}, {
+ __index = tbl,
+ __newindex = function(_, k, v)
+ error("attempted to change constant " .. tostring(k) .. " to " .. tostring(v), 2)
+ end,
+ })
+end
+
+return M
diff --git a/.config/awesome/quarrel/debugger.lua b/.config/awesome/quarrel/debugger.lua
new file mode 100644
index 0000000..b1a272a
--- /dev/null
+++ b/.config/awesome/quarrel/debugger.lua
@@ -0,0 +1,865 @@
+-- SPDX-License-Identifier: MIT
+-- Copyright (c) 2024 Scott Lembcke and Howling Moon Software
+
+local dbg
+
+-- Use ANSI color codes in the prompt by default.
+local COLOR_GRAY = ""
+local COLOR_RED = ""
+local COLOR_BLUE = ""
+local COLOR_YELLOW = ""
+local COLOR_RESET = ""
+local GREEN_CARET = " => "
+
+local function pretty(obj, max_depth)
+ if max_depth == nil then
+ max_depth = dbg.pretty_depth
+ end
+
+ -- Returns true if a table has a __tostring metamethod.
+ local function coerceable(tbl)
+ local meta = getmetatable(tbl)
+ return (meta and meta.__tostring)
+ end
+
+ local function recurse(obj, depth)
+ if type(obj) == "string" then
+ -- Dump the string so that escape sequences are printed.
+ return string.format("%q", obj)
+ elseif type(obj) == "table" and depth < max_depth and not coerceable(obj) then
+ local str = "{"
+
+ for k, v in pairs(obj) do
+ local pair = pretty(k, 0) .. " = " .. recurse(v, depth + 1)
+ str = str .. (str == "{" and pair or ", " .. pair)
+ end
+
+ return str .. "}"
+ else
+ -- tostring() can fail if there is an error in a __tostring metamethod.
+ local success, value = pcall(function()
+ return tostring(obj)
+ end)
+ return (success and value or "<!!error in __tostring metamethod!!>")
+ end
+ end
+
+ return recurse(obj, 0)
+end
+
+-- The stack level that cmd_* functions use to access locals or info
+-- The structure of the code very carefully ensures this.
+local CMD_STACK_LEVEL = 6
+
+-- Location of the top of the stack outside of the debugger.
+-- Adjusted by some debugger entrypoints.
+local stack_top = 0
+
+-- The current stack frame index.
+-- Changed using the up/down commands
+local stack_inspect_offset = 0
+
+-- LuaJIT has an off by one bug when setting local variables.
+local LUA_JIT_SETLOCAL_WORKAROUND = 0
+
+-- Default dbg.read function
+local function dbg_read(prompt)
+ dbg.write(prompt)
+ io.flush()
+ return io.read()
+end
+
+-- Default dbg.write function
+local function dbg_write(str)
+ io.write(str)
+end
+
+local function dbg_writeln(str, ...)
+ if select("#", ...) == 0 then
+ dbg.write((str or "<NULL>") .. "\n")
+ else
+ dbg.write(string.format(str .. "\n", ...))
+ end
+end
+
+local function format_loc(file, line)
+ return COLOR_BLUE .. file .. COLOR_RESET .. ":" .. COLOR_YELLOW .. line .. COLOR_RESET
+end
+local function format_stack_frame_info(info)
+ local filename = info.source:match "@(.*)"
+ local source = filename and dbg.shorten_path(filename) or info.short_src
+ local namewhat = (info.namewhat == "" and "chunk at" or info.namewhat)
+ local name = (
+ info.name and "'" .. COLOR_BLUE .. info.name .. COLOR_RESET .. "'" or format_loc(source, info.linedefined)
+ )
+ return format_loc(source, info.currentline) .. " in " .. namewhat .. " " .. name
+end
+
+local repl
+
+-- Return false for stack frames without source,
+-- which includes C frames, Lua bytecode, and `loadstring` functions
+local function frame_has_line(info)
+ return info.currentline >= 0
+end
+
+local function hook_factory(repl_threshold)
+ return function(offset, reason)
+ return function(event, _)
+ -- Skip events that don't have line information.
+ if not frame_has_line(debug.getinfo(2)) then
+ return
+ end
+
+ -- Tail calls are specifically ignored since they also will have tail returns to balance out.
+ if event == "call" then
+ offset = offset + 1
+ elseif event == "return" and offset > repl_threshold then
+ offset = offset - 1
+ elseif event == "line" and offset <= repl_threshold then
+ repl(reason)
+ end
+ end
+ end
+end
+
+local hook_step = hook_factory(1)
+local hook_next = hook_factory(0)
+local hook_finish = hook_factory(-1)
+
+-- Create a table of all the locally accessible variables.
+-- Globals are not included when running the locals command, but are when running the print command.
+local function local_bindings(offset, include_globals)
+ local level = offset + stack_inspect_offset + CMD_STACK_LEVEL
+ local func = debug.getinfo(level).func
+ local bindings = {}
+
+ -- Retrieve the upvalues
+ do
+ local i = 1
+ while true do
+ local name, value = debug.getupvalue(func, i)
+ if not name then
+ break
+ end
+ bindings[name] = value
+ i = i + 1
+ end
+ end
+
+ -- Retrieve the locals (overwriting any upvalues)
+ do
+ local i = 1
+ while true do
+ local name, value = debug.getlocal(level, i)
+ if not name then
+ break
+ end
+ bindings[name] = value
+ i = i + 1
+ end
+ end
+
+ -- Retrieve the varargs (works in Lua 5.2 and LuaJIT)
+ local varargs = {}
+ do
+ local i = 1
+ while true do
+ local name, value = debug.getlocal(level, -i)
+ if not name then
+ break
+ end
+ varargs[i] = value
+ i = i + 1
+ end
+ end
+ if #varargs > 0 then
+ bindings["..."] = varargs
+ end
+
+ if include_globals then
+ -- In Lua 5.2, you have to get the environment table from the function's locals.
+ local env = (_VERSION <= "Lua 5.1" and getfenv(func) or bindings._ENV)
+ return setmetatable(bindings, { __index = env or _G })
+ else
+ return bindings
+ end
+end
+
+-- Used as a __newindex metamethod to modify variables in cmd_eval().
+local function mutate_bindings(_, name, value)
+ local FUNC_STACK_OFFSET = 3 -- Stack depth of this function.
+ local level = stack_inspect_offset + FUNC_STACK_OFFSET + CMD_STACK_LEVEL
+
+ -- Set a local.
+ do
+ local i = 1
+ repeat
+ local var = debug.getlocal(level, i)
+ if name == var then
+ dbg_writeln(
+ COLOR_YELLOW
+ .. "debugger.lua"
+ .. GREEN_CARET
+ .. "Set local variable "
+ .. COLOR_BLUE
+ .. name
+ .. COLOR_RESET
+ )
+ return debug.setlocal(level + LUA_JIT_SETLOCAL_WORKAROUND, i, value)
+ end
+ i = i + 1
+ until var == nil
+ end
+
+ -- Set an upvalue.
+ local func = debug.getinfo(level).func
+ do
+ local i = 1
+ repeat
+ local var = debug.getupvalue(func, i)
+ if name == var then
+ dbg_writeln(
+ COLOR_YELLOW .. "debugger.lua" .. GREEN_CARET .. "Set upvalue " .. COLOR_BLUE .. name .. COLOR_RESET
+ )
+ return debug.setupvalue(func, i, value)
+ end
+ i = i + 1
+ until var == nil
+ end
+
+ -- Set a global.
+ dbg_writeln(
+ COLOR_YELLOW .. "debugger.lua" .. GREEN_CARET .. "Set global variable " .. COLOR_BLUE .. name .. COLOR_RESET
+ )
+ _G[name] = value
+end
+
+-- Compile an expression with the given variable bindings.
+local function compile_chunk(block, env)
+ local source = "debugger.lua REPL"
+ local chunk = nil
+
+ if _VERSION <= "Lua 5.1" then
+ chunk = loadstring(block, source)
+ if chunk then
+ setfenv(chunk, env)
+ end
+ else
+ -- The Lua 5.2 way is a bit cleaner
+ chunk = load(block, source, "t", env)
+ end
+
+ if not chunk then
+ dbg_writeln(COLOR_RED .. "Error: Could not compile block:\n" .. COLOR_RESET .. block)
+ end
+ return chunk
+end
+
+local SOURCE_CACHE = {}
+
+local function where(info, context_lines)
+ local source = SOURCE_CACHE[info.source]
+ if not source then
+ source = {}
+ local filename = info.source:match "@(.*)"
+ if filename then
+ pcall(function()
+ for line in io.lines(filename) do
+ table.insert(source, line)
+ end
+ end)
+ elseif info.source then
+ for line in info.source:gmatch "[^\n]+" do
+ table.insert(source, line)
+ end
+ end
+ SOURCE_CACHE[info.source] = source
+ end
+
+ if source and source[info.currentline] then
+ for i = info.currentline - context_lines, info.currentline + context_lines do
+ local tab_or_caret = (i == info.currentline and GREEN_CARET or " ")
+ local line = source[i]
+ if line then
+ dbg_writeln(COLOR_GRAY .. "% 4d" .. tab_or_caret .. "%s", i, line)
+ end
+ end
+ else
+ dbg_writeln(COLOR_RED .. "Error: Source not available for " .. COLOR_BLUE .. info.short_src)
+ end
+
+ return false
+end
+
+-- Wee version differences
+local unpack = unpack or table.unpack
+local pack = function(...)
+ return { n = select("#", ...), ... }
+end
+
+local function cmd_step()
+ stack_inspect_offset = stack_top
+ return true, hook_step
+end
+
+local function cmd_next()
+ stack_inspect_offset = stack_top
+ return true, hook_next
+end
+
+local function cmd_finish()
+ local offset = stack_top - stack_inspect_offset
+ stack_inspect_offset = stack_top
+ return true, offset < 0 and hook_factory(offset - 1) or hook_finish
+end
+
+local function cmd_print(expr)
+ local env = local_bindings(1, true)
+ local chunk = compile_chunk("return " .. expr, env)
+ if chunk == nil then
+ return false
+ end
+
+ -- Call the chunk and collect the results.
+ local results = pack(pcall(chunk, unpack(rawget(env, "...") or {})))
+
+ -- The first result is the pcall error.
+ if not results[1] then
+ dbg_writeln(COLOR_RED .. "Error:" .. COLOR_RESET .. " " .. results[2])
+ else
+ local output = ""
+ for i = 2, results.n do
+ output = output .. (i ~= 2 and ", " or "") .. dbg.pretty(results[i])
+ end
+
+ if output == "" then
+ output = "<no result>"
+ end
+ dbg_writeln(COLOR_BLUE .. expr .. GREEN_CARET .. output)
+ end
+
+ return false
+end
+
+local function cmd_eval(code)
+ local env = local_bindings(1, true)
+ local mutable_env = setmetatable({}, {
+ __index = env,
+ __newindex = mutate_bindings,
+ })
+
+ local chunk = compile_chunk(code, mutable_env)
+ if chunk == nil then
+ return false
+ end
+
+ -- Call the chunk and collect the results.
+ local success, err = pcall(chunk, unpack(rawget(env, "...") or {}))
+ if not success then
+ dbg_writeln(COLOR_RED .. "Error:" .. COLOR_RESET .. " " .. tostring(err))
+ end
+
+ return false
+end
+
+local function cmd_down()
+ local offset = stack_inspect_offset
+ local info
+
+ repeat -- Find the next frame with a file.
+ offset = offset + 1
+ info = debug.getinfo(offset + CMD_STACK_LEVEL)
+ until not info or frame_has_line(info)
+
+ if info then
+ stack_inspect_offset = offset
+ dbg_writeln("Inspecting frame: " .. format_stack_frame_info(info))
+ if tonumber(dbg.auto_where) then
+ where(info, dbg.auto_where)
+ end
+ else
+ info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL)
+ dbg_writeln "Already at the bottom of the stack."
+ end
+
+ return false
+end
+
+local function cmd_up()
+ local offset = stack_inspect_offset
+ local info
+
+ repeat -- Find the next frame with a file.
+ offset = offset - 1
+ if offset < stack_top then
+ info = nil
+ break
+ end
+ info = debug.getinfo(offset + CMD_STACK_LEVEL)
+ until frame_has_line(info)
+
+ if info then
+ stack_inspect_offset = offset
+ dbg_writeln("Inspecting frame: " .. format_stack_frame_info(info))
+ if tonumber(dbg.auto_where) then
+ where(info, dbg.auto_where)
+ end
+ else
+ info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL)
+ dbg_writeln "Already at the top of the stack."
+ end
+
+ return false
+end
+
+local function cmd_inspect(offset)
+ offset = stack_top + tonumber(offset)
+ local info = debug.getinfo(offset + CMD_STACK_LEVEL)
+ if info then
+ stack_inspect_offset = offset
+ dbg.writeln("Inspecting frame: " .. format_stack_frame_info(info))
+ else
+ dbg.writeln(COLOR_RED .. "ERROR: " .. COLOR_BLUE .. "Invalid stack frame index." .. COLOR_RESET)
+ end
+end
+
+local function cmd_where(context_lines)
+ local info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL)
+ return (info and where(info, tonumber(context_lines) or 5))
+end
+
+local function cmd_trace()
+ dbg_writeln("Inspecting frame %d", stack_inspect_offset - stack_top)
+ local i = 0
+ while true do
+ local info = debug.getinfo(stack_top + CMD_STACK_LEVEL + i)
+ if not info then
+ break
+ end
+
+ local is_current_frame = (i + stack_top == stack_inspect_offset)
+ local tab_or_caret = (is_current_frame and GREEN_CARET or " ")
+ dbg_writeln(COLOR_GRAY .. "% 4d" .. COLOR_RESET .. tab_or_caret .. "%s", i, format_stack_frame_info(info))
+ i = i + 1
+ end
+
+ return false
+end
+
+local function cmd_locals()
+ local bindings = local_bindings(1, false)
+
+ -- Get all the variable binding names and sort them
+ local keys = {}
+ for k, _ in pairs(bindings) do
+ table.insert(keys, k)
+ end
+ table.sort(keys)
+
+ for _, k in ipairs(keys) do
+ local v = bindings[k]
+
+ -- Skip the debugger object itself, "(*internal)" values, and Lua 5.2's _ENV object.
+ if not rawequal(v, dbg) and k ~= "_ENV" and not k:match "%(.*%)" then
+ dbg_writeln(" " .. COLOR_BLUE .. k .. GREEN_CARET .. dbg.pretty(v))
+ end
+ end
+
+ return false
+end
+
+local function cmd_help()
+ dbg.write(
+ ""
+ .. COLOR_BLUE
+ .. " <return>"
+ .. GREEN_CARET
+ .. "re-run last command\n"
+ .. COLOR_BLUE
+ .. " c"
+ .. COLOR_YELLOW
+ .. "(ontinue)"
+ .. GREEN_CARET
+ .. "continue execution\n"
+ .. COLOR_BLUE
+ .. " s"
+ .. COLOR_YELLOW
+ .. "(tep)"
+ .. GREEN_CARET
+ .. "step forward by one line (into functions)\n"
+ .. COLOR_BLUE
+ .. " n"
+ .. COLOR_YELLOW
+ .. "(ext)"
+ .. GREEN_CARET
+ .. "step forward by one line (skipping over functions)\n"
+ .. COLOR_BLUE
+ .. " f"
+ .. COLOR_YELLOW
+ .. "(inish)"
+ .. GREEN_CARET
+ .. "step forward until exiting the current function\n"
+ .. COLOR_BLUE
+ .. " u"
+ .. COLOR_YELLOW
+ .. "(p)"
+ .. GREEN_CARET
+ .. "move up the stack by one frame\n"
+ .. COLOR_BLUE
+ .. " d"
+ .. COLOR_YELLOW
+ .. "(own)"
+ .. GREEN_CARET
+ .. "move down the stack by one frame\n"
+ .. COLOR_BLUE
+ .. " i"
+ .. COLOR_YELLOW
+ .. "(nspect) "
+ .. COLOR_BLUE
+ .. "[index]"
+ .. GREEN_CARET
+ .. "move to a specific stack frame\n"
+ .. COLOR_BLUE
+ .. " w"
+ .. COLOR_YELLOW
+ .. "(here) "
+ .. COLOR_BLUE
+ .. "[line count]"
+ .. GREEN_CARET
+ .. "print source code around the current line\n"
+ .. COLOR_BLUE
+ .. " e"
+ .. COLOR_YELLOW
+ .. "(val) "
+ .. COLOR_BLUE
+ .. "[statement]"
+ .. GREEN_CARET
+ .. "execute the statement\n"
+ .. COLOR_BLUE
+ .. " p"
+ .. COLOR_YELLOW
+ .. "(rint) "
+ .. COLOR_BLUE
+ .. "[expression]"
+ .. GREEN_CARET
+ .. "execute the expression and print the result\n"
+ .. COLOR_BLUE
+ .. " t"
+ .. COLOR_YELLOW
+ .. "(race)"
+ .. GREEN_CARET
+ .. "print the stack trace\n"
+ .. COLOR_BLUE
+ .. " l"
+ .. COLOR_YELLOW
+ .. "(ocals)"
+ .. GREEN_CARET
+ .. "print the function arguments, locals and upvalues.\n"
+ .. COLOR_BLUE
+ .. " h"
+ .. COLOR_YELLOW
+ .. "(elp)"
+ .. GREEN_CARET
+ .. "print this message\n"
+ .. COLOR_BLUE
+ .. " q"
+ .. COLOR_YELLOW
+ .. "(uit)"
+ .. GREEN_CARET
+ .. "halt execution\n"
+ )
+ return false
+end
+
+local last_cmd = false
+
+local commands = {
+ ["^c$"] = function()
+ return true
+ end,
+ ["^s$"] = cmd_step,
+ ["^n$"] = cmd_next,
+ ["^f$"] = cmd_finish,
+ ["^p%s+(.*)$"] = cmd_print,
+ ["^e%s+(.*)$"] = cmd_eval,
+ ["^u$"] = cmd_up,
+ ["^d$"] = cmd_down,
+ ["i%s*(%d+)"] = cmd_inspect,
+ ["^w%s*(%d*)$"] = cmd_where,
+ ["^t$"] = cmd_trace,
+ ["^l$"] = cmd_locals,
+ ["^h$"] = cmd_help,
+ ["^q$"] = function()
+ dbg.exit(0)
+ return true
+ end,
+}
+
+local function match_command(line)
+ for pat, func in pairs(commands) do
+ -- Return the matching command and capture argument.
+ if line:find(pat) then
+ return func, line:match(pat)
+ end
+ end
+end
+
+-- Run a command line
+-- Returns true if the REPL should exit and the hook function factory
+local function run_command(line)
+ -- GDB/LLDB exit on ctrl-d
+ if line == nil then
+ dbg.exit(1)
+ return true
+ end
+
+ -- Re-execute the last command if you press return.
+ if line == "" then
+ line = last_cmd or "h"
+ end
+
+ local command, command_arg = match_command(line)
+ if command then
+ last_cmd = line
+ -- unpack({...}) prevents tail call elimination so the stack frame indices are predictable.
+ return unpack { command(command_arg) }
+ elseif dbg.auto_eval then
+ return unpack { cmd_eval(line) }
+ else
+ dbg_writeln(
+ COLOR_RED
+ .. "Error:"
+ .. COLOR_RESET
+ .. " command '%s' not recognized.\nType 'h' and press return for a command list.",
+ line
+ )
+ return false
+ end
+end
+
+repl = function(reason)
+ -- Skip frames without source info.
+ while not frame_has_line(debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL - 3)) do
+ stack_inspect_offset = stack_inspect_offset + 1
+ end
+
+ local info = debug.getinfo(stack_inspect_offset + CMD_STACK_LEVEL - 3)
+ reason = reason and (COLOR_YELLOW .. "break via " .. COLOR_RED .. reason .. GREEN_CARET) or ""
+ dbg_writeln(reason .. format_stack_frame_info(info))
+
+ if tonumber(dbg.auto_where) then
+ where(info, dbg.auto_where)
+ end
+
+ repeat
+ local success, done, hook = pcall(run_command, dbg.read(COLOR_RED .. "debugger.lua> " .. COLOR_RESET))
+ if success then
+ debug.sethook(hook and hook(0), "crl")
+ else
+ local message = COLOR_RED .. "INTERNAL DEBUGGER.LUA ERROR. ABORTING\n:" .. COLOR_RESET .. " " .. done
+ dbg_writeln(message)
+ error(message)
+ end
+ until done
+end
+
+-- Make the debugger object callable like a function.
+dbg = setmetatable({}, {
+ __call = function(_, condition, top_offset, source)
+ if condition then
+ return
+ end
+
+ top_offset = (top_offset or 0)
+ stack_inspect_offset = top_offset
+ stack_top = top_offset
+
+ debug.sethook(hook_next(1, source or "dbg()"), "crl")
+ return
+ end,
+})
+
+-- Expose the debugger's IO functions.
+dbg.read = dbg_read
+dbg.write = dbg_write
+dbg.shorten_path = function(path)
+ return path
+end
+dbg.exit = function(err)
+ os.exit(err)
+end
+
+dbg.writeln = dbg_writeln
+
+dbg.pretty_depth = 3
+dbg.pretty = pretty
+dbg.pp = function(value, depth)
+ dbg_writeln(dbg.pretty(value, depth))
+end
+
+dbg.auto_where = false
+dbg.auto_eval = false
+
+local lua_error, lua_assert = error, assert
+
+-- Works like error(), but invokes the debugger.
+function dbg.error(err, level)
+ level = level or 1
+ dbg_writeln(COLOR_RED .. "ERROR: " .. COLOR_RESET .. dbg.pretty(err))
+ dbg(false, level, "dbg.error()")
+
+ lua_error(err, level)
+end
+
+-- Works like assert(), but invokes the debugger on a failure.
+function dbg.assert(condition, message)
+ message = message or "assertion failed!"
+ if not condition then
+ dbg_writeln(COLOR_RED .. "ERROR: " .. COLOR_RESET .. message)
+ dbg(false, 1, "dbg.assert()")
+ end
+
+ return lua_assert(condition, message)
+end
+
+-- Works like pcall(), but invokes the debugger on an error.
+function dbg.call(f, ...)
+ return xpcall(f, function(err)
+ dbg_writeln(COLOR_RED .. "ERROR: " .. COLOR_RESET .. dbg.pretty(err))
+ dbg(false, 1, "dbg.call()")
+
+ return err
+ end, ...)
+end
+
+-- Error message handler that can be used with lua_pcall().
+function dbg.msgh(...)
+ if debug.getinfo(2) then
+ dbg_writeln(COLOR_RED .. "ERROR: " .. COLOR_RESET .. dbg.pretty(...))
+ dbg(false, 1, "dbg.msgh()")
+ else
+ dbg_writeln(
+ COLOR_RED
+ .. "debugger.lua: "
+ .. COLOR_RESET
+ .. "Error did not occur in Lua code. Execution will continue after dbg_pcall()."
+ )
+ end
+
+ return ...
+end
+
+-- Assume stdin/out are TTYs unless we can use LuaJIT's FFI to properly check them.
+local stdin_isatty = true
+local stdout_isatty = true
+
+-- Conditionally enable the LuaJIT FFI.
+local ffi = (jit and require "ffi")
+if ffi then
+ ffi.cdef [[
+ int isatty(int); // Unix
+ int _isatty(int); // Windows
+ void free(void *ptr);
+
+ char *readline(const char *);
+ int add_history(const char *);
+ ]]
+
+ local function get_func_or_nil(sym)
+ local success, func = pcall(function()
+ return ffi.C[sym]
+ end)
+ return success and func or nil
+ end
+
+ local isatty = get_func_or_nil "isatty" or get_func_or_nil "_isatty" or (ffi.load "ucrtbase")["_isatty"]
+ stdin_isatty = isatty(0)
+ stdout_isatty = isatty(1)
+end
+
+-- Conditionally enable color support.
+local color_maybe_supported = (stdout_isatty and os.getenv "TERM" and os.getenv "TERM" ~= "dumb")
+if color_maybe_supported and not os.getenv "DBG_NOCOLOR" then
+ COLOR_GRAY = string.char(27) .. "[90m"
+ COLOR_RED = string.char(27) .. "[91m"
+ COLOR_BLUE = string.char(27) .. "[94m"
+ COLOR_YELLOW = string.char(27) .. "[33m"
+ COLOR_RESET = string.char(27) .. "[0m"
+ GREEN_CARET = string.char(27) .. "[92m => " .. COLOR_RESET
+end
+
+if stdin_isatty and not os.getenv "DBG_NOREADLINE" then
+ pcall(function()
+ local linenoise = require "linenoise"
+
+ -- Load command history from ~/.lua_history
+ local hist_path = os.getenv "HOME" .. "/.lua_history"
+ linenoise.historyload(hist_path)
+ linenoise.historysetmaxlen(50)
+
+ local function autocomplete(env, input, matches)
+ for name, _ in pairs(env) do
+ if name:match("^" .. input .. ".*") then
+ linenoise.addcompletion(matches, name)
+ end
+ end
+ end
+
+ -- Auto-completion for locals and globals
+ linenoise.setcompletion(function(matches, input)
+ -- First, check the locals and upvalues.
+ local env = local_bindings(1, true)
+ autocomplete(env, input, matches)
+
+ -- Then, check the implicit environment.
+ env = getmetatable(env).__index
+ autocomplete(env, input, matches)
+ end)
+
+ dbg.read = function(prompt)
+ local str = linenoise.linenoise(prompt)
+ if str and not str:match "^%s*$" then
+ linenoise.historyadd(str)
+ linenoise.historysave(hist_path)
+ end
+ return str
+ end
+ dbg_writeln(COLOR_YELLOW .. "debugger.lua: " .. COLOR_RESET .. "Linenoise support enabled.")
+ end)
+
+ -- Conditionally enable LuaJIT readline support.
+ pcall(function()
+ if dbg.read == dbg_read and ffi then
+ local readline = ffi.load "readline"
+ dbg.read = function(prompt)
+ local cstr = readline.readline(prompt)
+ if cstr ~= nil then
+ local str = ffi.string(cstr)
+ if string.match(str, "[^%s]+") then
+ readline.add_history(cstr)
+ end
+
+ ffi.C.free(cstr)
+ return str
+ else
+ return nil
+ end
+ end
+ dbg_writeln(COLOR_YELLOW .. "debugger.lua: " .. COLOR_RESET .. "Readline support enabled.")
+ end
+ end)
+end
+
+-- Detect Lua version.
+if jit then -- LuaJIT
+ LUA_JIT_SETLOCAL_WORKAROUND = -1
+ dbg_writeln(COLOR_YELLOW .. "debugger.lua: " .. COLOR_RESET .. "Loaded for " .. jit.version)
+elseif "Lua 5.1" <= _VERSION and _VERSION <= "Lua 5.4" then
+ dbg_writeln(COLOR_YELLOW .. "debugger.lua: " .. COLOR_RESET .. "Loaded for " .. _VERSION)
+else
+ dbg_writeln(COLOR_YELLOW .. "debugger.lua: " .. COLOR_RESET .. "Not tested against " .. _VERSION)
+ dbg_writeln "Please send me feedback!"
+end
+
+return dbg
diff --git a/.config/awesome/quarrel/delegate.lua b/.config/awesome/quarrel/delegate.lua
index 54db786..3ea6d75 100644
--- a/.config/awesome/quarrel/delegate.lua
+++ b/.config/awesome/quarrel/delegate.lua
@@ -1,8 +1,10 @@
+local M = {}
+
--- Capture `fn`'s upvalues and pass to `delegate`
---@param delegate fun(env: table<string, any>, _: ...): ...
---@param fn function
---@return fun(...): ...
-return function(delegate, fn)
+function M.new(delegate, fn)
local upvalues = {}
for i = 1, debug.getinfo(fn, "u").nups do
local name, value = debug.getupvalue(fn, i)
@@ -12,3 +14,9 @@ return function(delegate, fn)
return delegate(upvalues, ...)
end
end
+
+return setmetatable(M, {
+ __call = function(_, ...)
+ return M.new(...)
+ end,
+})
diff --git a/.config/awesome/quarrel/fs.lua b/.config/awesome/quarrel/fs.lua
index 502f189..fa3fc21 100644
--- a/.config/awesome/quarrel/fs.lua
+++ b/.config/awesome/quarrel/fs.lua
@@ -1,24 +1,35 @@
+local gdebug = require "gears.debug"
local GFile = require("lgi").Gio.File
---@class QuarrelFs
-local qfs = {}
+local M = {}
--- Read a file with the specified format (or "a") and close the file
---@param path string
----@param format openmode
+---@param format? openmode
---@return any
-function qfs.read(path, format)
+function M.read(path, format)
local f = assert(io.open(path, "r"))
local c = f:read(format or "a")
f:close()
return c
end
+--- Write a file with the specified format (or "a") and close the file
+---@param path string
+---@param format? openmode
+---@return any
+function M.write(content, path, format)
+ local f = assert(io.open(path, format or "w+"))
+ f:write(content)
+ f:close()
+end
+
--- List files in a directory
---@param path string
---@param absolute boolean?
---@return table
-function qfs.ls_files(path, absolute)
+function M.ls_files(path, absolute)
local files = GFile.new_for_path(path):enumerate_children("standard::*", 0)
local files_filtered = {}
@@ -41,4 +52,4 @@ function qfs.ls_files(path, absolute)
return files_filtered
end
-return qfs
+return M
diff --git a/.config/awesome/quarrel/iconset.lua b/.config/awesome/quarrel/iconset.lua
new file mode 100644
index 0000000..43b7104
--- /dev/null
+++ b/.config/awesome/quarrel/iconset.lua
@@ -0,0 +1,21 @@
+local gstring = require "gears.string"
+
+local M = {}
+
+--- Generate a shim to allow indexing an iconset as a Lua table
+---@param path string
+---@return table
+function M.new(path)
+ return setmetatable({}, {
+ ---@param icon string
+ __index = function(self, icon)
+ return path .. (gstring.endswith(path, "/") and "" or "/") .. icon:gsub("_", "-") .. ".svg"
+ end,
+ })
+end
+
+return setmetatable(M, {
+ __call = function(_, ...)
+ return M.new(...)
+ end,
+})
diff --git a/.config/awesome/quarrel/init.lua b/.config/awesome/quarrel/init.lua
index 96f7f5b..5617c1a 100644
--- a/.config/awesome/quarrel/init.lua
+++ b/.config/awesome/quarrel/init.lua
@@ -9,14 +9,4 @@ function quarrel.debug(message)
n { message = tostring(message) }
end
---- Check if there was a restart
----@return boolean
-function quarrel.is_restart()
- awesome.register_xproperty("is_restart", "boolean")
- local restart_detected = awesome.get_xproperty "is_restart" ~= nil
- awesome.set_xproperty("is_restart", true)
-
- return restart_detected
-end
-
return quarrel
diff --git a/.config/awesome/quarrel/lua-rust.tar.gz b/.config/awesome/quarrel/lua-rust.tar.gz
deleted file mode 100644
index 5ff33fd..0000000
--- a/.config/awesome/quarrel/lua-rust.tar.gz
+++ /dev/null
Binary files differ
diff --git a/.config/awesome/quarrel/markup.lua b/.config/awesome/quarrel/markup.lua
index d206530..f89368c 100644
--- a/.config/awesome/quarrel/markup.lua
+++ b/.config/awesome/quarrel/markup.lua
@@ -1,8 +1,10 @@
+local M = {}
+
--- Apply markup to a file
---@param content string
---@param args { bold: boolean, italic: boolean, fg: string, bg: string }
---@return string
-return function(content, args)
+function M.convert(content, args)
args = args or {}
if args.bold then
content = "<b>" .. content .. "</b>"
@@ -28,3 +30,9 @@ return function(content, args)
return content
end
+
+return setmetatable(M, {
+ __call = function(_, ...)
+ return M.convert(...)
+ end,
+})
diff --git a/.config/awesome/quarrel/math/consts.lua b/.config/awesome/quarrel/math/consts.lua
new file mode 100644
index 0000000..f18a093
--- /dev/null
+++ b/.config/awesome/quarrel/math/consts.lua
@@ -0,0 +1,5 @@
+local C = {}
+
+C.EPSILON = 2 ^ -52
+
+return C
diff --git a/.config/awesome/quarrel/math.lua b/.config/awesome/quarrel/math/init.lua
index b16547b..d437654 100644
--- a/.config/awesome/quarrel/math.lua
+++ b/.config/awesome/quarrel/math/init.lua
@@ -1,11 +1,9 @@
+local M = require "quarrel.math.consts"
local gmath = require "gears.math"
----@class QuarrelMath
-local qmath = {}
-
-- TODO: Finish documenting these functions
-function qmath.step_value(value, steps)
+function M.step_value(value, steps)
if value > steps[#steps - 1][1] then
return steps[#steps - 1][2]
end
@@ -16,16 +14,16 @@ function qmath.step_value(value, steps)
end
end
-function qmath.translate_range(value, in_min, in_max, out_min, out_max)
+function M.translate_range(value, in_min, in_max, out_min, out_max)
return out_min + ((out_max - out_min) / (in_max - in_min)) * (value - in_min)
end
-function qmath.percentage(value, max)
+function M.percentage(value, max)
return gmath.round(value / max * 100)
end
-function qmath.clamp(value, min, max)
+function M.clamp(value, min, max)
return math.max(math.min(value, max), min)
end
-return qmath
+return M
diff --git a/.config/awesome/quarrel/native/Cargo.toml b/.config/awesome/quarrel/native/Cargo.toml
index 8d56c9f..2551507 100644
--- a/.config/awesome/quarrel/native/Cargo.toml
+++ b/.config/awesome/quarrel/native/Cargo.toml
@@ -8,22 +8,21 @@ edition = "2021"
[dependencies]
freedesktop_entry_parser = "1.3.0"
cpc = "1.9.1"
-mlua = { version = "0.8.7", features = [ "module", "lua54", "serialize" ] }
+mlua = { version = "0.10.3", features = [ "module", "lua54", "serialize" ] }
rayon = "1.6.1"
serde = { version = "1.0.152", features = [ "derive" ] }
url = "2.3.1"
-rodio = "0.17.1"
-nix = "0.26.2"
+rodio = "0.20.1"
+nix = { version = "0.29.0", features = [ "ioctl", "socket" ] }
chrono = "0.4.24"
-itertools = "0.10.5"
+itertools = "0.14.0"
html-escape = "0.2.13"
-mpd = { git = "https://github.com/kstep/rust-mpd", features = [ "serde" ], version = "0.1.0" }
-cairo-rs = { git = "https://github.com/gtk-rs/gtk-rs-core.git", version = "0.18.0" }
-gdk-pixbuf = { git = "https://github.com/gtk-rs/gtk-rs-core.git", version = "0.18.0" }
-symphonia = "0.5.3"
-dirs = "5.0.1"
+dirs = "6.0.0"
once_cell = "1.18.0"
-
+ureq = "3.0.9"
+anyhow = { version = "1.0.97", features = ["backtrace"] }
+mdrop = { git = "https://github.com/frahz/mdrop" }
+sysinfo = "0.35.0"
[lib]
crate-type = ["cdylib"]
diff --git a/.config/awesome/quarrel/native/init.lua b/.config/awesome/quarrel/native/init.lua
index e5d5aab..6a823ba 100644
--- a/.config/awesome/quarrel/native/init.lua
+++ b/.config/awesome/quarrel/native/init.lua
@@ -26,16 +26,12 @@ package.cpath = package.cpath .. ";" .. cfg .. "quarrel/native/lib?.so"
---@field decode_html fun(input: string): string
---@field open_file fun(path: string): FileHandle
----@class Mpd
----@field init
-
---@class Net
---@field get_essid fun(): string
---@class QuarrelNative
---@field lenses Lenses
---@field util Util
----@field mpd Mpd
---@field net Net
local qnative = require "qnative"
diff --git a/.config/awesome/quarrel/native/src/http.rs b/.config/awesome/quarrel/native/src/http.rs
new file mode 100644
index 0000000..e995556
--- /dev/null
+++ b/.config/awesome/quarrel/native/src/http.rs
@@ -0,0 +1,22 @@
+use ureq::{Agent, agent};
+use std::{sync::LazyLock, thread};
+use mlua::prelude::*;
+
+static AGENT: LazyLock<Agent> = LazyLock::new(|| agent());
+
+struct Stream(ureq::Body);
+
+pub fn get(_: &Lua, url: String, callback: LuaFunction, err_callback: LuaFunction) -> LuaResult<()> {
+ thread::spawn(|| {
+ match AGENT.get(url).call() {
+ Ok(body) => {
+
+ },
+ Err(err) => {
+ err_callback.call(err.to_string());
+ }
+ }
+ // callback.call::<>()
+ });
+ Ok(())
+}
diff --git a/.config/awesome/quarrel/native/src/lenses/application.rs b/.config/awesome/quarrel/native/src/lenses/application.rs
index 72aba8d..89b7bb4 100644
--- a/.config/awesome/quarrel/native/src/lenses/application.rs
+++ b/.config/awesome/quarrel/native/src/lenses/application.rs
@@ -1,6 +1,5 @@
use std::{
- fs::read_dir,
- path::PathBuf,
+ fs::read_dir, path::PathBuf
};
use freedesktop_entry_parser as fd;
@@ -8,12 +7,54 @@ use mlua::prelude::*;
use rayon::prelude::*;
use url::Url;
-use crate::lenses::entry::{
- entries_to_lua_table,
+use crate::lenses::{
Entry,
+ Lense,
+ Cache
};
-fn parse_entry(entry: &fd::Entry, path: &PathBuf) -> Result<Entry, ()> {
+#[derive(Default)]
+pub struct Application(pub Cache);
+
+impl Lense for Application {
+ const NAME: &str = "Application";
+
+ fn get_cache(&self) -> &Cache {
+ &self.0
+ }
+
+ fn set_cache(&mut self, cache: Cache) {
+ self.0 = cache;
+ }
+
+ fn query(_: &Lua, input: String) -> Result<Vec<Entry>, anyhow::Error> {
+ let applications_dir = "/usr/share/applications";
+ let entries = read_dir(applications_dir)?
+ .map(|result| result.map(|e| e.path()))
+ .collect::<Result<Vec<_>, std::io::Error>>()?;
+
+ let parsed_entries: Vec<Entry> = entries
+ .into_par_iter()
+ .filter(|path| path.extension().is_some_and(|ext| ext == "desktop"))
+ .filter_map(|path| {
+ parse_entry(path).ok().flatten()
+ })
+ .collect();
+
+ Ok(
+ parsed_entries
+ .into_iter()
+ .filter(|entry| entry.message.to_lowercase().contains(&input.to_lowercase()))
+ .collect(),
+ )
+ }
+}
+
+fn parse_entry(path: PathBuf) -> Result<Option<Entry>, ()> {
+ let Ok(entry) = fd::parse_entry(&path) else {
+ return Err(())
+ };
+
let section = entry.section("Desktop Entry");
let name = section.attr("Name").ok_or(())?.to_string();
@@ -38,14 +79,14 @@ fn parse_entry(entry: &fd::Entry, path: &PathBuf) -> Result<Entry, ()> {
}
}
'c' => new_exec.replace_range(index..index + 2, &name),
- 'k' => new_exec.replace_range(index..index + 2, Url::from_file_path(path)?.as_str()),
+ 'k' => new_exec.replace_range(index..index + 2, Url::from_file_path(&path)?.as_str()),
'f' | 'u' | 'v' | 'm' | 'd' | 'n' => new_exec.replace_range(index..index + 2, ""),
_ => continue,
}
}
- Ok(Entry {
+ Ok(Some(Entry {
message: name,
exec: Some((
new_exec,
@@ -55,33 +96,5 @@ fn parse_entry(entry: &fd::Entry, path: &PathBuf) -> Result<Entry, ()> {
.parse()
.map_err(drop)?,
)),
- provider: "Application".to_string(),
- })
-}
-
-pub fn query(lua: &Lua, input: String) -> LuaResult<LuaTable> {
- let applications_dir = "/usr/share/applications";
- let entries = read_dir(applications_dir)?
- .map(|result| result.map(|e| e.path()))
- .collect::<Result<Vec<_>, std::io::Error>>()?;
-
- let parsed_entries: Vec<Entry> = entries
- .into_par_iter()
- .filter(|path| matches!(path.extension(), Some(ext) if ext == "desktop"))
- .filter_map(|path| {
- let Ok(entry) = fd::parse_entry(&path) else {
- return None
- };
-
- parse_entry(&entry, &path).ok()
- })
- .collect();
-
- Ok(entries_to_lua_table(
- parsed_entries
- .into_iter()
- .filter(|entry| entry.message.to_lowercase().contains(&input.to_lowercase()))
- .collect(),
- lua,
- ))
+ }))
}
diff --git a/.config/awesome/quarrel/native/src/lenses/calculator.rs b/.config/awesome/quarrel/native/src/lenses/calculator.rs
index 07f1ee2..640bdeb 100644
--- a/.config/awesome/quarrel/native/src/lenses/calculator.rs
+++ b/.config/awesome/quarrel/native/src/lenses/calculator.rs
@@ -5,25 +5,36 @@ use cpc::{
};
use mlua::prelude::*;
-use crate::lenses::entry::{
- entries_to_lua_table,
+use crate::lenses::{
Entry,
+ Cache,
+ Lense
};
-pub fn query(lua: &Lua, input: String) -> LuaResult<LuaTable> {
- let result = match eval(input.trim(), true, Unit::Celsius, false) {
- Ok(result) => {
- format!("{result}")
- }
- Err(_) => return lua.create_table(),
- };
-
- Ok(entries_to_lua_table(
- vec![Entry {
- message: result,
- exec: None,
- provider: "Calculator".to_string(),
- }],
- lua,
- ))
+pub struct Calculator;
+
+impl Lense for Calculator {
+ const NAME: &str = "Calculator";
+
+ fn get_cache(&self) -> &Cache {
+ &Cache::Stale
+ }
+
+ fn set_cache(&mut self, _: Cache) {}
+
+ fn query(_: &Lua, input: String) -> Result<Vec<Entry>, anyhow::Error> {
+ let result = match eval(input.trim(), true, Unit::Celsius, false) {
+ Ok(result) => {
+ format!("{result}")
+ }
+ Err(err) => { return Err(anyhow::anyhow!(err)); },
+ };
+
+ Ok(vec![Entry {
+ message: result,
+ exec: None,
+ }; 1]
+ )
+ }
}
+
diff --git a/.config/awesome/quarrel/native/src/lenses/entry.rs b/.config/awesome/quarrel/native/src/lenses/entry.rs
deleted file mode 100644
index c7ac09e..0000000
--- a/.config/awesome/quarrel/native/src/lenses/entry.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use mlua::{
- prelude::*,
- LuaSerdeExt,
-};
-use serde::Serialize;
-
-#[derive(Serialize, Clone)]
-pub struct Entry {
- pub message: String,
- pub exec: Option<(String, bool)>,
- pub provider: String,
-}
-
-pub fn entries_to_lua_table(entries: Vec<Entry>, lua: &Lua) -> LuaTable {
- match lua.to_value(&entries).unwrap() {
- LuaValue::Table(t) => t,
- _ => unreachable!(),
- }
-}
diff --git a/.config/awesome/quarrel/native/src/lenses/mod.rs b/.config/awesome/quarrel/native/src/lenses/mod.rs
index d0db6f7..6eb5b58 100644
--- a/.config/awesome/quarrel/native/src/lenses/mod.rs
+++ b/.config/awesome/quarrel/native/src/lenses/mod.rs
@@ -1,3 +1,79 @@
pub mod application;
pub mod calculator;
-pub mod entry;
+
+use mlua::{
+ prelude::*,
+ LuaSerdeExt,
+};
+use serde::{Serialize, Deserialize};
+
+#[derive(Deserialize, Serialize, Clone)]
+pub struct Entry {
+ pub message: String,
+ pub exec: Option<(String, bool)>,
+}
+
+impl IntoLua for Entry {
+ fn into_lua(self, lua: &Lua) -> LuaResult<LuaValue> {
+ return lua.to_value(&self)
+ }
+}
+
+impl FromLua for Entry {
+ fn from_lua(value: LuaValue, lua: &Lua) -> LuaResult<Self> {
+ return lua.from_value(value);
+ }
+}
+
+#[derive(Default)]
+pub enum Cache {
+ Valid(Vec<Entry>),
+ #[default]
+ Stale
+}
+
+pub struct _Lense<T: Lense>(pub T);
+
+pub trait Lense {
+ const NAME: &'static str;
+
+ fn set_cache(&mut self, cache: Cache);
+ fn get_cache(&self) -> &Cache;
+
+ fn query(lua: &Lua, input: String) -> Result<Vec<Entry>, anyhow::Error>;
+}
+
+impl<T: Lense + 'static> LuaUserData for _Lense<T> {
+ fn add_fields<F: LuaUserDataFields<Self>>(fields: &mut F) {
+ // fields.add_field_method_get("cache", |lua, this| {
+ // let cache = this.0.get_cache();
+ // match cache {
+ // Cache::Valid(cache) => Ok(cache.clone().into_lua(lua)?),
+ // Cache::Stale => Ok(LuaNil)
+ // }
+ // });
+
+ // fields.add_field_method_set("cache", |_, this, cache: Vec<Entry>| {
+ // Ok(this.0.set_cache(Cache::Valid(cache)))
+ // });
+
+ fields.add_field_method_get("stale", |_, this| Ok(matches!(this.0.get_cache(), Cache::Stale)));
+
+ fields.add_field("name", T::NAME);
+ }
+
+ fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
+ methods.add_method_mut("query", |lua, this, input: String| {
+ return Ok(match this.0.get_cache() {
+ Cache::Valid(entries) => entries.clone(),
+ Cache::Stale => {
+ let entries = T::query(lua, input).map_err(LuaError::external)?;
+ this.0.set_cache(Cache::Valid(entries.clone()));
+ entries
+ }
+ });
+ });
+
+ methods.add_method_mut("mark_stale", |_, this, _: ()| Ok(this.0.set_cache(Cache::Stale)) );
+ }
+}
diff --git a/.config/awesome/quarrel/native/src/lib.rs b/.config/awesome/quarrel/native/src/lib.rs
index 472313e..4f1780d 100644
--- a/.config/awesome/quarrel/native/src/lib.rs
+++ b/.config/awesome/quarrel/native/src/lib.rs
@@ -1,29 +1,29 @@
mod lenses;
-mod mpd;
mod net;
+// mod http;
mod util;
+mod moondrop;
use mlua::prelude::*;
+use lenses::{ _Lense };
#[mlua::lua_module]
fn qnative(lua: &Lua) -> LuaResult<LuaTable> {
let lenses = lua.create_table()?;
- lenses.set("1", lua.create_function(lenses::calculator::query)?)?;
- lenses.set("2", lua.create_function(lenses::application::query)?)?;
+ lenses.push(_Lense(lenses::calculator::Calculator))?;
+ lenses.push(_Lense(lenses::application::Application::default()))?;
+ // lenses.set("1", lua.create_function(lenses::calculator::Application::query)?)?;
+ // lenses.set("2", lua.create_function(lenses::application::Application::query)?)?;
let util = lua.create_table()?;
util.set("decode_html", lua.create_function(util::decode_html)?)?;
util.set("open_file", lua.create_function(util::FileHandle::new)?)?;
- let mpd = lua.create_table()?;
- mpd.set("init", lua.create_function(mpd::init)?)?;
-
let net = lua.create_table()?;
net.set("get_essid", lua.create_function(net::get_first_essid)?)?;
let exports = lua.create_table()?;
exports.set("lenses", lenses)?;
- exports.set("mpd", mpd)?;
exports.set("net", net)?;
exports.set("util", util)?;
diff --git a/.config/awesome/quarrel/native/src/moondrop.rs b/.config/awesome/quarrel/native/src/moondrop.rs
new file mode 100644
index 0000000..d416ef6
--- /dev/null
+++ b/.config/awesome/quarrel/native/src/moondrop.rs
@@ -0,0 +1,3 @@
+use mdrop::Moondrop;
+
+
diff --git a/.config/awesome/quarrel/native/src/mpd.rs b/.config/awesome/quarrel/native/src/mpd.rs
deleted file mode 100644
index 08c9e42..0000000
--- a/.config/awesome/quarrel/native/src/mpd.rs
+++ /dev/null
@@ -1,142 +0,0 @@
-use std::{
- ffi::c_void,
- fs::File,
- net::TcpStream,
- path::PathBuf,
- sync::mpsc::channel,
-};
-
-use dirs::home_dir;
-use gdk_pixbuf::{
- ffi::GdkPixbuf,
- glib::translate::IntoGlibPtr,
- traits::PixbufLoaderExt,
- Pixbuf,
- PixbufLoader,
-};
-use mlua::{
- prelude::*,
- LuaSerdeExt,
-};
-use mpd::Client;
-use once_cell::sync::Lazy;
-use symphonia::{
- core::{
- formats::FormatOptions,
- io::{
- MediaSourceStream,
- MediaSourceStreamOptions,
- },
- meta::MetadataOptions,
- probe::Hint,
- },
- default::get_probe,
-};
-
-static MPD_MUSIC_PATH: Lazy<PathBuf> = Lazy::new(|| {
- [
- home_dir().expect("home directory should be set"),
- "Music".into(),
- ]
- .iter()
- .collect()
-});
-
-static COVER_FORMATS: [&str; 2] = ["png", "jpg"];
-
-pub struct Connection(Client<TcpStream>);
-
-impl LuaUserData for Connection {
- fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
- methods.add_method_mut("status", |lua, this: &mut Connection, (): ()| {
- lua.to_value(&this.0.status().map_err(LuaError::external)?)
- });
-
- methods.add_method_mut("song", |lua, this: &mut Connection, (): ()| {
- Ok(
- if let Some(song) = this.0.currentsong().map_err(LuaError::external)? {
- lua.to_value(&song)?
- } else {
- LuaNil
- },
- )
- });
-
- methods.add_method(
- "get_cover_pixbuf",
- |_, _: &Connection, file: String| {
- let song_path = MPD_MUSIC_PATH.join(file);
- let mut has_external_cover = false;
- let mut cover_path = PathBuf::new();
-
- for format in COVER_FORMATS {
- let cover = song_path
- .parent()
- .unwrap()
- .to_owned()
- .join(format!("cover.{}", format));
- if cover.exists() {
- has_external_cover = cover.exists();
- cover_path = cover;
- break;
- }
- }
-
- let mss = MediaSourceStream::new(
- Box::new(File::open(song_path)?),
- MediaSourceStreamOptions::default(),
- );
-
- let mut probed = get_probe()
- .format(
- &Hint::default(),
- mss,
- &FormatOptions::default(),
- &MetadataOptions::default(),
- )
- .map_err(LuaError::external)?;
-
- let visuals;
-
- if let Some(metadata) = probed.format.metadata().skip_to_latest() {
- visuals = metadata.visuals();
- if visuals.is_empty() && has_external_cover {
- let pixbuf = Pixbuf::from_file(cover_path).map_err(LuaError::external)?;
-
- return Ok((
- Some(LuaLightUserData(unsafe {
- <Pixbuf as IntoGlibPtr<*mut GdkPixbuf>>::into_glib_ptr(pixbuf)
- .cast::<c_void>()
- })),
- Some(true),
- ));
- }
-
- let loader = PixbufLoader::new();
- loader
- .write(visuals.first().unwrap().data.as_ref())
- .map_err(LuaError::external)?;
- loader.close().map_err(LuaError::external)?;
-
- return Ok((
- Some(LuaLightUserData(unsafe {
- <Pixbuf as IntoGlibPtr<*mut GdkPixbuf>>::into_glib_ptr(
- loader.pixbuf().expect("Pixbuf should be initialized"),
- )
- .cast::<c_void>()
- })),
- Some(false),
- ));
- }
-
- Ok((None, None))
- },
- );
- }
-}
-
-pub fn init(_: &Lua, _: ()) -> LuaResult<Connection> {
- Ok(Connection(
- Client::connect("localhost:6600").map_err(LuaError::external)?,
- ))
-}
diff --git a/.config/awesome/quarrel/native/src/net/mod.rs b/.config/awesome/quarrel/native/src/net/mod.rs
index 96c853e..dbd87b9 100644
--- a/.config/awesome/quarrel/native/src/net/mod.rs
+++ b/.config/awesome/quarrel/native/src/net/mod.rs
@@ -9,7 +9,7 @@ use std::{
c_void,
},
mem::size_of,
- os::fd::RawFd,
+ os::fd::{IntoRawFd, RawFd},
};
use mlua::prelude::*;
@@ -108,7 +108,7 @@ fn get_first_socket() -> LuaResult<RawFd> {
for family in families {
if let Ok(socket) = open_socket(family, SockType::Datagram, SockFlag::empty(), None) {
- return Ok(socket);
+ return Ok(socket.into_raw_fd());
}
}
diff --git a/.config/awesome/quarrel/native/src/util.rs b/.config/awesome/quarrel/native/src/util.rs
index 85a2574..41096d8 100644
--- a/.config/awesome/quarrel/native/src/util.rs
+++ b/.config/awesome/quarrel/native/src/util.rs
@@ -1,7 +1,6 @@
use std::{
cell::RefCell,
fs::File,
- fs::OpenOptions,
io::{
Read,
Seek,
@@ -49,7 +48,7 @@ enum StringOrNumber {
pub struct FileHandle(Rc<RefCell<File>>);
impl LuaUserData for FileHandle {
- fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
+ fn add_methods<'lua, M: LuaUserDataMethods<Self>>(methods: &mut M) {
methods.add_method("read", |lua, this: &FileHandle, mode: String| {
let content = this.read()?;
Ok(lua.to_value(&match ReadMode::from_str(&mode).unwrap() {
diff --git a/.config/awesome/quarrel/persistent.lua b/.config/awesome/quarrel/persistent.lua
new file mode 100644
index 0000000..2b15c89
--- /dev/null
+++ b/.config/awesome/quarrel/persistent.lua
@@ -0,0 +1,62 @@
+local gdebug = require "gears.debug"
+local qfs = require "quarrel.fs"
+local qjson = require "quarrel.json"
+
+awesome.register_xproperty("is_restart", "boolean")
+
+---@alias QPersistentValue string|number|table|nil|boolean
+
+---@class QuarrelPersistent
+local qpersistent = {}
+
+---@type table<string, QPersistentValue>
+local inner = {}
+
+--- Store a key-value pair in a persistent store
+---@param key string
+---@param value QPersistentValue
+function qpersistent.store(key, value)
+ inner[key] = value
+end
+
+--- Get a key-value pair in a persistent store
+---@param key string
+---@return QPersistentValue
+function qpersistent.get(key)
+ return inner[key]
+end
+
+--- Check if there was a restart
+---@return boolean
+function qpersistent.is_restart()
+ return awesome.get_xproperty "is_restart" ~= nil
+end
+
+do
+ local ok, content = pcall(qfs.read, "/tmp/qpersistent-store")
+
+ if not ok and not qpersistent.is_restart() then
+ gdebug.print_error "failed to read persistent store"
+ inner = {}
+ else
+ inner = qjson.decode(content)
+ end
+end
+
+awesome.connect_signal("exit", function(restart)
+ if not restart then
+ return
+ end -- we probably errored
+
+ local ok = pcall(qfs.write, qjson.encode(inner), "/tmp/qpersistent-store")
+ if not ok then
+ gdebug.print_error "failed to open persistent store"
+ return
+ end
+
+ if not qpersistent.is_restart() then
+ awesome.set_xproperty("is_restart", true)
+ end
+end)
+
+return qpersistent
diff --git a/.config/awesome/quarrel/store.lua b/.config/awesome/quarrel/store.lua
deleted file mode 100644
index 9422c21..0000000
--- a/.config/awesome/quarrel/store.lua
+++ /dev/null
@@ -1,4 +0,0 @@
----@type table
-local qstore = {}
-
-return qstore
diff --git a/.config/awesome/quarrel/ui.lua b/.config/awesome/quarrel/ui.lua
deleted file mode 100644
index 1db4a70..0000000
--- a/.config/awesome/quarrel/ui.lua
+++ /dev/null
@@ -1,145 +0,0 @@
-local awful = require "awful"
-local gtable = require "gears.table"
-local qbind = require "quarrel.bind"
-local qvars = require "quarrel.vars"
-local rtimed = require "lib.rubato.timed"
-local wibox = require "wibox"
-
----@class QuarrelUi
-local qui = {}
-
---- Return qvars.text_font with size scaled by factor
----@param factor number
----@return string
----@see QuarrelVars.text_font
-function qui.font(factor)
- return qvars.text_font .. " " .. qvars.font_size * (factor or 1)
-end
-
---- Inject background widget styling into target
----@param target table
----@return table
-function qui.styled(target)
- return gtable.crush({
- bg = qvars.colors.bg,
- border_color = qvars.colors.bright.black,
- border_width = qvars.border_width,
- shape = qvars.shape,
- }, target)
-end
-
---- Generate a styled popup
----@param target table
----@return table
-function qui.popup(target)
- target.widget = {
- widget = wibox.container.margin,
- margins = qvars.big_padding,
- target.widget,
- }
-
- return awful.popup(qui.styled(target))
-end
-
---- Generate svg recolor string
----@param color string
----@return string
-function qui.recolor(color)
- return "svg{fill:" .. color .. "}"
-end
-
---- Generate icon widget
----@param args table
----@return table
-function qui.icon(args)
- return gtable.crush({
- widget = wibox.widget.imagebox,
- image = args.icon,
- forced_width = qvars.char_height,
- forced_height = qvars.char_height,
- stylesheet = qui.recolor(args.color or qvars.colors.fg),
- }, args.widget or {})
-end
-
---- Generate button widget
----@param args table
----@return table
-function qui.button(args)
- args.press = args.press or function(_) end
- local widget = wibox.widget(gtable.crush({
- widget = wibox.widget.imagebox,
- image = args.image,
- forced_height = qvars.char_height,
- forced_width = qvars.char_height,
- stylesheet = qui.recolor(qvars.colors.fg),
- press = args.press,
- }, args.widget or {}))
-
- widget.buttons = {
- qbind:new {
- triggers = qvars.btns.left,
- press = function()
- widget:press()
- end,
- hidden = true,
- },
- }
-
- return widget
-end
-
---- Generate toggle widget
----@param args table
----@return table
-function qui.toggle(args)
- args.press = args.press or function(_) end
- local widget = qui.button {
- widget = gtable.crush({
- toggled = false,
- silent_press = function(self, state)
- if state then
- self.toggled = state
- else
- self.toggled = not self.toggled
- end
-
- if self.toggled then
- self.image = args.on
- else
- self.image = args.off
- end
- end,
- }, args.widget or {}),
- image = args.off,
- press = function(self)
- if not args.manual then
- self:silent_press()
- end
- args.press(self)
- end,
- }
-
- return widget
-end
-
----@param widget wibox.widget.base
----@param cursor string
-function qui.hoverable(widget, cursor)
- local hovering = false
-
- widget:connect_signal("mouse::enter", function()
- local w = mouse.current_wibox
- if w then
- w.cursor = cursor
- end
- end)
-
- widget:connect_signal("mouse::leave", function()
- local w = mouse.current_wibox
- if w then
- w.cursor = "left_ptr"
- end
- end)
-end
-
-return qui
diff --git a/.config/awesome/quarrel/ui/consts.lua b/.config/awesome/quarrel/ui/consts.lua
new file mode 100644
index 0000000..e6e12d0
--- /dev/null
+++ b/.config/awesome/quarrel/ui/consts.lua
@@ -0,0 +1,24 @@
+local awful = require "awful"
+local qconst = require "quarrel.const"
+local wibox = require "wibox"
+
+local C = {}
+
+C.BORDER_WIDTH = 1
+C.BORDER_RADIUS = 4
+C.PADDING = 4
+C.BIG_PADDING = 8
+C.TEXT_FONT = "Iosevka Comfy Regular"
+C.FONT_SIZE = 9
+
+local char_width, char_height = wibox
+ .widget({
+ widget = wibox.widget.textbox,
+ text = "a",
+ })
+ :get_preferred_size_at_dpi(awful.screen.focused().dpi)
+
+C.CHAR_HEIGHT = char_height
+C.CHAR_WIDTH = char_width
+
+return C
diff --git a/.config/awesome/quarrel/ui/init.lua b/.config/awesome/quarrel/ui/init.lua
new file mode 100644
index 0000000..5acaa51
--- /dev/null
+++ b/.config/awesome/quarrel/ui/init.lua
@@ -0,0 +1,167 @@
+local M = require "quarrel.ui.consts"
+local awful = require "awful"
+local gshape = require "gears.shape"
+local gtable = require "gears.table"
+local qbind = require "quarrel.bind"
+local qcolor = require "quarrel.color"
+local wibox = require "wibox"
+
+--- Clip Cairo context
+---@param cr cairo_surface Cairo surface
+---@param w integer Widget width
+---@param h integer Widget height
+---@return nil
+function M.shape(cr, w, h)
+ -- gears.shape.rounded_rect(cr, w, h, dpi(4))
+ gshape.rounded_rect(cr, w, h, M.BORDER_RADIUS)
+ -- gshape.rectangle(cr, w, h)
+end
+
+--- Returns qvars.text_font with size scaled by factor
+---@param factor number?
+---@return string
+function M.font(factor)
+ return M.TEXT_FONT .. " " .. M.FONT_SIZE * (factor or 1)
+end
+
+--- Injects background widget styling into target
+---@param target table
+---@return table
+function M.styled(target)
+ return gtable.crush({
+ bg = qcolor.palette.bg(),
+ border_color = qcolor.palette.border(),
+ border_width = M.BORDER_WIDTH,
+ shape = M.shape,
+ }, target)
+end
+
+--- Generates a styled popup
+---@param target table
+---@return table
+function M.popup(target, debug)
+ target.widget = {
+ widget = wibox.container.margin,
+ margins = M.BIG_PADDING,
+ target.widget,
+ }
+
+ return awful.popup(M.styled(target))
+end
+
+--- Generates svg recolor string
+---@param color string
+---@return string
+function M.recolor(color)
+ return "svg{fill:" .. color .. "}"
+end
+
+--- Generates icon widget
+---@param args table
+---@return table
+function M.icon(args)
+ return wibox.widget(gtable.crush({
+ widget = wibox.widget.imagebox,
+ image = args.icon,
+ forced_width = (not args.unsized) and M.CHAR_HEIGHT,
+ forced_height = (not args.unsized) and M.CHAR_HEIGHT,
+ stylesheet = M.recolor(args.color or qcolor.palette.fg()),
+ }, args.widget or {}))
+end
+
+--- Generates button widget
+---@param args table
+---@return table
+function M.button(args)
+ args.press = args.press or function(_) end
+ local widget = wibox.widget(gtable.crush({
+ widget = wibox.widget.imagebox,
+ image = args.icon,
+ forced_height = M.CHAR_HEIGHT,
+ forced_width = M.CHAR_HEIGHT,
+ stylesheet = M.recolor(qcolor.palette.fg()),
+ press = args.press,
+ }, args.widget or {}))
+
+ widget.buttons = {
+ qbind {
+ triggers = qbind.btns.left,
+ press = function()
+ widget:press()
+ end,
+ hidden = true,
+ },
+ }
+
+ return widget
+end
+
+--- Generates toggle widget
+---@param args table
+---@return table
+function M.toggle(args)
+ args.press = args.press or function(_) end
+ local widget = M.button {
+ widget = gtable.crush({
+ toggled = false,
+ silent_press = function(self, state)
+ if state then
+ self.toggled = state
+ else
+ self.toggled = not self.toggled
+ end
+
+ if self.toggled then
+ self.image = args.on
+ else
+ self.image = args.off
+ end
+ end,
+ }, args.widget or {}),
+ icon = args.off,
+ press = function(self)
+ if not args.manual then
+ self:silent_press()
+ end
+ args.press(self)
+ end,
+ }
+
+ return widget
+end
+
+---@param widget wibox.widget.base
+---@param cursor string
+---@return wibox.widget.base
+function M.hoverable(widget, cursor)
+ local last_wibox
+
+ widget:connect_signal("mouse::enter", function()
+ local w = mouse.current_wibox
+ last_wibox = w
+ if w then
+ w.cursor = cursor
+ end
+
+ if type(widget.hover) == "function" then -- on enter bind
+ widget.hover()
+ elseif type(widget.hover) == "table" and widget.hover.enter then -- { enter: fun(), leave: fun() }
+ widget.hover.enter()
+ end
+ end)
+
+ widget:connect_signal("mouse::leave", function()
+ local w = last_wibox or mouse.current_wibox
+ if w then
+ w.cursor = "left_ptr"
+ end
+
+ if type(widget.hover) == "table" and widget.hover.leave then -- { enter: fun(), leave: fun() }
+ widget.hover.leave()
+ end
+ end)
+
+ return widget
+end
+
+return M
diff --git a/.config/awesome/quarrel/vars.lua b/.config/awesome/quarrel/vars.lua
index 1983343..5c3b178 100644
--- a/.config/awesome/quarrel/vars.lua
+++ b/.config/awesome/quarrel/vars.lua
@@ -1,37 +1,38 @@
local awful = require "awful"
local btns = awful.button.names
-local gears = require "gears"
-local xresources = require "beautiful.xresources"
-local x_col = xresources.get_current_theme()
-local dpi = xresources.apply_dpi
+local gshape = require "gears.shape"
local wibox = require "wibox"
+-- local qconts = require "quarrel.consts"
+local qbezier = require "quarrel.animation.bezier"
----@class QuarrelVars
local qvars = {}
-qvars.anim_duration = 0.20
-qvars.anim_intro = qvars.anim_duration / 4
-
-qvars.notif_timeout = 3
+-- ---@param protected (fun(): table) | table
+-- function qconsts.protect(protected)
+-- ---@type table
+-- local tbl
+-- if type(protected) == "table" then
+-- tbl = protected
+-- elseif type(protected) == "function" then
+-- tbl = protected()
+-- else
+-- error("expected a table or a function that returns a table, got " .. type(protected), 2)
+-- end
+-- return setmetatable({}, {
+-- __index = tbl,
+-- __newindex = function(t, k, v)
+-- error("attempted to change constant " .. tostring(k) .. " to " .. tostring(v), 2)
+-- end
+-- })
+-- end
+
+-- qui.BORDER_WIDTH = 1
+-- qui.BORDER_RADIUS = 4
+
+-- qui.PADDING = 4
+-- qui.BIG_PADDING = 8
---- Clip Cairo context
----@param cr cairo_surface Cairo surface
----@param w integer Widget width
----@param h integer Widget height
----@return nil
-function qvars.shape(cr, w, h)
- gears.shape.rounded_rect(cr, w, h, dpi(4))
-end
-
-qvars.border_width = dpi(1.5)
-
-qvars.padding = dpi(4)
-qvars.big_padding = dpi(8)
-
--- qvars.text_font = "Fira Code Nerd Font Mono Medium"
--- qvars.text_font = "Iosevka Comfy SemiBold"
qvars.text_font = "Iosevka Comfy Regular"
--- qvars.font_size = 8
qvars.font_size = 9
qvars.font = qvars.text_font .. " " .. qvars.font_size
@@ -42,63 +43,152 @@ local char_width, char_height = wibox
})
:get_preferred_size_at_dpi(awful.screen.focused().dpi)
-qvars.char_height = char_height
-qvars.char_width = char_width
-
-qvars.bar_size = dpi(24) + qvars.big_padding * 2
-qvars.element_size = dpi(12)
-qvars.expanded_bar_size = qvars.big_padding + (qvars.big_padding * 2 + qvars.element_size * 4) * 3 + qvars.padding * 2
-
-qvars.colors = {
- fg = x_col.foreground,
- bg = x_col.background,
-
- black = x_col.color0,
- red = x_col.color1,
- green = x_col.color2,
- yellow = x_col.color3,
- blue = x_col.color4,
- pink = x_col.color5,
- cyan = x_col.color6,
- white = x_col.color7,
-
- bright = {
- black = x_col.color8,
- red = x_col.color9,
- green = x_col.color10,
- yellow = x_col.color11,
- blue = x_col.color12,
- pink = x_col.color13,
- cyan = x_col.color14,
- white = x_col.color15,
- },
-
- dim = {
- fg = "#77828c",
- bg = "#161b22",
- },
-
- transparent = "#00000000",
-}
+-- qui.CHAR_HEIGHT = char_height
+-- qui.CHAR_WIDTH = char_width
+
+--- Clip Cairo context
+---@param cr cairo_surface Cairo surface
+---@param w integer Widget width
+---@param h integer Widget height
+---@return nil
+-- function qui.shape(cr, w, h)
+-- -- gears.shape.rounded_rect(cr, w, h, dpi(4))
+-- gshape.rounded_rect(cr, w, h, qui.BORDER_RADIUS)
+-- end
+
+qvars.anim_duration = 0.30
+qvars.anim_intro = qvars.anim_duration / 4
+
+local easing_bezier = qbezier(0.2, 0, 0, 1)
+function qvars.easing(t, b, c, d)
+ local epsilon = 1000 / d
+ return c * easing_bezier:solve(t / d, epsilon) + b
+end
+
+qvars.notif_timeout = 3
+
+-- qvars.bar_size = dpi(24) + qui.BIG_PADDING * 2
+-- qvars.element_size = dpi(12)
+-- qvars.bar_size = 24 + qui.BIG_PADDING * 2
+-- qvars.element_size = 12
+-- qvars.expanded_bar_size = qui.BIG_PADDING + (qui.BIG_PADDING * 2 + qvars.element_size * 4) * 4 + qui.PADDING * 2
+
+-- local function palette(colors)
+-- return setmetatable(colors, {
+-- __call = function(self)
+-- return self[1]
+-- end,
+-- })
+-- end
+
+-- local colors = require "quarrel.color"
+-- qcolor.palette = colors.palette
+-- qcolor.palette = {
+-- fg = palette {
+-- low = "#a8afb7",
+-- "#c8ced7",
+-- high = "#e8eff8",
+-- },
+--
+-- bg = palette {
+-- lowest = "#070c11",
+-- low = "#0c1116",
+-- "#12161c",
+-- high = "#171c22",
+-- highest = "#1d2228",
+-- },
+--
+-- border = palette {
+-- "#323b46",
+-- variant = "#262f39",
+-- },
+--
+-- red = palette {
+-- "#ff928a",
+-- bright = "#ffb2a9",
+-- },
+-- orange = palette {
+-- "#ff9f6f",
+-- bright = "#ffc08e",
+-- },
+-- yellow = palette {
+-- "#ecb256",
+-- bright = "#ffd278",
+-- },
+-- green = palette {
+-- "#8bd294",
+-- bright = "#abf3b3",
+-- },
+-- cyan = palette {
+-- "#6dd3c2",
+-- bright = "#8ef4e2",
+-- },
+-- blue = palette {
+-- "#8bc3fc",
+-- bright = "#abe4ff",
+-- },
+-- purple = palette {
+-- "#c4b1f6",
+-- bright = "#e4d1ff",
+-- },
+-- pink = palette {
+-- "#e5acb4",
+-- bright = "#ffccd4",
+-- },
+--
+-- transparent = "#00000000",
+-- }
+
+-- qcolor.palette = {
+-- fg = x_col.foreground,
+-- bg = x_col.background,
+--
+-- black = x_col.color0,
+-- red = x_col.color1,
+-- green = x_col.color2,
+-- yellow = x_col.color3,
+-- blue = x_col.color4,
+-- pink = x_col.color5,
+-- cyan = x_col.color6,
+-- white = x_col.color7,
+--
+-- bright = {
+-- black = x_col.color8,
+-- red = x_col.color9,
+-- green = x_col.color10,
+-- yellow = x_col.color11,
+-- blue = x_col.color12,
+-- pink = x_col.color13,
+-- cyan = x_col.color14,
+-- white = x_col.color15,
+-- },
+--
+-- dim = {
+-- fg = "#77828c",
+-- bg = "#161b22",
+-- },
+--
+-- transparent = "#00000000",
+-- }
-- taken from https://github.com/bew/dotfiles/blob/ab9bb1935783f7a31ef777b1d7e26d53f35df864/gui/wezterm/cfg_utils.lua
-qvars.mods = setmetatable({ _SHORT_MAP = { C = "Control", S = "Shift", A = "Mod1", M = "Mod4" } }, {
- __index = function(self, key)
- local resolved_mods = {}
- for i = 1, #key do
- resolved_mods[i] = self._SHORT_MAP[key:sub(i, i)]
- end
- return resolved_mods
- end,
-})
-
----@enum buttons
-qvars.btns = {
- left = btns.LEFT,
- right = btns.RIGHT,
- middle = btns.MIDDLE,
- up = btns.SCROLL_UP,
- down = btns.SCROLL_DOWN,
-}
+-- qbind.mods = setmetatable({ _SHORT_MAP = { C = "Control", S = "Shift", A = "Mod1", M = "Mod4" } }, {
+-- __index = function(self, key)
+-- local resolved_mods = {}
+-- for i = 1, #key do
+-- resolved_mods[i] = self._SHORT_MAP[key:sub(i, i)]
+-- end
+-- return resolved_mods
+-- end,
+-- })
+--
+-- ---@enum buttons
+-- qbind.btns = {
+-- left = btns.LEFT,
+-- right = btns.RIGHT,
+-- middle = btns.MIDDLE,
+-- up = btns.SCROLL_UP,
+-- down = btns.SCROLL_DOWN,
+-- }
return qvars
diff --git a/.config/awesome/rc.lua b/.config/awesome/rc.lua
index be5b3f1..32147be 100644
--- a/.config/awesome/rc.lua
+++ b/.config/awesome/rc.lua
@@ -18,7 +18,6 @@ require "services"
require "misc"
require "ui"
-
-- require "curious"
-- local gdebug = require "gears.debug"
--
diff --git a/.config/awesome/services/backlight.lua b/.config/awesome/services/backlight.lua
index 02ed808..bb9bedc 100644
--- a/.config/awesome/services/backlight.lua
+++ b/.config/awesome/services/backlight.lua
@@ -1,10 +1,8 @@
local backlight = require("lib.lit").backlight.new("amdgpu_bl1", 5)
local gobject = require "gears.object"
-local gtimer = require "gears.timer"
-local naughty = require "naughty"
local phosphor = require "assets.phosphor"
+local qcolor = require "quarrel.color"
local qmath = require "quarrel.math"
-local qvars = require "quarrel.vars"
local backlight_wrapper = gobject {
class = {
@@ -27,7 +25,7 @@ backlight:connect_signal("brightness", function(_, brightness)
{ 255 },
})
- backlight_wrapper:emit_signal("icon", phosphor[icon_data .. "_fill"], qvars.colors.fg)
+ backlight_wrapper:emit_signal("icon", phosphor[icon_data .. "_fill"], qcolor.palette.fg())
backlight_wrapper:emit_signal("value", brightness)
end)
diff --git a/.config/awesome/services/battery.lua b/.config/awesome/services/battery.lua
index 98bacc9..d10714e 100644
--- a/.config/awesome/services/battery.lua
+++ b/.config/awesome/services/battery.lua
@@ -2,8 +2,8 @@ local UPower = require("lgi").UPowerGlib
local gobject = require "gears.object"
local gtimer = require "gears.timer"
local phosphor = require "assets.phosphor"
+local qcolor = require "quarrel.color"
local qmath = require "quarrel.math"
-local qvars = require "quarrel.vars"
local upower = assert(UPower.Client.new_full())
local inner
@@ -51,7 +51,11 @@ local function icon_handler()
{ 100 },
})
- battery:emit_signal("icon", phosphor["battery_vertical_" .. icon_data[1] .. "_fill"], qvars.colors[icon_data[2]])
+ battery:emit_signal(
+ "icon",
+ phosphor["battery_vertical_" .. icon_data[1] .. "_fill"],
+ qcolor.palette[icon_data[2]]()
+ )
end
local function level_handler()
diff --git a/.config/awesome/services/common.lua b/.config/awesome/services/common.lua
index ad1f998..9657c8a 100644
--- a/.config/awesome/services/common.lua
+++ b/.config/awesome/services/common.lua
@@ -1,12 +1,10 @@
local gfs = require "gears.filesystem"
local gtimer = require "gears.timer"
local phosphor = require "assets.phosphor"
+local qcolor = require "quarrel.color"
local qjson = require "quarrel.json"
local qmath = require "quarrel.math"
local qnative = require "quarrel.native"
-local qstore = require "quarrel.store"
-local qvars = require "quarrel.vars"
-local UPower = require("lgi").UPowerGlib
--- Register a service
---@param name string
@@ -39,9 +37,9 @@ if not gfs.file_readable "/tmp/wp_audio_status" then
assert(io.open("/tmp/wp_audio_status", "w")):write("{}"):close()
end
-qstore.audio_file = qnative.util.open_file "/tmp/wp_audio_status"
-qstore.brightness_file = qnative.util.open_file "/sys/class/backlight/amdgpu_bl1/actual_brightness"
-qstore.wifi_file = qnative.util.open_file "/proc/net/wireless"
+local audio_file = qnative.util.open_file "/tmp/wp_audio_status"
+-- qstore.brightness_file = qnative.util.open_file "/sys/class/backlight/amdgpu_bl1/actual_brightness"
+local wifi_file = qnative.util.open_file "/proc/net/wireless"
-- follows the format `service = { provider, icon_provider }`
---@class Service
@@ -53,7 +51,7 @@ local services = {
audio = {
-- volume, muted
function()
- local audio_status = qjson.decode(read(qstore.audio_file))
+ local audio_status = qjson.decode(read(audio_file))
local default_sink = audio_status["G435 Wireless Gaming Headset Analog Stereo"]
if not default_sink then
@@ -64,7 +62,7 @@ local services = {
end,
function(volume, muted)
if muted or not volume then
- return phosphor.speaker_simple_x_fill, qvars.colors.red
+ return phosphor.speaker_simple_x_fill, qcolor.palette.red()
end
local icon_data = qmath.step_value(volume, {
@@ -75,32 +73,32 @@ local services = {
{ 100 },
})
- return phosphor["speaker_simple_" .. icon_data .. "_fill"], qvars.colors.fg
- end,
- },
- brightness = {
- -- brightness
- function()
- return read(qstore.brightness_file, "n")
- end,
- function(brightness)
- local icon_data = qmath.step_value(brightness, {
- { 0, "cloud_moon" },
- { 51, "moon" },
- { 102, "sun_horizon" },
- { 153, "sun_dim" },
- { 204, "sun" },
- { 255 },
- })
-
- return phosphor[icon_data .. "_fill"], qvars.colors.fg
+ return phosphor["speaker_simple_" .. icon_data .. "_fill"], qcolor.palette.fg()
end,
},
+ -- brightness = {
+ -- -- brightness
+ -- function()
+ -- return read(qstore.brightness_file, "n")
+ -- end,
+ -- function(brightness)
+ -- local icon_data = qmath.step_value(brightness, {
+ -- { 0, "cloud_moon" },
+ -- { 51, "moon" },
+ -- { 102, "sun_horizon" },
+ -- { 153, "sun_dim" },
+ -- { 204, "sun" },
+ -- { 255 },
+ -- })
+ --
+ -- return phosphor[icon_data .. "_fill"], qcolor.palette.fg()
+ -- end,
+ -- },
wifi = {
-- essid, strength, connected
function()
- local lines = qstore.wifi_file:lines()
- qstore.wifi_file:rewind()
+ local lines = wifi_file:lines()
+ wifi_file:rewind()
if not lines[3] then
return nil, 0, false
@@ -112,7 +110,7 @@ local services = {
end,
function(_, strength, connected)
if not connected then
- return phosphor.wifi_x_fill, qvars.colors.red
+ return phosphor.wifi_x_fill, qcolor.palette.red()
end
local icon_data = qmath.step_value(strength, {
@@ -123,7 +121,7 @@ local services = {
{ 100 },
})
- return phosphor["wifi_" .. icon_data[1] .. "_fill"], qvars.colors[icon_data[2]]
+ return phosphor["wifi_" .. icon_data[1] .. "_fill"], qcolor.palette[icon_data[2]]()
end,
},
}
diff --git a/.config/awesome/services/mpris/init.lua b/.config/awesome/services/mpris/init.lua
new file mode 100644
index 0000000..3659a3a
--- /dev/null
+++ b/.config/awesome/services/mpris/init.lua
@@ -0,0 +1,100 @@
+local gobject = require "gears.object"
+local playerctl = require "services.mpris.playerctl"
+local qpersistent = require "quarrel.persistent"
+
+---@class ServiceMpris : gears.object
+---@field inner Playerctl
+---@field index number the index of the currently active player
+local M = {}
+
+---@param self ServiceMpris
+---@param player Playerctl.data
+local function update_player(self, player)
+ self:emit_signal("player::metadata", player)
+ self:emit_signal("player::position", player)
+ -- handle_position(nil, player)
+end
+
+---@param diff_player Playerctl.data
+local function recalculate_active_player(diff_player, vanished)
+ if type(diff_player) ~= "table" then
+ return
+ end
+ -- if #layout.children == 0 then
+ -- M.active_player_index = 1
+ -- update_player()
+ -- return
+ -- end
+
+ local active_player = players[M.active_player_index]
+ if not active_player then -- we're recovering from a state with no players
+ update_player(diff_player)
+ return
+ end
+
+ if diff_player.instance == active_player.instance and vanished then -- active player vanished; fall back to previous player
+ M.previous_player()
+ else -- non-active player appeared/vanished; try to find active player
+ for i, p in ipairs(playerctl:list()) do
+ if p.instance == active_player.instance then
+ M.active_player_index = i
+ update_player(p)
+ return
+ end
+ end
+
+ gdebug.print_warning(
+ "failed to find active player:\n " .. gdebug.dump_return(active_player, nil, 2):gsub("\n", "\n ")
+ )
+ M.active_player_index = 1
+ update_player(playerctl:list()[M.active_player_index])
+ end
+end
+
+function M:next_player()
+ local players = self.inner:list()
+
+ if #players == 0 then
+ return
+ elseif self.index + 1 > #players then
+ self.index = 1
+ else
+ self.index = self.index + 1
+ end
+
+ -- update_player(playerctl:list()[M.active_player_index])
+ local player = players[self.index]
+ self:emit_signal("player::metadata", player)
+ self:emit_signal("player::position", player)
+end
+
+function M:previous_player()
+ local players = self.inner:list()
+ if #players == 0 then
+ return
+ elseif self.index - 1 < #players then
+ self.index = #players
+ else
+ self.index = self.index - 1
+ end
+
+ local player = players[self.index]
+ self:emit_signal("player::metadata", player)
+ self:emit_signal("player::position", player)
+end
+
+M.inner = playerctl.new {
+ players = {},
+ metadata = {
+ album = "xesam:album",
+ title = "xesam:title",
+ artist = "xesam:artist",
+ art = "mpris:artUrl",
+ },
+}
+
+local instance = gobject { class = M }
+instance:connect_signal("property::index", function(self, index)
+ qpersistent.store("active_player_index", self.index)
+end)
+return instance
diff --git a/.config/awesome/services/playerctl.lua b/.config/awesome/services/playerctl.lua
index f6ee71a..c4be74b 100644
--- a/.config/awesome/services/playerctl.lua
+++ b/.config/awesome/services/playerctl.lua
@@ -1,45 +1,591 @@
-local playerctl = require("lib.bling.signal.playerctl").lib {
- player = { "spotify", "%any" },
+-- CREDIT: https://github.com/kosorin/awesome-rice/blob/b6813bd1bbb3fdd697a1b994784b72daaeaf405d/services/media/playerctl.lua
+
+-- DEPENDENCIES: playerctl
+
+local type = type
+local pairs = pairs
+local ipairs = ipairs
+local gobject = require "gears.object"
+local gtable = require "gears.table"
+local gtimer = require "gears.timer"
+local lgi_playerctl = require("lgi").Playerctl -- /usr/share/gtk-doc/html/playerctl/index.html
+
+---@alias Playerctl.selector
+---|>nil # Select primary player
+---| string # Select player by `instance`
+---| "%all" # Select all players
+
+---@class Playerctl.position_data
+---@field position? integer
+---@field length? integer
+
+---@class Playerctl.data
+---@field name string
+---@field instance string
+---@field playback_status lgi.Playerctl.PlaybackStatus
+---@field position integer
+---@field shuffle boolean
+---@field loop_status lgi.Playerctl.LoopStatus
+---@field volume number
+---@field metadata table<string, any>
+---@field package _position_timer? gears.timer
+
+local playerctl = {
+ lowest_priority = math.maxinteger,
+ any = { name = "%any" },
+ all = { name = "%all" },
}
-playerctl:connect_signal("metadata", function(_, ...)
- awesome.emit_signal("services::playerctl::metadata", ...)
-end)
+---@class Playerctl : gears.object
+---@field unit integer # Number of microseconds in a second.
+---@field package primary_player_data? Playerctl.data
+---@field package player_data table<string, Playerctl.data>
+---@field package tracked_metadata table<string, string>
+---@field package excluded_players table<string, boolean>
+---@field package player_priorities table<string, integer>
+---@field package manager lgi.Playerctl.PlayerManager
+playerctl.object = { unit = 1000000 }
+
+---@param player_data? Playerctl.data
+---@return Playerctl.position_data|nil
+function playerctl.object:get_position_data(player_data)
+ player_data = player_data or self.primary_player_data
+ return (player_data and player_data.metadata)
+ and {
+ position = player_data.position,
+ length = player_data.metadata.length,
+ }
+end
+
+---@param self Playerctl
+---@param instance string
+---@return lgi.Playerctl.Player|nil
+local function find_player_by_instance(self, instance)
+ for _, player in ipairs(self.manager.players) do
+ if player.player_instance == instance then
+ return player
+ end
+ end
+end
+
+---@param self Playerctl
+---@param player_selector Playerctl.selector
+---@param action fun(player: lgi.Playerctl.Player)
+local function for_each_player(self, player_selector, action)
+ local players
+ if not player_selector then
+ local player_data = self.primary_player_data
+ if player_data then
+ players = { find_player_by_instance(self, player_data.instance) }
+ end
+ elseif player_selector == playerctl.all.name then
+ players = self.manager.players
+ elseif type(player_selector) == "string" then
+ players = { find_player_by_instance(self, player_selector) }
+ end
+
+ if players then
+ for _, p in ipairs(players) do
+ action(p)
+ end
+ end
+end
+
+---@param player_selector Playerctl.selector
+function playerctl.object:play_pause(player_selector)
+ for_each_player(self, player_selector, function(p)
+ p:play_pause()
+ end)
+end
+
+---@param player_selector Playerctl.selector
+function playerctl.object:play(player_selector)
+ for_each_player(self, player_selector, function(p)
+ p:play()
+ end)
+end
+
+---@param player_selector Playerctl.selector
+function playerctl.object:pause(player_selector)
+ for_each_player(self, player_selector, function(p)
+ p:pause()
+ end)
+end
+
+---@param player_selector Playerctl.selector
+function playerctl.object:stop(player_selector)
+ for_each_player(self, player_selector, function(p)
+ p:stop()
+ end)
+end
+
+---@param player_selector Playerctl.selector
+function playerctl.object:previous(player_selector)
+ for_each_player(self, player_selector, function(p)
+ p:previous()
+ end)
+end
+
+---@param player_selector Playerctl.selector
+function playerctl.object:next(player_selector)
+ for_each_player(self, player_selector, function(p)
+ p:next()
+ end)
+end
+
+---@param offset integer
+---@param player_selector Playerctl.selector
+function playerctl.object:skip(offset, player_selector)
+ if offset > 0 then
+ self:next(player_selector)
+ else
+ self:previous(player_selector)
+ end
+end
+
+---@param offset integer
+---@param player_selector Playerctl.selector
+function playerctl.object:rewind(offset, player_selector)
+ self:seek(-offset, player_selector)
+end
+
+---@param offset integer
+---@param player_selector Playerctl.selector
+function playerctl.object:fast_forward(offset, player_selector)
+ self:seek(offset, player_selector)
+end
+
+---@param offset integer
+---@param player_selector Playerctl.selector
+function playerctl.object:seek(offset, player_selector)
+ for_each_player(self, player_selector, function(p)
+ p:seek(offset)
+ end)
+end
+
+---@param loop_status lgi.Playerctl.LoopStatus
+---@param player_selector Playerctl.selector
+function playerctl.object:set_loop_status(loop_status, player_selector)
+ loop_status = loop_status:upper()
+ for_each_player(self, player_selector, function(p)
+ p:set_loop_status(loop_status)
+ end)
+end
+
+---@param player_selector Playerctl.selector
+function playerctl.object:cycle_loop_status(player_selector)
+ for_each_player(self, player_selector, function(p)
+ if p.loop_status == "NONE" then
+ p:set_loop_status "TRACK"
+ elseif p.loop_status == "TRACK" then
+ p:set_loop_status "PLAYLIST"
+ elseif p.loop_status == "PLAYLIST" then
+ p:set_loop_status "NONE"
+ end
+ end)
+end
+
+---@param position integer
+---@param player_selector Playerctl.selector
+function playerctl.object:set_position(position, player_selector)
+ for_each_player(self, player_selector, function(p)
+ p:set_position(position)
+ end)
+end
+
+---@param shuffle boolean
+---@param player_selector Playerctl.selector
+function playerctl.object:set_shuffle(shuffle, player_selector)
+ for_each_player(self, player_selector, function(p)
+ p:set_shuffle(shuffle)
+ end)
+end
+
+---@param player_selector Playerctl.selector
+function playerctl.object:toggle_shuffle(player_selector)
+ for_each_player(self, player_selector, function(p)
+ p:set_shuffle(not p.shuffle)
+ end)
+end
+
+---@param volume number
+---@param player_selector Playerctl.selector
+function playerctl.object:set_volume(volume, player_selector)
+ for_each_player(self, player_selector, function(p)
+ p:set_volume(volume)
+ end)
+end
+
+---@param player_data? Playerctl.data
+---@return boolean
+function playerctl.object:is_primary_player(player_data)
+ return self.primary_player_data == player_data
+end
+
+---@return Playerctl.data|nil
+function playerctl.object:get_primary_player_data()
+ return self.primary_player_data
+end
+
+---@param self Playerctl
+---@param player? lgi.Playerctl.Player
+local function update_primary_player(self, player)
+ if player then
+ self.manager:move_player_to_top(player)
+ end
+
+ local primary_player = self.manager.players[1]
+
+ local old = self.primary_player_data
+ local new = self.player_data[primary_player and primary_player.player_instance]
+ if old ~= new then
+ self.primary_player_data = new
+ self:emit_signal("player::primary", new, old)
+ end
+end
+
+---@param player_data Playerctl.data
+---@return boolean
+function playerctl.object:is_pinned(player_data)
+ local player_instance = player_data and player_data.instance or nil
+ return self.pinned_player_instance == player_instance
+end
+
+---@param player_data? Playerctl.data
+function playerctl.object:pin(player_data)
+ local player_instance = player_data and player_data.instance or nil
+ if self.pinned_player_instance ~= player_instance then
+ self.pinned_player_instance = player_instance
+ self:emit_signal("player::pinned", self.pinned_player_instance)
+
+ local player
+ if player_data then
+ player = find_player_by_instance(self, player_data.instance)
+ elseif self.primary_player_data then
+ player = find_player_by_instance(self, self.primary_player_data.instance)
+ end
+
+ update_primary_player(self, player)
+ end
+end
+
+---@return Playerctl.data[]
+function playerctl.object:list()
+ ---@type Playerctl.data[]
+ local players = {}
+ for _, p in ipairs(self.manager.players) do
+ players[#players + 1] = self.player_data[p.player_instance]
+ end
+ return players
+end
+
+---@param player_data Playerctl.data
+local function refresh_position_timer(player_data)
+ if player_data.playback_status == "PLAYING" then
+ player_data._position_timer:again()
+ else
+ player_data._position_timer:stop()
+ end
+end
+
+---@param self Playerctl
+---@param player_data Playerctl.data
+---@param by_timer? boolean
+local function update_position(self, player_data, by_timer)
+ local player = find_player_by_instance(self, player_data.instance)
+ if player then
+ player_data.position = player:get_position()
+ self:emit_signal("player::position", player_data, by_timer)
+ end
+end
-playerctl:connect_signal("position", function(_, ...)
- awesome.emit_signal("services::playerctl::position", ...)
-end)
+---@param self Playerctl
+---@param player_data Playerctl.data
+---@param metadata lgi.Playerctl.Metadata
+---@return boolean
+---@return table<string, boolean>
+local function update_metadata(self, player_data, metadata)
+ metadata = metadata and metadata.value or {}
-playerctl:connect_signal("playback_status", function(_, ...)
- awesome.emit_signal("services::playerctl::playback_status", ...)
-end)
+ local changed = false
+ local changed_data = {}
-playerctl:connect_signal("seeked", function(_, ...)
- awesome.emit_signal("services::playerctl::seeked", ...)
-end)
+ local function mark_changed(name)
+ changed = true
+ changed_data[name] = true
+ end
-playerctl:connect_signal("volume", function(_, ...)
- awesome.emit_signal("services::playerctl::volume", ...)
-end)
+ local target_metadata = player_data.metadata
+ for name, mpris_name in pairs(self.tracked_metadata) do
+ local value = metadata[mpris_name]
+ local value_type = type(value)
+ if value_type == "nil" or value_type == "boolean" or value_type == "number" or value_type == "string" then
+ if target_metadata[name] ~= value then
+ target_metadata[name] = value
+ mark_changed(name)
+ end
+ elseif value_type == "userdata" and value.type == "as" then
+ local old = target_metadata[name]
+ if type(old) ~= "table" then
+ old = {}
+ end
-playerctl:connect_signal("loop_status", function(_, ...)
- awesome.emit_signal("services::playerctl::loop_status", ...)
-end)
+ local new = {}
+ for _, s in value:ipairs() do
+ new[#new + 1] = s
+ end
-playerctl:connect_signal("shuffle", function(_, ...)
- awesome.emit_signal("services::playerctl::shuffle", ...)
-end)
+ target_metadata[name] = new
-playerctl:connect_signal("exit", function(_, ...)
- awesome.emit_signal("services::playerctl::exit", ...)
-end)
+ if #old ~= #new then
+ mark_changed(name)
+ else
+ for i = 1, #new do
+ if old[i] ~= new[i] then
+ mark_changed(name)
+ break
+ end
+ end
+ end
+ else
+ if target_metadata[name] ~= nil then
+ target_metadata[name] = nil
+ mark_changed(name)
+ end
+ end
+ end
-playerctl:connect_signal("exit", function(_, ...)
- awesome.emit_signal("services::playerctl::exit", ...)
-end)
+ return changed, changed_data
+end
-playerctl:connect_signal("no_players", function()
- awesome.emit_signal "services::playerctl::no_players"
-end)
+---@param self Playerctl
+---@param full_name lgi.Playerctl.PlayerName
+---@return lgi.Playerctl.Player
+local function manage_player(self, full_name)
+ local new_player = lgi_playerctl.Player.new_from_name(full_name)
-return playerctl
+ function new_player.on_metadata(p, metadata)
+ local player_data = self.player_data[p.player_instance]
+ if player_data and update_metadata(self, player_data, metadata) then
+ self:emit_signal("player::metadata", player_data)
+
+ update_position(self, player_data)
+ refresh_position_timer(player_data)
+ end
+ end
+
+ function new_player.on_playback_status(p, playback_status)
+ update_primary_player(self, p)
+
+ local player_data = self.player_data[p.player_instance]
+ if player_data and player_data.playback_status ~= playback_status then
+ player_data.playback_status = playback_status
+ self:emit_signal("player::playback_status", player_data)
+
+ update_position(self, player_data)
+ refresh_position_timer(player_data)
+ end
+ end
+
+ function new_player.on_seeked(p, position)
+ local player_data = self.player_data[p.player_instance]
+ if player_data and player_data.position ~= position then
+ player_data.position = position
+ self:emit_signal("player::position", player_data)
+
+ refresh_position_timer(player_data)
+ end
+ end
+
+ function new_player.on_shuffle(p, shuffle)
+ local player_data = self.player_data[p.player_instance]
+ if player_data and player_data.shuffle ~= shuffle then
+ player_data.shuffle = shuffle
+ self:emit_signal("player::shuffle", player_data)
+ end
+ end
+
+ function new_player.on_loop_status(p, loop_status)
+ local player_data = self.player_data[p.player_instance]
+ if player_data and player_data.loop_status ~= loop_status then
+ player_data.loop_status = loop_status
+ self:emit_signal("player::loop_status", player_data)
+ end
+ end
+
+ function new_player.on_volume(p, volume)
+ local player_data = self.player_data[p.player_instance]
+ if player_data and player_data.volume ~= volume then
+ player_data.volume = volume
+ self:emit_signal("player::volume", player_data)
+ end
+ end
+
+ self.manager:manage_player(new_player)
+
+ return new_player
+end
+
+---@param self Playerctl
+---@param player_name string
+---@return boolean
+local function filter_name(self, player_name)
+ if self.excluded_players[player_name] then
+ return false
+ end
+ if self.player_priorities[playerctl.any] or self.player_priorities[player_name] then
+ return true
+ end
+ return false
+end
+
+---@param self Playerctl
+---@param player_a lgi.Playerctl.Player
+---@param player_b lgi.Playerctl.Player
+---@return sign
+local function compare_players(self, player_a, player_b)
+ if player_a.player_name < player_b.player_name then
+ return -1
+ elseif player_a.player_name > player_b.player_name then
+ return 1
+ else
+ return 0
+ end
+end
+
+---@param self Playerctl
+local function initialize_manager(self)
+ self.player_data = {}
+
+ self.manager = lgi_playerctl.PlayerManager()
+ self.manager:set_sort_func(function(a, b)
+ local player_a = lgi_playerctl.Player(a)
+ local player_b = lgi_playerctl.Player(b)
+ return compare_players(self, player_a, player_b)
+ end)
+
+ ---@param full_name lgi.Playerctl.PlayerName
+ ---@return lgi.Playerctl.Player|nil
+ local function try_manage(full_name)
+ if filter_name(self, full_name.name) then
+ return manage_player(self, full_name)
+ end
+ end
+
+ function self.manager.on_name_appeared(_, full_name)
+ try_manage(full_name)
+ end
+
+ function self.manager.on_player_appeared(_, player)
+ ---@type Playerctl.data
+ local player_data = {
+ name = player.player_name,
+ instance = player.player_instance,
+ playback_status = player.playback_status,
+ position = player.position,
+ shuffle = player.shuffle,
+ loop_status = player.loop_status,
+ volume = player.volume,
+ metadata = {},
+ }
+ update_metadata(self, player_data, player.metadata)
+
+ player_data._position_timer = gtimer {
+ timeout = 1,
+ callback = function()
+ update_position(self, player_data, true)
+ end,
+ }
+ refresh_position_timer(player_data)
+
+ self.player_data[player_data.instance] = player_data
+ self:emit_signal("player::appeared", player_data)
+
+ update_primary_player(self, player)
+ end
+
+ function self.manager.on_player_vanished(_, player)
+ update_primary_player(self)
+
+ if self.pinned_player_instance == player.player_instance then
+ self:pin(nil)
+ end
+
+ local player_data = self.player_data[player.player_instance]
+ if player_data then
+ player_data._position_timer:stop()
+ self:emit_signal("player::vanished", player_data)
+ self.player_data[player.player_instance] = nil
+ end
+ end
+
+ for _, full_name in ipairs(self.manager.player_names) do
+ try_manage(full_name)
+ end
+
+ update_primary_player(self)
+end
+
+---@param self Playerctl
+---@param args? Playerctl.new.args
+local function parse_args(self, args)
+ args = args or {}
+
+ self.tracked_metadata = args.metadata or {}
+
+ -- Always track length
+ self.tracked_metadata.length = "mpris:length"
+
+ local excluded_players = {}
+ if type(args.excluded_players) == "string" then
+ excluded_players[args.excluded_players] = true
+ elseif args.excluded_players then
+ for _, name in ipairs(args.excluded_players) do
+ excluded_players[name] = true
+ end
+ end
+ self.excluded_players = excluded_players
+
+ local function get_priority_key(name)
+ return name == playerctl.any.name and playerctl.any or name
+ end
+ local player_priorities
+ if type(args.players) == "string" then
+ player_priorities = { [get_priority_key(args.players)] = 1 }
+ elseif type(args.players) == "table" and #args.players > 0 then
+ player_priorities = {}
+ for i, name in ipairs(args.players) do
+ player_priorities[get_priority_key(name)] = i
+ end
+ else
+ player_priorities = { [playerctl.any] = 1 }
+ end
+ self.player_priorities = player_priorities
+end
+
+---@class Playerctl.new.args
+---@field players string[]
+---@field excluded_players? string[]
+---@field metadata table<string, string>
+
+---@param args? Playerctl.new.args
+---@return Playerctl
+function playerctl.new(args)
+ local self = gtable.crush(gobject {}, playerctl.object, true) --[[@as Playerctl]]
+
+ parse_args(self, args)
+
+ initialize_manager(self)
+
+ return self
+end
+
+return playerctl.new {
+ players = {},
+ metadata = {
+ album = "xesam:album",
+ title = "xesam:title",
+ artist = "xesam:artist",
+ art = "mpris:artUrl",
+ },
+}
diff --git a/.config/awesome/signals/client.lua b/.config/awesome/signals/client.lua
index 8788735..f0b5136 100644
--- a/.config/awesome/signals/client.lua
+++ b/.config/awesome/signals/client.lua
@@ -1,9 +1,9 @@
-local qvars = require "quarrel.vars"
+local qui = require "quarrel.ui"
local rectangle = require("gears.shape").rectangle
-local conductor = require "ui.conductor"
+-- local conductor = require "ui.conductor"
client.connect_signal("request::manage", function(c)
- c.shape = qvars.shape
+ c.shape = qui.shape
if c.maximized then
c.maximized = false
c.maximized = true
@@ -17,8 +17,8 @@ local function handle_corners(c)
c.shape = rectangle
c.border_width = 0
else
- c.shape = qvars.shape
- c.border_width = qvars.border_width
+ c.shape = qui.shape
+ c.border_width = qui.BORDER_WIDTH
end
end
diff --git a/.config/awesome/ui/conductor/init.lua b/.config/awesome/ui/conductor/init.lua
index e6fe552..402945c 100644
--- a/.config/awesome/ui/conductor/init.lua
+++ b/.config/awesome/ui/conductor/init.lua
@@ -1,8 +1,64 @@
local awful = require "awful"
+local gshape = require "gears.shape"
+local phosphor = require "assets.phosphor"
local q = require "quarrel"
local qui = require "quarrel.ui"
local qvars = require "quarrel.vars"
+local wibox = require "wibox"
-local conductor = {}
+local M = {}
-return conductor
+M._popup = qui.popup {
+ -- visible = false,
+ ontop = true,
+ placement = "right",
+ shape = function(cr, w, h)
+ gshape.partially_rounded_rect(cr, w, h, true, false, false, true, qui.BORDER_RADIUS)
+ end,
+ -- x = awful.screen.focused().geometry.width,
+ -- minimum_width = width,
+ -- maximum_width = width,
+ -- maximum_height = max_height,
+ widget = awful.widget.tasklist {
+ screen = awful.screen.focused(),
+ filter = awful.widget.tasklist.filter.allscreen,
+ layout = {
+ spacing = qui.BIG_PADDING,
+ layout = wibox.layout.fixed.vertical,
+ },
+ widget_template = qui.styled {
+ widget = wibox.container.background,
+ {
+ {
+ widget = wibox.container.constraint,
+ strategy = "max",
+ height = qui.CHAR_HEIGHT,
+ width = qui.CHAR_HEIGHT,
+ {
+ widget = wibox.widget.imagebox,
+ id = "icon_role",
+ },
+ },
+ {
+ widget = wibox.container.constraint,
+ strategy = "max",
+ width = qui.CHAR_WIDTH * 24,
+ {
+ widget = wibox.widget.textbox,
+ id = "client_name",
+ },
+ },
+ layout = wibox.layout.fixed.horizontal,
+ spacing = qui.PADDING,
+ },
+ create_callback = function(self, c)
+ self:get_children_by_id("client_name")[1].text = c.icon_name
+ end,
+ update_callback = function(self, c)
+ self:get_children_by_id("client_name")[1].text = c.name
+ end,
+ },
+ },
+}
+
+return M
diff --git a/.config/awesome/ui/decorations/wallpaper.lua b/.config/awesome/ui/decorations/wallpaper.lua
index 3a204c2..cb01745 100644
--- a/.config/awesome/ui/decorations/wallpaper.lua
+++ b/.config/awesome/ui/decorations/wallpaper.lua
@@ -1,12 +1,12 @@
+local qcolor = require "quarrel.color"
local qmarkup = require "quarrel.markup"
local qui = require "quarrel.ui"
-local qvars = require "quarrel.vars"
local wallpaper = require "awful.wallpaper"
local wibox = require "wibox"
screen.connect_signal("request::wallpaper", function(s)
wallpaper {
- bg = qvars.colors.dim.bg,
+ bg = qcolor.palette.bg.low,
screen = s,
widget = {
widget = wibox.container.place,
@@ -16,12 +16,12 @@ screen.connect_signal("request::wallpaper", function(s)
widget = wibox.widget.textbox,
font = qui.font(1.5),
markup = table.concat({
- qmarkup(" ___", { fg = qvars.colors.red }),
- qmarkup(" /\\ \\", { fg = qvars.colors.green }),
- qmarkup(" /::\\ \\", { fg = qvars.colors.yellow }),
- qmarkup("/::\\:\\__\\", { fg = qvars.colors.blue }),
- qmarkup("\\/\\::/ /", { fg = qvars.colors.pink }),
- qmarkup(" \\/__/", { fg = qvars.colors.cyan }),
+ qmarkup(" ___", { fg = qcolor.palette.blue() }),
+ qmarkup(" /\\ \\", { fg = qcolor.palette.green() }),
+ qmarkup(" /::\\ \\", { fg = qcolor.palette.yellow() }),
+ qmarkup("/::\\:\\__\\", { fg = qcolor.palette.orange() }),
+ qmarkup("\\/\\::/ /", { fg = qcolor.palette.red() }),
+ qmarkup(" \\/__/", { fg = qcolor.palette.pink() }),
}, "\n"),
},
},
diff --git a/.config/awesome/ui/fresnel/init.lua b/.config/awesome/ui/fresnel/init.lua
index 9905741..5d091b6 100644
--- a/.config/awesome/ui/fresnel/init.lua
+++ b/.config/awesome/ui/fresnel/init.lua
@@ -1,8 +1,10 @@
local al_prompt = require "lib.bling.widget.app_launcher.prompt"
local awful = require "awful"
-local beautiful = require "beautiful"
local cfg = require "misc.cfg"
+local gshape = require "gears.shape"
local gtable = require "gears.table"
+local qanim = require "quarrel.animation"
+local qcolor = require "quarrel.color"
local qnative = require "quarrel.native"
local qtable = require "quarrel.table"
local qui = require "quarrel.ui"
@@ -42,8 +44,8 @@ function fresnel:_update(query, scrolled)
self._entries_offset = 0
end
- local layout = self._widget.widget:get_children_by_id("entry_layout")[1]
- local status = self._widget.widget:get_children_by_id("status")[1]
+ local layout = self._popup.widget:get_children_by_id("entry_layout")[1]
+ local status = self._popup.widget:get_children_by_id("status")[1]
local all_providers = {}
local entries_count = 0
@@ -71,18 +73,18 @@ function fresnel:_update(query, scrolled)
local entry_widget = wibox.widget {
widget = wibox.container.background,
- shape = qvars.shape,
+ shape = qui.shape,
{
widget = wibox.container.margin,
- margins = qvars.padding,
+ margins = qui.PADDING,
{
widget = wibox.container.constraint,
strategy = "max",
- height = qvars.char_height,
+ height = qui.CHAR_HEIGHT,
{
{
widget = wibox.container.background,
- fg = qvars.colors.dim.fg,
+ fg = qcolor.palette.low,
{
widget = wibox.widget.textbox,
text = entry.provider .. " | ",
@@ -92,7 +94,7 @@ function fresnel:_update(query, scrolled)
widget = wibox.widget.textbox,
text = entry.message,
},
- spacing = qvars.padding,
+ spacing = qui.PADDING,
layout = wibox.layout.fixed.horizontal,
},
},
@@ -111,21 +113,21 @@ function fresnel:_update(query, scrolled)
if self._selected_index + self._entries_offset == i then
entry_widget._selected = true
- entry_widget.bg = qvars.colors.black
+ entry_widget.bg = qcolor.palette.bg.high
end
entry_widget:connect_signal("mouse::enter", function()
if entry_widget._selected == true then
return
end
- entry_widget.bg = qvars.colors.black
+ entry_widget.bg = qcolor.palette.bg.high
end)
entry_widget:connect_signal("mouse::leave", function()
if entry_widget._selected == true then
return
end
- entry_widget.bg = qvars.colors.bg
+ entry_widget.bg = qcolor.palette.bg()
end)
layout:add(entry_widget)
@@ -143,7 +145,7 @@ fresnel._prompt = al_prompt {
prompt = "",
reset_on_stop = true,
ul_cursor = "low",
- bg_cursor = qvars.colors.black,
+ bg_cursor = qcolor.palette.bg.high,
changed_callback = function(text)
if fresnel._text == text then
return
@@ -192,27 +194,31 @@ fresnel._prompt = al_prompt {
end,
}
-fresnel._widget = qui.popup {
- visible = false,
+local max_height = qui.BIG_PADDING * 2 + (qui.BIG_PADDING * 2 + qui.CHAR_HEIGHT) * 10
+-- + qui.PADDING
+-- + qui.PADDING * 9
+
+local width = awful.screen.focused().geometry.width / 2
+
+fresnel._popup = qui.popup {
+ -- visible = false,
ontop = true,
- placement = function(d)
- awful.placement.top(d, {
- margins = {
- top = beautiful.useless_gap * 2,
- },
- })
+ placement = false,
+ shape = function(cr, w)
+ gshape.partially_rounded_rect(cr, w, 0, false, false, true, true, qui.BORDER_RADIUS)
end,
- minimum_width = awful.screen.focused().geometry.width / 2,
- maximum_width = awful.screen.focused().geometry.width / 2,
+ x = width / 2,
+ minimum_width = width,
+ maximum_width = width,
+ -- maximum_height = max_height,
widget = {
- {
+ qui.styled {
widget = wibox.container.background,
- bg = qvars.colors.black,
- fg = qvars.colors.dim.fg,
- shape = qvars.shape,
+ fg = qcolor.palette.fg.low,
+ bg = qcolor.palette.bg.high,
{
widget = wibox.container.margin,
- margins = qvars.padding,
+ margins = qui.BIG_PADDING,
{
{
widget = wibox.widget.textbox,
@@ -221,16 +227,16 @@ fresnel._widget = qui.popup {
{
widget = wibox.container.margin,
margins = {
- left = qvars.padding,
- right = qvars.padding,
+ left = qui.PADDING,
+ right = qui.PADDING,
},
{
widget = wibox.container.constraint,
strategy = "max",
- height = qvars.char_height,
+ height = qui.CHAR_HEIGHT,
{
widget = wibox.container.background,
- fg = qvars.colors.fg,
+ fg = qcolor.palette.fg(),
fresnel._prompt.textbox,
},
},
@@ -247,10 +253,10 @@ fresnel._widget = qui.popup {
{
widget = wibox.container.margin,
margins = {
- top = qvars.padding,
+ top = qui.PADDING,
},
{
- spacing = qvars.padding,
+ spacing = qui.PADDING,
layout = wibox.layout.fixed.vertical,
id = "entry_layout",
},
@@ -258,35 +264,43 @@ fresnel._widget = qui.popup {
layout = wibox.layout.align.vertical,
},
}
-fresnel._widget.maximum_height = qvars.big_padding * 2
- + (qvars.padding * 2 + qvars.char_height) * (1 + 10)
- + qvars.padding
- + qvars.padding * 9
-
function fresnel:show()
self._toggled = true
- self._timed.target = 1
+ self._opacity_timed.target = 1
+ self._height_timed:set(max_height)
self:_update()
self._prompt:start()
end
function fresnel:hide()
self._toggled = false
- self._timed.target = 0
+ self._opacity_timed.target = 0
+ self._height_timed:set(0)
self._prompt:stop()
end
-fresnel._timed = rubato.timed {
+fresnel._height_timed = qanim:new {
+ duration = qvars.anim_duration,
+ pos = 0,
+ easing = qvars.easing,
+ subscribed = function(pos)
+ fresnel._popup.shape = function(cr, w)
+ gshape.partially_rounded_rect(cr, w, pos, false, false, true, true, qui.BORDER_RADIUS)
+ end
+ end,
+}
+
+-- TODO: optimize the search algo to be more efficient and not require making fresnel invisible
+fresnel._opacity_timed = rubato.timed {
duration = qvars.anim_duration,
- intro = qvars.anim_intro,
pos = 0,
subscribed = function(pos)
- fresnel._widget.opacity = pos
+ fresnel._popup.opacity = pos
if pos == 0 then
- fresnel._widget.visible = false
+ fresnel._popup.visible = false
else
- fresnel._widget.visible = true
+ fresnel._popup.visible = true
end
end,
}
diff --git a/.config/awesome/ui/init.lua b/.config/awesome/ui/init.lua
index dd93ad1..6ad1c7b 100644
--- a/.config/awesome/ui/init.lua
+++ b/.config/awesome/ui/init.lua
@@ -1,4 +1,4 @@
require "ui.statusbar"
require "ui.decorations"
-require "ui.conductor"
-require "ui.osd"
+-- require "ui.conductor"
+-- require "ui.osd"
diff --git a/.config/awesome/ui/insightful/init.lua b/.config/awesome/ui/insightful/init.lua
index 3dcbbea..be072d5 100644
--- a/.config/awesome/ui/insightful/init.lua
+++ b/.config/awesome/ui/insightful/init.lua
@@ -1,6 +1,6 @@
local awful = require "awful"
local beautiful = require "beautiful"
-local qstore = require "quarrel.store"
+local qbind = require "quarrel.bind"
local qtable = require "quarrel.table"
local qui = require "quarrel.ui"
local qvars = require "quarrel.vars"
@@ -81,7 +81,7 @@ insightful._widget = qui.popup {
minimum_width = awful.screen.focused().geometry.width / 2,
widget = {
layout = wibox.layout.fixed.vertical,
- spacing = qvars.big_padding,
+ spacing = qui.BIG_PADDING,
id = "layout_container",
},
}
@@ -90,7 +90,7 @@ function insightful:_generate()
local grouped_binds = {}
local layout_container = insightful._widget.widget.layout_container
- for _, keybind in ipairs(qstore.bindings) do
+ for _, keybind in ipairs(qbind.bindings) do
local group = keybind.group or "general"
local group_exists = grouped_binds[group] ~= nil
@@ -107,7 +107,7 @@ function insightful:_generate()
for group, keybinds in qtable.opairs(grouped_binds) do
local group_layout = {
- spacing = qvars.padding,
+ spacing = qui.PADDING,
layout = wibox.layout.fixed.vertical,
}
@@ -129,11 +129,11 @@ function insightful:_generate()
1,
qui.styled {
widget = wibox.container.background,
- bg = qvars.colors.bright.black,
+ bg = qcolor.palette.bright.black,
border_width = 0,
{
widget = wibox.container.margin,
- margins = qvars.padding,
+ margins = qui.PADDING,
{
widget = wibox.widget.textbox,
text = insightful._keymap[mod] or mod,
@@ -166,7 +166,7 @@ function insightful:_generate()
if keybind.desc then
key_and_desc_layout[3] = {
widget = wibox.container.background,
- fg = qvars.colors.dim.fg,
+ fg = qcolor.palette.dim.fg,
{
widget = wibox.widget.textbox,
text = keybind.desc,
@@ -183,12 +183,12 @@ function insightful:_generate()
{
{
widget = wibox.container.background,
- bg = qvars.colors.yellow,
- fg = qvars.colors.bg,
- shape = qvars.shape,
+ bg = qcolor.palette.yellow,
+ fg = qcolor.palette.bg,
+ shape = qui.shape,
{
widget = wibox.container.margin,
- margins = qvars.padding,
+ margins = qui.PADDING,
{
widget = wibox.widget.textbox,
text = group,
@@ -199,7 +199,7 @@ function insightful:_generate()
layout = wibox.layout.align.horizontal,
},
group_layout,
- spacing = qvars.padding,
+ spacing = qui.PADDING,
layout = wibox.layout.fixed.vertical,
}
end
diff --git a/.config/awesome/ui/osd.lua b/.config/awesome/ui/osd.lua
deleted file mode 100644
index fc74ed9..0000000
--- a/.config/awesome/ui/osd.lua
+++ /dev/null
@@ -1,102 +0,0 @@
---- Heavy inspiration from a design made by https://github.com/tsukki9696
-
-local awful = require "awful"
-local beautiful = require "beautiful"
-local gtimer = require "gears.timer"
-local phosphor = require "assets.phosphor"
-local qmath = require "quarrel.math"
-local qui = require "quarrel.ui"
-local qvars = require "quarrel.vars"
-local rubato = require "lib.rubato"
-local wibox = require "wibox"
-
-local osd = {}
-
-local widget = qui.popup {
- ontop = true,
- visible = false,
- placement = function(d)
- awful.placement.top(d, {
- margins = {
- top = beautiful.useless_gap * 2,
- },
- })
- end,
- minimum_height = qvars.char_height * 2,
- -- minimum_width = awful.screen.focused().geometry.width / 2,
- widget = {
- {
- widget = wibox.container.place,
-
- qui.icon {
- icon = phosphor.speaker_simple_none_fill,
- widget = {
- forced_height = qvars.char_height * 1.2,
- forced_width = qvars.char_height * 1.2,
- id = "icon",
- },
- },
- },
- {
- widget = wibox.container.place,
- {
- widget = wibox.widget.progressbar,
- max_value = 100,
- value = 20,
- forced_height = qvars.char_height / 1.5,
- forced_width = qvars.expanded_bar_size
- - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2)
- - (qvars.char_height / 1.25 + qvars.padding) * 3,
- color = qvars.colors.yellow,
- background_color = qvars.colors.black,
- shape = qvars.shape,
- id = "progress",
- },
- },
-
- {
- widget = wibox.widget.textbox,
- text = "20%",
- font = qui.font(1.2),
- id = "percentage",
- },
- layout = wibox.layout.fixed.horizontal,
- spacing = qvars.big_padding,
- },
-}
-
-local timer
-
-local anim = rubato.timed {
- duration = qvars.anim_duration,
- intro = qvars.anim_intro,
- pos = 0,
- subscribed = function(pos)
- widget.opacity = pos
- if pos == 0 then
- widget.visible = false
- elseif not widget.visible then
- widget.visible = true
- elseif pos == 1 then
- timer:start()
- end
- end,
-}
-
-timer = gtimer {
- timeout = 1,
- callback = function()
- anim.target = 0
- end,
- single_shot = true,
-}
-
-function osd.notify(icon, value, max)
- anim.target = 1
- widget:get_children_by_id("icon")[1].image = icon
- widget:get_children_by_id("progress")[1].value = value
- widget:get_children_by_id("progress")[1].max_value = max
- widget:get_children_by_id("percentage")[1].text = tostring(qmath.percentage(value, max)) .. "%"
-end
-
-return osd
diff --git a/.config/awesome/ui/osd/init.lua b/.config/awesome/ui/osd/init.lua
new file mode 100644
index 0000000..4310f17
--- /dev/null
+++ b/.config/awesome/ui/osd/init.lua
@@ -0,0 +1,102 @@
+local awful = require "awful"
+local beautiful = require "beautiful"
+local gtimer = require "gears.timer"
+local phosphor = require "assets.phosphor"
+local qmath = require "quarrel.math"
+local qui = require "quarrel.ui"
+local qvars = require "quarrel.vars"
+local rubato = require "lib.rubato"
+local wibox = require "wibox"
+-- local mpris_widget = require "ui.osd.mpris"
+
+local osd = {}
+
+local widget = awful.popup(qui.styled {
+ ontop = true,
+ visible = true,
+ placement = function(d)
+ awful.placement.top(d, {
+ margins = {
+ top = beautiful.useless_gap * 2,
+ },
+ })
+ end,
+ minimum_height = qui.CHAR_HEIGHT * 3,
+ -- widget = mpris_widget
+ -- minimum_width = awful.screen.focused().geometry.width / 2,
+ -- widget = {
+ -- {
+ -- widget = wibox.container.place,
+ --
+ -- qui.icon {
+ -- icon = phosphor.speaker_simple_none_fill,
+ -- widget = {
+ -- forced_height = qui.CHAR_HEIGHT * 1.2,
+ -- forced_width = qui.CHAR_HEIGHT * 1.2,
+ -- id = "icon",
+ -- },
+ -- },
+ -- },
+ -- {
+ -- widget = wibox.container.place,
+ -- {
+ -- widget = wibox.widget.progressbar,
+ -- max_value = 100,
+ -- value = 20,
+ -- forced_height = qui.CHAR_HEIGHT / 1.5,
+ -- forced_width = qvars.expanded_bar_size
+ -- - (qui.BIG_PADDING + qui.BIG_PADDING * 2 + qui.PADDING * 2)
+ -- - (qui.CHAR_HEIGHT / 1.25 + qui.PADDING) * 3,
+ -- color = qcolor.palette.yellow(),
+ -- background_color = qcolor.palette.border.variant,
+ -- shape = qui.shape,
+ -- id = "progress",
+ -- },
+ -- },
+ --
+ -- {
+ -- widget = wibox.widget.textbox,
+ -- text = "20%",
+ -- font = qui.font(1.2),
+ -- id = "percentage",
+ -- },
+ -- layout = wibox.layout.fixed.horizontal,
+ -- spacing = qui.BIG_PADDING,
+ -- },
+})
+
+local timer
+
+local anim = rubato.timed {
+ duration = qvars.anim_duration,
+ intro = qvars.anim_intro,
+ pos = 1,
+ subscribed = function(pos)
+ widget.opacity = pos
+ if pos == 0 then
+ widget.visible = false
+ elseif not widget.visible then
+ widget.visible = true
+ elseif pos == 1 then
+ -- timer:start()
+ end
+ end,
+}
+
+timer = gtimer {
+ timeout = 1,
+ callback = function()
+ -- anim.target = 0
+ end,
+ single_shot = true,
+}
+
+function osd.notify(icon, value, max)
+ anim.target = 1
+ widget:get_children_by_id("icon")[1].image = icon
+ widget:get_children_by_id("progress")[1].value = value
+ widget:get_children_by_id("progress")[1].max_value = max
+ widget:get_children_by_id("percentage")[1].text = tostring(qmath.percentage(value, max)) .. "%"
+end
+
+return osd
diff --git a/.config/awesome/ui/powermenu/init.lua b/.config/awesome/ui/powermenu/init.lua
index 81d2ea3..2e2b4af 100644
--- a/.config/awesome/ui/powermenu/init.lua
+++ b/.config/awesome/ui/powermenu/init.lua
@@ -1,5 +1,6 @@
local awful = require "awful"
local phosphor = require "assets.phosphor"
+local qcolor = require "quarrel.color"
local qui = require "quarrel.ui"
local qvars = require "quarrel.vars"
local rubato = require "lib.rubato"
@@ -8,21 +9,21 @@ local wibox = require "wibox"
local function create_button(title, icon, color, exec)
return wibox.widget {
widget = wibox.container.background,
- shape = qvars.shape,
+ shape = qui.shape,
{
widget = wibox.container.margin,
- margins = qvars.big_padding,
+ margins = qui.BIG_PADDING,
{
qui.styled {
widget = wibox.container.background,
border_color = color,
{
widget = wibox.container.margin,
- margins = qvars.big_padding,
+ margins = qui.BIG_PADDING,
qui.icon {
widget = {
- forced_height = qvars.char_height * 4 - qvars.big_padding * 2,
- forced_width = qvars.char_height * 4 - qvars.big_padding * 2,
+ forced_height = qui.CHAR_HEIGHT * 4 - qui.BIG_PADDING * 2,
+ forced_width = qui.CHAR_HEIGHT * 4 - qui.BIG_PADDING * 2,
},
icon = icon,
color = color,
@@ -37,12 +38,12 @@ local function create_button(title, icon, color, exec)
},
},
layout = wibox.layout.fixed.vertical,
- spacing = qvars.big_padding,
+ spacing = qui.BIG_PADDING,
exec = exec,
},
},
select = function(self)
- self.bg = qvars.colors.black
+ self.bg = qcolor.palette.bg.high
end,
}
end
@@ -56,7 +57,7 @@ screen.connect_signal("request::desktop_decoration", function(s)
visible = false,
minimum_width = s.geometry.width,
minimum_height = s.geometry.height,
- bg = qvars.colors.bg .. "ee",
+ bg = qcolor.palette.bg() .. "ee",
border_width = 0,
widget = {
widget = wibox.container.place,
@@ -64,10 +65,10 @@ screen.connect_signal("request::desktop_decoration", function(s)
widget = wibox.container.background,
{
widget = wibox.container.margin,
- margins = qvars.big_padding,
+ margins = qui.BIG_PADDING,
{
layout = wibox.layout.fixed.horizontal,
- spacing = qvars.big_padding * 2,
+ spacing = qui.BIG_PADDING * 2,
id = "list",
},
},
@@ -77,16 +78,16 @@ screen.connect_signal("request::desktop_decoration", function(s)
local layout = powermenu._widget.widget:get_children_by_id("list")[1]
- layout:add(create_button("Shutdown", phosphor.power_bold, qvars.colors.red, function()
+ layout:add(create_button("Shutdown", phosphor.power_bold, qcolor.palette.red(), function()
awful.spawn "poweroff"
end))
- layout:add(create_button("Log out", phosphor.sign_out_bold, qvars.colors.green, function()
+ layout:add(create_button("Log out", phosphor.sign_out_bold, qcolor.palette.green(), function()
awesome.quit()
end))
- layout:add(create_button("Lock", phosphor.lock_simple_fill, qvars.colors.blue, function()
+ layout:add(create_button("Lock", phosphor.lock_simple_fill, qcolor.palette.blue(), function()
require("quarrel").debug "locked!l"
end))
- layout:add(create_button("Restart", phosphor.arrow_clockwise_bold, qvars.colors.pink, function()
+ layout:add(create_button("Restart", phosphor.arrow_clockwise_bold, qcolor.palette.pink(), function()
awful.spawn "reboot"
end))
end)
diff --git a/.config/awesome/ui/statusbar/consts.lua b/.config/awesome/ui/statusbar/consts.lua
new file mode 100644
index 0000000..00d01bf
--- /dev/null
+++ b/.config/awesome/ui/statusbar/consts.lua
@@ -0,0 +1,8 @@
+local qui = require "quarrel.ui"
+local C = {}
+
+C.BAR_SIZE = 24 + qui.BIG_PADDING * 2
+C.ELEMENT_SIZE = 12
+C.EXPANDED_BAR_SIZE = qui.BIG_PADDING + (qui.BIG_PADDING * 2 + C.ELEMENT_SIZE * 4) * 4 + qui.PADDING * 2
+
+return C
diff --git a/.config/awesome/ui/statusbar/init.lua b/.config/awesome/ui/statusbar/init.lua
index 77a28f7..137ffb8 100644
--- a/.config/awesome/ui/statusbar/init.lua
+++ b/.config/awesome/ui/statusbar/init.lua
@@ -1,18 +1,17 @@
local awful = require "awful"
local beautiful = require "beautiful"
local panel = require "ui.statusbar.panel"
-local phosphor = require "assets.phosphor"
-local qstore = require "quarrel.store"
+local qanim = require "quarrel.animation"
local qui = require "quarrel.ui"
local qvars = require "quarrel.vars"
-local rubato = require "lib.rubato"
local wibox = require "wibox"
+local M = require "ui.statusbar.consts"
+
local clock = require "ui.statusbar.widgets.clock"
local displays = require "ui.statusbar.widgets.displays"
local keyboardlayout = require "ui.statusbar.widgets.keyboardlayout"
-local taglist = require "ui.statusbar.widgets.taglist"
-local tasklist = require "ui.statusbar.widgets.tasklist"
+local taglist = require "ui.statusbar.widgets.taglist_new"
screen.connect_signal("request::desktop_decoration", function(s)
local bar = qui.popup {
@@ -21,17 +20,16 @@ screen.connect_signal("request::desktop_decoration", function(s)
margins = beautiful.useless_gap * 2,
})
end,
- minimum_height = s.geometry.height - (beautiful.useless_gap * 4 + qvars.border_width * 2),
+ minimum_height = s.geometry.height - (beautiful.useless_gap * 4 + qui.BORDER_WIDTH * 2),
widget = {
{
{
taglist(s),
layout = wibox.layout.fixed.vertical,
- spacing = qvars.padding * 2,
+ spacing = qui.PADDING * 2,
id = "top",
},
nil,
- -- tasklist(s),
{
widget = wibox.container.place,
valign = "bottom",
@@ -44,14 +42,14 @@ screen.connect_signal("request::desktop_decoration", function(s)
widget = wibox.container.place,
{
widget = wibox.container.constraint,
- height = qvars.char_height,
- width = qvars.char_height,
+ height = qui.CHAR_HEIGHT,
+ width = qui.CHAR_HEIGHT,
keyboardlayout,
},
},
clock,
layout = wibox.layout.fixed.vertical,
- spacing = qvars.padding * 2,
+ spacing = qui.PADDING * 2,
},
},
layout = wibox.layout.align.vertical,
@@ -61,51 +59,47 @@ screen.connect_signal("request::desktop_decoration", function(s)
nil,
layout = wibox.layout.align.horizontal,
},
+
+ toggled = false,
}
- local bar_width = bar.width + qvars.border_width * 2
+ local bar_width = bar.width + qui.BORDER_WIDTH * 2
+
+ bar.shape = function(cr, _, h)
+ qui.shape(cr, bar_width, h)
+ end
bar:struts {
left = bar_width + beautiful.useless_gap * 4,
}
- local panel_width
- local panel_toggle = { toggled = false } -- hacky but it works
+ bar.widget.widget.third = panel
- local timed = rubato.timed {
+ local timed = qanim:new {
duration = qvars.anim_duration,
- intro = qvars.anim_intro,
pos = bar_width,
+ easing = qvars.easing,
subscribed = function(pos)
- if pos ~= bar_width and panel_toggle.toggled then
- bar.widget.widget.third = panel
- if panel_width == nil then
- panel_width = bar.widget.widget.third.width
- end
+ if pos ~= bar_width and bar.toggled then
bar.ontop = true
- elseif pos == bar_width and not panel_toggle.toggled then
- bar.widget.widget.third = nil
+ elseif pos == bar_width and not bar.toggled then
bar.ontop = false
end
bar.shape = function(cr, _, h)
- qvars.shape(cr, pos, h)
+ qui.shape(cr, pos, h)
end
end,
}
- panel_toggle = qui.toggle {
- off = phosphor.arrows_out_simple_fill,
- on = phosphor.arrows_in_simple_fill,
- press = function(self)
- if not self.toggled then
- timed.target = bar_width
- else
- timed.target = bar_width + qvars.expanded_bar_size
- end
- end,
- }
+ function bar:toggle()
+ self.toggled = not self.toggled
+ if self.toggled then
+ timed:set(bar_width + M.EXPANDED_BAR_SIZE)
+ else
+ timed:set(bar_width)
+ end
+ end
- bar.widget:get_children_by_id("top")[1]:insert(1, panel_toggle)
- qstore.panel_toggle = panel_toggle
+ s.bar = bar
end)
diff --git a/.config/awesome/ui/statusbar/panel/init.lua b/.config/awesome/ui/statusbar/panel/init.lua
index 31c2860..814a5a1 100644
--- a/.config/awesome/ui/statusbar/panel/init.lua
+++ b/.config/awesome/ui/statusbar/panel/init.lua
@@ -1,51 +1,61 @@
-local qvars = require "quarrel.vars"
+local C = require "ui.statusbar.consts"
+local qui = require "quarrel.ui"
local wibox = require "wibox"
local battery = require "ui.statusbar.panel.widgets.battery"
local calendar = require "ui.statusbar.panel.widgets.calendar"
local displays = require "ui.statusbar.panel.widgets.displays"
-local music = require "ui.statusbar.panel.widgets.music"
-local power_menu = require "ui.statusbar.panel.widgets.power_menu"
+local music = require "ui.statusbar.panel.widgets.mpris"
local wifi = require "ui.statusbar.panel.widgets.wifi"
local panel = wibox.widget {
- widget = wibox.container.margin,
- margins = {
- left = qvars.big_padding,
- },
+ widget = wibox.container.constraint,
+ strategy = "max",
+ width = C.EXPANDED_BAR_SIZE,
+ -- visible = false,
{
+ widget = wibox.container.background,
{
- widget = wibox.container.place,
- valign = "top",
+ widget = wibox.container.margin,
+ margins = {
+ left = qui.BIG_PADDING,
+ },
{
{
- displays.battery,
- displays.audio,
- displays.brightness,
- layout = wibox.layout.fixed.horizontal,
- spacing = qvars.padding,
+ widget = wibox.container.place,
+ valign = "top",
+ {
+ {
+ displays.battery,
+ displays.audio,
+ -- displays.brightness,
+ -- displays.brightness,
+ layout = wibox.layout.flex.horizontal,
+ spacing = qui.PADDING,
+ },
+ wifi,
+ -- battery,
+ music.widget,
+ calendar,
+ layout = wibox.layout.fixed.vertical,
+ spacing = qui.PADDING,
+ },
},
- wifi,
- -- battery,
- -- music,
- calendar,
- layout = wibox.layout.fixed.vertical,
- spacing = qvars.padding,
- },
- },
- {
- widget = wibox.container.background,
- {
- widget = wibox.widget.textbox,
- text = ":)",
+ -- {
+ -- widget = wibox.container.background,
+ -- {
+ -- widget = wibox.widget.textbox,
+ -- text = ":)",
+ -- },
+ -- },
+ -- {
+ -- widget = wibox.container.place,
+ -- valign = "bottom",
+ -- power_menu,
+ -- },
+ layout = wibox.layout.align.vertical,
},
},
- {
- widget = wibox.container.place,
- valign = "bottom",
- power_menu,
- },
- layout = wibox.layout.align.vertical,
},
}
diff --git a/.config/awesome/ui/statusbar/panel/widgets/battery.lua b/.config/awesome/ui/statusbar/panel/widgets/battery.lua
index 52685b7..52cf5da 100644
--- a/.config/awesome/ui/statusbar/panel/widgets/battery.lua
+++ b/.config/awesome/ui/statusbar/panel/widgets/battery.lua
@@ -1,6 +1,5 @@
local gears = require "gears"
local lit = require "lib.lit"
-local qbezier = require "quarrel.bezier"
local qui = require "quarrel.ui"
local wibox = require "wibox"
diff --git a/.config/awesome/ui/statusbar/panel/widgets/calendar.lua b/.config/awesome/ui/statusbar/panel/widgets/calendar.lua
index 8933543..ddbffc0 100644
--- a/.config/awesome/ui/statusbar/panel/widgets/calendar.lua
+++ b/.config/awesome/ui/statusbar/panel/widgets/calendar.lua
@@ -1,58 +1,123 @@
local awful = require "awful"
local phosphor = require "assets.phosphor"
local qbind = require "quarrel.bind"
+local qcolor = require "quarrel.color"
local qmarkup = require "quarrel.markup"
local qui = require "quarrel.ui"
-local qvars = require "quarrel.vars"
local wibox = require "wibox"
local weekday_map = { 7, 1, 2, 3, 4, 5, 6 }
local calendar = wibox.widget(qui.styled {
widget = wibox.container.background,
+ bg = qcolor.palette.bg.high,
{
widget = wibox.container.margin,
- margins = qvars.big_padding,
+ margins = qui.BORDER_WIDTH,
{
{
{
- widget = wibox.container.place,
- qui.icon {
- icon = phosphor.caret_left_bold,
+ layout = wibox.layout.grid,
+ forced_num_rows = 7,
+ forced_num_cols = 7,
+ -- spacing = (qui.PADDING) / 2,
+ border_width = {
+ inner = qui.BORDER_WIDTH,
+ outer = 0,
},
+ border_color = qcolor.palette.border.variant,
+ id = "grid",
},
{
- widget = wibox.container.place,
- {
- widget = wibox.widget.textbox,
- text = "January 2024",
- },
+ widget = wibox.container.background,
+ bg = qcolor.palette.border(),
+ forced_width = qui.BORDER_WIDTH,
},
+ layout = wibox.layout.fixed.horizontal,
+ },
+ {
+ widget = wibox.container.margin,
+ margins = qui.BIG_PADDING,
{
- widget = wibox.container.place,
- qui.icon {
- icon = phosphor.caret_right_bold,
+ {
+ widget = wibox.container.place,
+ fill_horizontal = true,
+ {
+ widget = wibox.widget.textbox,
+ text = "05\n25",
+ },
},
+ nil,
+ qui.styled {
+ widget = wibox.container.background,
+ bg = qcolor.palette.bg.highest,
+ {
+ {
+ widget = wibox.container.margin,
+ margins = qui.PADDING,
+ {
+ widget = wibox.container.place,
+ qui.icon {
+ icon = phosphor.caret_up_bold,
+ widget = {
+ forced_height = qui.CHAR_HEIGHT / 2,
+ forced_width = qui.CHAR_HEIGHT / 2,
+ },
+ },
+ },
+ },
+ {
+ widget = wibox.container.background,
+ bg = qcolor.palette.border.variant,
+ forced_height = qui.BORDER_WIDTH,
+ },
+ {
+ widget = wibox.container.margin,
+ margins = qui.PADDING,
+ {
+ widget = wibox.container.place,
+ qui.icon {
+ icon = phosphor.caret_down_bold,
+ widget = {
+ forced_height = qui.CHAR_HEIGHT / 2,
+ forced_width = qui.CHAR_HEIGHT / 2,
+ },
+ },
+ },
+ },
+ layout = wibox.layout.fixed.vertical,
+ -- spacing = qui.PADDING
+ },
+ },
+ layout = wibox.layout.align.vertical,
},
- layout = wibox.layout.align.horizontal,
- expand = "outside",
- },
- {
- layout = wibox.layout.grid,
- forced_num_rows = 7,
- forced_num_cols = 7,
- spacing = qvars.padding,
- id = "grid",
},
- layout = wibox.layout.fixed.vertical,
- spacing = qvars.big_padding,
+ layout = wibox.layout.fixed.horizontal,
},
},
})
+local grid = calendar:get_children_by_id("grid")[1]
+
-- Logic heavily inspired by https://github.com/Sinomor/dotfiles/blob/e409f9a84bf40daf1e39c0179ec749232ed827c9/home/.config/awesome/ui/control/moment/calendar.lua#L134-L173
function calendar:compute_grid(year, month)
- local calendar_table = { {}, {}, {}, {}, {}, {} }
+ local calendar_table = {
+ {
+ { "Mo", false, true },
+ { "Tu", false, true },
+ { "We", false, true },
+ { "Th", false, true },
+ { "Fr", false, true },
+ { "Sa", false, true },
+ { "Su", false, true },
+ },
+ {},
+ {},
+ {},
+ {},
+ {},
+ {},
+ }
local current = os.date("*t", os.time())
local first_day = os.date("*t", os.time { year = year, month = month, day = 1 })
local last_day = os.date("*t", os.time { year = year, month = month + 1, day = 0 })
@@ -68,14 +133,14 @@ function calendar:compute_grid(year, month)
for offset = prev_offset, 1, -1 do
local day = prev_days.day - offset + 1
table.insert(
- calendar_table[1],
+ calendar_table[2],
{ day, day == current.day and prev_month == current.month and prev_year == current.year, false }
)
end
end
do
- local row = 1
+ local row = 2
local weekday = weekday_map[first_day.wday]
for day = 1, days do
table.insert(
@@ -108,14 +173,22 @@ function calendar:compute_grid(year, month)
for i, row in ipairs(calendar_table) do
for j, col in ipairs(row) do
- local widget = self:get_children_by_id("grid")[1]:get_widgets_at(i + 1, j)[1]
- widget.widget.widget.text = col[1]
+ local widget = grid:get_widgets_at(i, j)[1]
+ widget.widget.widget.widget.text = col[1]
if col[2] then
- widget.bg = qvars.colors.yellow
- widget.fg = qvars.colors.bg
+ widget.bg = qcolor.palette.yellow()
+ widget.fg = qcolor.palette.bg()
+ elseif j % 6 == 0 or j % 7 == 0 then
+ widget.bg = col[3] and qcolor.palette.bg.highest or qcolor.palette.bg.high
else
- widget.bg = qvars.colors.bg
- widget.fg = col[3] and qvars.colors.fg or qvars.colors.dim.fg
+ widget.bg = col[3] and qcolor.palette.bg.high or qcolor.palette.bg()
+ end
+
+ if i == 1 then
+ widget.fg = qcolor.palette.fg()
+ elseif not col[2] then
+ -- widget.fg = col[3] and qcolor.palette.fg() or qcolor.palette.fg.low
+ widget.fg = qcolor.palette.fg.low
end
end
end
@@ -125,32 +198,31 @@ local cells = {}
local function cell(content)
local widget = wibox.widget {
widget = wibox.container.background,
- shape = qvars.shape,
+ bg = qcolor.palette.bg.high,
+ -- shape = qui.shape,
{
- widget = wibox.container.place,
- forced_height = qvars.char_height * 1.5,
- forced_width = qvars.char_height * 1.5,
+ widget = wibox.container.margin,
+ margins = qui.PADDING * 1.5,
{
- widget = wibox.widget.textbox,
- markup = content,
+ widget = wibox.container.place,
+ forced_height = qui.CHAR_HEIGHT,
+ forced_width = qui.CHAR_HEIGHT,
+ {
+ widget = wibox.widget.textbox,
+ markup = content,
+ },
},
},
}
table.insert(cells, widget)
end
-cell "Mo"
-cell "Tu"
-cell "We"
-cell "Th"
-cell "Fr"
-cell(qmarkup("Sa", { bold = true }))
-cell(qmarkup("Su", { bold = true }))
-
-for _ = 1, 42 do
+for _ = 1, 49 do
cell()
end
-calendar:get_children_by_id("grid")[1]:add(table.unpack(cells))
-calendar:compute_grid(2024, 1)
+grid:add(table.unpack(cells))
+local current_time = os.date "*t"
+calendar:compute_grid(current_time.year, current_time.month)
+grid:add_row_border(2, qui.BORDER_WIDTH, { color = qcolor.palette.border() })
return calendar
diff --git a/.config/awesome/ui/statusbar/panel/widgets/displays.lua b/.config/awesome/ui/statusbar/panel/widgets/displays.lua
index b52c986..bfb9113 100644
--- a/.config/awesome/ui/statusbar/panel/widgets/displays.lua
+++ b/.config/awesome/ui/statusbar/panel/widgets/displays.lua
@@ -1,37 +1,43 @@
-local backlight = require "services.backlight"
+-- local backlight = require "services.backlight"
local battery = require "services.battery"
local phosphor = require "assets.phosphor"
-local qmath = require "quarrel.math"
+-- local qmath = require "quarrel.math"
+local C = require "ui.statusbar.consts"
+local qcolor = require "quarrel.color"
local qui = require "quarrel.ui"
-local qvars = require "quarrel.vars"
local wibox = require "wibox"
local function create_display(icon, color)
+ -- the reason this is done is because ids can't be found with `get_children_by_id` across widget boundaries,
+ -- so we return it as a second value in order to manipulate the icon
+ local d_icon = qui.icon {
+ icon = icon,
+ color = color,
+ widget = {
+ id = "icon",
+ },
+ }
+
return wibox.widget(qui.styled {
widget = wibox.container.background,
+ bg = qcolor.palette.bg.high,
{
widget = wibox.container.margin,
- margins = qvars.big_padding,
+ margins = qui.BIG_PADDING,
{
widget = wibox.container.place,
{
{
widget = wibox.container.radialprogressbar,
max_value = 100,
- border_color = qvars.colors.black,
+ border_color = qcolor.palette.bg.lowest,
color = color,
- border_width = qvars.border_width * 2,
- forced_height = qvars.element_size * 4,
- forced_width = qvars.element_size * 4,
+ border_width = qui.BORDER_WIDTH * 2,
+ forced_height = C.ELEMENT_SIZE * 4,
+ forced_width = C.ELEMENT_SIZE * 4,
{
widget = wibox.container.place,
- qui.icon {
- icon = icon,
- color = color,
- widget = {
- id = "icon",
- },
- },
+ d_icon,
},
id = "indicator",
},
@@ -44,14 +50,15 @@ local function create_display(icon, color)
},
},
layout = wibox.layout.fixed.vertical,
- spacing = qvars.big_padding,
+ spacing = qui.BIG_PADDING,
},
},
},
- })
+ }),
+ d_icon
end
-local d_audio = create_display(phosphor.speaker_simple_high_fill, qvars.colors.fg)
+local d_audio, d_audio_icon = create_display(phosphor.speaker_simple_high_fill, qcolor.palette.fg())
awesome.connect_signal("services::audio", function(volume)
if not volume then
return
@@ -61,30 +68,33 @@ awesome.connect_signal("services::audio", function(volume)
end)
awesome.connect_signal("services::audio::icon", function(icon, color)
d_audio:get_children_by_id("indicator")[1].color = color
- d_audio:get_children_by_id("icon")[1].image = icon
- d_audio:get_children_by_id("icon")[1].stylesheet = qui.recolor(color)
+ d_audio_icon.image = icon
+ d_audio_icon.stylesheet = qui.recolor(color)
end)
-local d_battery = create_display(phosphor.battery_vertical_warning_fill, qvars.colors.red)
+local d_battery, d_battery_icon = create_display(phosphor.battery_vertical_warning_fill, qcolor.palette.red())
battery:connect_signal("level", function(_, level)
d_battery:get_children_by_id("indicator")[1].value = level
d_battery:get_children_by_id("text")[1].text = level .. "%"
end)
battery:connect_signal("icon", function(_, icon, color)
d_battery:get_children_by_id("indicator")[1].color = color
- d_battery:get_children_by_id("icon")[1].image = icon
- d_battery:get_children_by_id("icon")[1].stylesheet = qui.recolor(color)
+ d_battery_icon.image = icon
+ d_battery_icon.stylesheet = qui.recolor(color)
end)
-local d_brightness = create_display(phosphor.sun_fill, qvars.colors.fg)
-backlight:connect_signal("value", function(_, brightness)
- brightness = math.floor(qmath.translate_range(brightness, 0, 255, 0, 100))
- d_brightness:get_children_by_id("indicator")[1].value = brightness
- d_brightness:get_children_by_id("text")[1].text = brightness .. "%"
-end)
-backlight:connect_signal("icon", function(_, icon, color)
- d_brightness:get_children_by_id("icon")[1].image = icon
- d_brightness:get_children_by_id("icon")[1].stylesheet = qui.recolor(color)
-end)
+-- local d_brightness, d_brightness_icon = create_display(phosphor.sun_fill, qcolor.palette.fg())
+-- backlight:connect_signal("value", function(_, brightness)
+-- brightness = math.floor(qmath.translate_range(brightness, 0, 255, 0, 100))
+-- d_brightness:get_children_by_id("indicator")[1].value = brightness
+-- d_brightness:get_children_by_id("text")[1].text = brightness .. "%"
+-- end)
+-- backlight:connect_signal("icon", function(_, icon, color)
+-- d_brightness_icon.image = icon
+-- d_brightness_icon.stylesheet = qui.recolor(color)
+-- end)
-return { audio = d_audio, battery = d_battery, brightness = d_brightness }
+return {
+ audio = d_audio,
+ battery = d_battery, --[[brightness = d_brightness]]
+}
diff --git a/.config/awesome/ui/statusbar/panel/widgets/imagebox.lua b/.config/awesome/ui/statusbar/panel/widgets/imagebox.lua
deleted file mode 100644
index 5caadc5..0000000
--- a/.config/awesome/ui/statusbar/panel/widgets/imagebox.lua
+++ /dev/null
@@ -1,720 +0,0 @@
----------------------------------------------------------------------------
--- A widget to display an image.
---
--- The `wibox.widget.imagebox` is part of the Awesome WM's widget system
--- (see @{03-declarative-layout.md}).
---
--- This widget displays an image. The image can be a file,
--- a cairo image surface, or an rsvg handle object (see the
--- [image property](#image)).
---
--- Examples using a `wibox.widget.imagebox`:
--- ---
---
--- @DOC_wibox_widget_defaults_imagebox_EXAMPLE@
---
--- Alternatively, you can declare the `imagebox` widget using the
--- declarative pattern (both variants are strictly equivalent):
---
--- @DOC_wibox_widget_declarative-pattern_imagebox_EXAMPLE@
---
--- @author Uli Schlachter
--- @copyright 2010 Uli Schlachter
--- @widgetmod wibox.widget.imagebox
--- @supermodule wibox.widget.base
----------------------------------------------------------------------------
-
-local lgi = require "lgi"
-local cairo = lgi.cairo
-
-local base = require "wibox.widget.base"
-local gdebug = require "gears.debug"
-local gtable = require "gears.table"
-local surface = require "gears.surface"
-local setmetatable = setmetatable
-local type = type
-local math = math
-
-local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
-
--- Safe load for optional Rsvg module
-local Rsvg = nil
-do
- local success, err = pcall(function()
- Rsvg = lgi.Rsvg
- end)
- if not success then
- gdebug.print_warning(debug.traceback("Could not load Rsvg: " .. tostring(err)))
- end
-end
-
-local imagebox = { mt = {} }
-
-local rsvg_handle_cache = setmetatable({}, { __mode = "k" })
-
----Load rsvg handle form image file
--- @tparam string file Path to svg file.
--- @return Rsvg handle
--- @treturn table A table where cached data can be stored.
-local function load_rsvg_handle(file)
- if not Rsvg then
- return
- end
-
- local cache = (rsvg_handle_cache[file] or {})["handle"]
-
- if cache then
- return cache, rsvg_handle_cache[file]
- end
-
- local handle, err
-
- if file:match "<[?]?xml" or file:match "<svg" then
- handle, err = Rsvg.Handle.new_from_data(file)
- else
- handle, err = Rsvg.Handle.new_from_file(file)
- end
-
- if not err then
- rsvg_handle_cache[file] = rsvg_handle_cache[file] or {}
- rsvg_handle_cache[file]["handle"] = handle
- return handle, rsvg_handle_cache[file]
- end
-end
-
----Apply cairo surface for given imagebox widget
-local function set_surface(ib, surf)
- local is_surf_valid = surf.width > 0 and surf.height > 0
- if not is_surf_valid then
- return false
- end
-
- ib._private.default = { width = surf.width, height = surf.height }
- ib._private.handle = nil
- ib._private.image = surf
- return true
-end
-
----Apply RsvgHandle for given imagebox widget
-local function set_handle(ib, handle, cache)
- local dim = handle:get_dimensions()
- local is_handle_valid = dim.width > 0 and dim.height > 0
- if not is_handle_valid then
- return false
- end
-
- ib._private.default = { width = dim.width, height = dim.height }
- ib._private.handle = handle
- ib._private.cache = cache
- ib._private.image = nil
-
- return true
-end
-
----Try to load some image object from file then apply it to imagebox.
----@tparam table ib Imagebox
----@tparam string file Image file name
----@tparam function image_loader Function to load image object from file
----@tparam function image_setter Function to set image object to imagebox
----@treturn boolean True if image was successfully applied
-local function load_and_apply(ib, file, image_loader, image_setter)
- local image_applied
- local object, cache = image_loader(file)
-
- if object then
- image_applied = image_setter(ib, object, cache)
- end
- return image_applied
-end
-
----Update the cached size depending on the stylesheet and dpi.
---
--- It's necessary because a single RSVG handle can be used by
--- many imageboxes. So DPI and Stylesheet need to be set each time.
-local function update_dpi(self, ctx)
- if not self._private.handle then
- return
- end
-
- local dpi = self._private.auto_dpi and ctx.dpi or self._private.dpi or nil
-
- local need_dpi = dpi and self._private.last_dpi ~= dpi
-
- local need_style = self._private.handle.set_stylesheet and self._private.stylesheet
-
- local old_size = self._private.default and self._private.default.width
-
- if dpi and dpi ~= self._private.cache.dpi then
- if type(dpi) == "table" then
- self._private.handle:set_dpi_x_y(dpi.x, dpi.y)
- else
- self._private.handle:set_dpi(dpi)
- end
- end
-
- if need_style and self._private.cache.stylesheet ~= self._private.stylesheet then
- self._private.handle:set_stylesheet(self._private.stylesheet)
- end
-
- -- Reload the size.
- if need_dpi or (need_style and self._private.stylesheet ~= self._private.last_stylesheet) then
- set_handle(self, self._private.handle, self._private.cache)
- end
-
- self._private.last_dpi = dpi
- self._private.cache.dpi = dpi
- self._private.last_stylesheet = self._private.stylesheet
- self._private.cache.stylesheet = self._private.stylesheet
-
- -- This can happen in the constructor when `dpi` is set after `image`.
- if old_size and old_size ~= self._private.default.width then
- self:emit_signal "widget::redraw_needed"
- self:emit_signal "widget::layout_changed"
- end
-end
-
--- Draw an imagebox with the given cairo context in the given geometry.
-function imagebox:draw(ctx, cr, width, height)
- if width == 0 or height == 0 or not self._private.default then
- return
- end
-
- -- For valign = "top" and halign = "left"
- local translate = {
- x = 0,
- y = 0,
- }
-
- update_dpi(self, ctx)
-
- local w, h = self._private.default.width, self._private.default.height
-
- if self._private.resize then
- -- That's for the "fit" policy.
- local aspects = {
- w = width / w,
- h = height / h,
- }
-
- local policy = {
- w = self._private.horizontal_fit_policy or "auto",
- h = self._private.vertical_fit_policy or "auto",
- }
-
- for _, aspect in ipairs { "w", "h" } do
- if self._private.upscale == false and (w < width and h < height) then
- aspects[aspect] = 1
- elseif self._private.downscale == false and (w >= width and h >= height) then
- aspects[aspect] = 1
- elseif policy[aspect] == "none" then
- aspects[aspect] = 1
- elseif policy[aspect] == "auto" then
- aspects[aspect] = math.min(width / w, height / h)
- elseif policy[aspect] == "cover" then
- aspects[aspect] = math.max(width / w, height / h)
- end
- end
-
- if self._private.halign == "center" then
- translate.x = math.floor((width - w * aspects.w) / 2)
- elseif self._private.halign == "right" then
- translate.x = math.floor(width - (w * aspects.w))
- end
-
- if self._private.valign == "center" then
- translate.y = math.floor((height - h * aspects.h) / 2)
- elseif self._private.valign == "bottom" then
- translate.y = math.floor(height - (h * aspects.h))
- end
-
- cr:translate(translate.x, translate.y)
-
- -- Before using the scale, make sure it is below the threshold.
- local threshold, max_factor = self._private.max_scaling_factor, math.max(aspects.w, aspects.h)
-
- if threshold and threshold > 0 and threshold < max_factor then
- aspects.w = (aspects.w * threshold) / max_factor
- aspects.h = (aspects.h * threshold) / max_factor
- end
-
- -- Set the clip
- if self._private.clip_shape then
- cr:clip(self._private.clip_shape(cr, w * aspects.w, h * aspects.h, unpack(self._private.clip_args)))
- end
-
- cr:scale(aspects.w, aspects.h)
- else
- if self._private.halign == "center" then
- translate.x = math.floor((width - w) / 2)
- elseif self._private.halign == "right" then
- translate.x = math.floor(width - w)
- end
-
- if self._private.valign == "center" then
- translate.y = math.floor((height - h) / 2)
- elseif self._private.valign == "bottom" then
- translate.y = math.floor(height - h)
- end
-
- cr:translate(translate.x, translate.y)
-
- -- Set the clip
- if self._private.clip_shape then
- cr:clip(self._private.clip_shape(cr, w, h, unpack(self._private.clip_args)))
- end
- end
-
- if self._private.handle then
- self._private.handle:render_cairo(cr)
- else
- cr:set_source_surface(self._private.image, 0, 0)
-
- local filter = self._private.scaling_quality
-
- if filter then
- cr:get_source():set_filter(cairo.Filter[filter:upper()])
- end
-
- cr:paint()
- end
-end
-
--- Fit the imagebox into the given geometry
-function imagebox:fit(ctx, width, height)
- if not self._private.default then
- return 0, 0
- end
-
- update_dpi(self, ctx)
-
- local w, h = self._private.default.width, self._private.default.height
-
- if w <= width and h <= height and self._private.upscale == false then
- return w, h
- end
-
- if (w < width or h < height) and self._private.downscale == false then
- return w, h
- end
-
- if self._private.resize or w > width or h > height then
- local aspect = math.min(width / w, height / h)
- return w * aspect, h * aspect
- end
-
- return w, h
-end
-
---- The image rendered by the `imagebox`.
---
--- @property image
--- @tparam[opt=nil] image|nil image
--- @propemits false false
-
---- Set the `imagebox` image.
---
--- The image can be a file, a cairo image surface, or an rsvg handle object
--- (see the [image property](#image)).
--- @method set_image
--- @hidden
--- @tparam image image The image to render.
--- @treturn boolean `true` on success, `false` if the image cannot be used.
--- @usage my_imagebox:set_image(beautiful.awesome_icon)
--- @usage my_imagebox:set_image('/usr/share/icons/theme/my_icon.png')
--- @see image
-function imagebox:set_image(image)
- local setup_succeed
-
- -- Keep the original to prevent the cache from being GCed.
- self._private.original_image = image
-
- if type(image) == "userdata" and not (Rsvg and Rsvg.Handle:is_type_of(image)) then
- -- This function is not documented to handle userdata objects, but
- -- historically it did, and it did by just assuming they refer to a
- -- cairo surface.
- image = surface.load(image)
- end
-
- if type(image) == "string" then
- -- try to load rsvg handle from file
- setup_succeed = load_and_apply(self, image, load_rsvg_handle, set_handle)
-
- if not setup_succeed then
- -- rsvg handle failed, try to load cairo surface with pixbuf
- setup_succeed = load_and_apply(self, image, surface.load, set_surface)
- end
- elseif Rsvg and Rsvg.Handle:is_type_of(image) then
- -- try to apply given rsvg handle
- rsvg_handle_cache[image] = rsvg_handle_cache[image] or {}
- setup_succeed = set_handle(self, image, rsvg_handle_cache[image])
- elseif cairo.Surface:is_type_of(image) then
- -- try to apply given cairo surface
- setup_succeed = set_surface(self, image)
- elseif not image then
- -- nil as argument mean full imagebox reset
- setup_succeed = true
- self._private.handle = nil
- self._private.image = nil
- self._private.default = nil
- end
-
- if not setup_succeed then
- return false
- end
-
- self:emit_signal "widget::redraw_needed"
- self:emit_signal "widget::layout_changed"
- self:emit_signal "property::image"
- return true
-end
-
---- Set a clip shape for this imagebox.
---
--- A clip shape defines an area and dimension to which the content should be
--- trimmed.
---
--- @DOC_wibox_widget_imagebox_clip_shape_EXAMPLE@
---
--- @property clip_shape
--- @tparam[opt=gears.shape.rectangle] shape clip_shape A `gears.shape` compatible shape function.
--- @propemits true false
--- @see gears.shape
-
---- Set a clip shape for this imagebox.
---
--- A clip shape defines an area and dimensions to which the content should be
--- trimmed.
---
--- Additional parameters will be passed to the clip shape function.
---
--- @tparam function|gears.shape clip_shape A `gears_shape` compatible shape function.
--- @method set_clip_shape
--- @hidden
--- @see gears.shape
--- @see clip_shape
-function imagebox:set_clip_shape(clip_shape, ...)
- self._private.clip_shape = clip_shape
- self._private.clip_args = { ... }
- self:emit_signal "widget::redraw_needed"
- self:emit_signal("property::clip_shape", clip_shape)
-end
-
---- Should the image be resized to fit into the available space?
---
--- Note that `upscale` and `downscale` can affect the value of `resize`.
--- If conflicting values are passed to the constructor, then the result
--- is undefined.
---
--- @DOC_wibox_widget_imagebox_resize_EXAMPLE@
--- @property resize
--- @propemits true false
--- @tparam[opt=true] boolean resize
-
---- Allow the image to be upscaled (made bigger).
---
--- Note that `upscale` and `downscale` can affect the value of `resize`.
--- If conflicting values are passed to the constructor, then the result
--- is undefined.
---
--- @DOC_wibox_widget_imagebox_upscale_EXAMPLE@
--- @property upscale
--- @tparam[opt=self.resize] boolean upscale
--- @see downscale
--- @see resize
-
---- Allow the image to be downscaled (made smaller).
---
--- Note that `upscale` and `downscale` can affect the value of `resize`.
--- If conflicting values are passed to the constructor, then the result
--- is undefined.
---
--- @DOC_wibox_widget_imagebox_downscale_EXAMPLE@
--- @property downscale
--- @tparam[opt=self.resize] boolean downscale
--- @see upscale
--- @see resize
-
---- Set the SVG CSS stylesheet.
---
--- If the image is an SVG (vector graphics), this property allows to set
--- a CSS stylesheet. It can be used to set colors and much more.
---
--- Note that this property is a string, not a path. If the stylesheet is
--- stored on disk, read the content first.
---
---@DOC_wibox_widget_imagebox_stylesheet_EXAMPLE@
---
--- @property stylesheet
--- @tparam[opt=""] string stylesheet
--- @propemits true false
-
---- Set the SVG DPI (dot per inch).
---
--- Force a specific DPI when rendering the `.svg`. For other file formats,
--- this does nothing.
---
--- It can either be a number of a table containing the `x` and `y` keys.
---
--- Please note that DPI and `resize` can "fight" each other and end up
--- making the image smaller instead of bigger.
---
---@DOC_wibox_widget_imagebox_dpi_EXAMPLE@
---
--- @property dpi
--- @tparam[opt=96] number|table dpi
--- @negativeallowed false
--- @propemits true false
--- @see auto_dpi
-
---- Use the object DPI when rendering the SVG.
---
--- By default, the SVG are interpreted as-is. When this property is set,
--- the screen DPI will be passed to the SVG renderer. Depending on which
--- tool was used to create the `.svg`, this may do nothing at all. However,
--- for example, if the `.svg` uses `<text>` elements and doesn't have an
--- hardcoded stylesheet, the result will differ.
---
--- @property auto_dpi
--- @tparam[opt=false] boolean auto_dpi
--- @propemits true false
--- @see dpi
-
-for _, prop in ipairs { "stylesheet", "dpi", "auto_dpi" } do
- imagebox["set_" .. prop] = function(self, value)
- -- It will be set in :fit and :draw. The handle is shared
- -- by multiple imagebox, so it cannot be set just once.
- self._private[prop] = value
-
- self:emit_signal "widget::redraw_needed"
- self:emit_signal "widget::layout_changed"
- self:emit_signal("property::" .. prop)
- end
-end
-
-function imagebox:set_resize(allowed)
- self._private.resize = allowed
-
- if allowed then
- self._private.downscale = true
- self._private.upscale = true
- self:emit_signal("property::downscale", allowed)
- self:emit_signal("property::upscale", allowed)
- end
-
- self:emit_signal "widget::redraw_needed"
- self:emit_signal "widget::layout_changed"
- self:emit_signal("property::resize", allowed)
-end
-
-for _, prop in ipairs { "downscale", "upscale" } do
- imagebox["set_" .. prop] = function(self, allowed)
- self._private[prop] = allowed
-
- if self._private.resize ~= (self._private.upscale or self._private.downscale) then
- self._private.resize = self._private.upscale or self._private.downscale
- self:emit_signal("property::resize", self._private.resize)
- end
-
- self:emit_signal "widget::redraw_needed"
- self:emit_signal "widget::layout_changed"
- self:emit_signal("property::" .. prop, allowed)
- end
-end
-
---- Set the horizontal fit policy.
---
--- Here is the result for a 22x32 image:
---
--- @DOC_wibox_widget_imagebox_horizontal_fit_policy_EXAMPLE@
---
--- @property horizontal_fit_policy
--- @tparam[opt="auto"] string horizontal_fit_policy
--- @propertyvalue "auto" Honor the `resize` variable and preserve the aspect ratio.
--- @propertyvalue "none" Do not resize at all.
--- @propertyvalue "fit" Resize to the widget width.
--- @propertyvalue "cover" Resize to fill widget and preserve the aspect ratio.
--- @propemits true false
--- @see vertical_fit_policy
--- @see resize
-
---- Set the vertical fit policy.
---
--- Here is the result for a 32x22 image:
---
--- @DOC_wibox_widget_imagebox_vertical_fit_policy_EXAMPLE@
---
--- @property vertical_fit_policy
--- @tparam[opt="auto"] string vertical_fit_policy
--- @propertyvalue "auto" Honor the `resize` variable and preserve the aspect ratio.
--- @propertyvalue "none" Do not resize at all.
--- @propertyvalue "fit" Resize to the widget height.
--- @propertyvalue "cover" Resize to fill widget and preserve the aspect ratio.
--- @propemits true false
--- @see horizontal_fit_policy
--- @see resize
-
---- The vertical alignment.
---
--- @DOC_wibox_widget_imagebox_valign_EXAMPLE@
---
--- @property valign
--- @tparam[opt="center"] string valign
--- @propertyvalue "top"
--- @propertyvalue "center"
--- @propertyvalue "bottom"
--- @propemits true false
--- @see wibox.container.place
--- @see halign
-
---- The horizontal alignment.
---
--- @DOC_wibox_widget_imagebox_halign_EXAMPLE@
---
--- @property halign
--- @tparam[opt="center"] string halign
--- @propertyvalue "left"
--- @propertyvalue "center"
--- @propertyvalue "right"
--- @propemits true false
--- @see wibox.container.place
--- @see valign
-
---- The maximum scaling factor.
---
--- If an image is scaled too much, it gets very blurry. This
--- property allows to limit the scaling.
--- Use the properties `valign` and `halign` to control how the image will be
--- aligned.
---
--- In the example below, the original size is 22x22
---
--- @DOC_wibox_widget_imagebox_max_scaling_factor_EXAMPLE@
---
--- @property max_scaling_factor
--- @tparam[opt=0] number max_scaling_factor Use `0` for "no limit".
--- @negativeallowed false
--- @propemits true false
--- @see valign
--- @see halign
--- @see scaling_quality
-
---- Set the scaling aligorithm.
---
--- Depending on how the image is used, what is the "correct" way to
--- scale can change. For example, upscaling a pixel art image should
--- not make it blurry. However, scaling up a photo should not make it
--- blocky.
---
---<table class='widget_list' border=1>
--- <tr style='font-weight: bold;'>
--- <th align='center'>Value</th>
--- <th align='center'>Description</th>
--- </tr>
--- <tr><td>fast</td><td>A high-performance filter</td></tr>
--- <tr><td>good</td><td>A reasonable-performance filter</td></tr>
--- <tr><td>best</td><td>The highest-quality available</td></tr>
--- <tr><td>nearest</td><td>Nearest-neighbor filtering (blocky)</td></tr>
--- <tr><td>bilinear</td><td>Linear interpolation in two dimensions</td></tr>
---</table>
---
--- The image used in the example below has a resolution of 32x22 and is
--- intentionally blocky to highlight the difference.
--- It is zoomed by a factor of 3.
---
--- @DOC_wibox_widget_imagebox_scaling_quality_EXAMPLE@
---
--- @property scaling_quality
--- @tparam[opt="good"] string scaling_quality
--- @propertyvalue "fast" A high-performance filter.
--- @propertyvalue "good" A reasonable-performance filter.
--- @propertyvalue "best" The highest-quality available.
--- @propertyvalue "nearest" Nearest-neighbor filtering (blocky).
--- @propertyvalue "bilinear" Linear interpolation in two dimensions.
--- @propemits true false
--- @see resize
--- @see horizontal_fit_policy
--- @see vertical_fit_policy
--- @see max_scaling_factor
-
-local defaults = {
- halign = "left",
- valign = "top",
- horizontal_fit_policy = "auto",
- vertical_fit_policy = "auto",
- max_scaling_factor = 0,
- scaling_quality = "good",
-}
-
-local function get_default(prop, value)
- if value == nil then
- return defaults[prop]
- end
-
- return value
-end
-
-for prop in pairs(defaults) do
- imagebox["set_" .. prop] = function(self, value)
- if value == self._private[prop] then
- return
- end
-
- self._private[prop] = get_default(prop, value)
- self:emit_signal "widget::redraw_needed"
- self:emit_signal("property::" .. prop, self._private[prop])
- end
-
- imagebox["get_" .. prop] = function(self)
- if self._private[prop] == nil then
- return defaults[prop]
- end
-
- return self._private[prop]
- end
-end
-
---- Returns a new `wibox.widget.imagebox` instance.
---
--- This is the constructor of `wibox.widget.imagebox`. It creates a new
--- instance of imagebox widget.
---
--- Alternatively, the declarative layout syntax can handle
--- `wibox.widget.imagebox` instanciation.
---
--- The image can be a file, a cairo image surface, or an rsvg handle object
--- (see the [image property](#image)).
---
--- Any additional arguments will be passed to the clip shape function.
--- @tparam[opt] image image The image to display (may be `nil`).
--- @tparam[opt] boolean resize_allowed If `false`, the image will be
--- clipped, else it will be resized to fit into the available space.
--- @tparam[opt] function clip_shape A `gears.shape` compatible function.
--- @treturn wibox.widget.imagebox A new `wibox.widget.imagebox` widget instance.
--- @constructorfct wibox.widget.imagebox
-local function new(image, resize_allowed, clip_shape, ...)
- local ret = base.make_widget(nil, nil, { enable_properties = true })
-
- gtable.crush(ret, imagebox, true)
- ret._private.resize = true
-
- if image then
- ret:set_image(image)
- end
-
- if resize_allowed ~= nil then
- ret.resize = resize_allowed
- end
-
- ret._private.clip_shape = clip_shape
- ret._private.clip_args = { ... }
-
- return ret
-end
-
-function imagebox.mt:__call(...)
- return new(...)
-end
-
-return setmetatable(imagebox, imagebox.mt)
-
--- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/.config/awesome/ui/statusbar/panel/widgets/mpris.lua b/.config/awesome/ui/statusbar/panel/widgets/mpris.lua
new file mode 100644
index 0000000..c27bf66
--- /dev/null
+++ b/.config/awesome/ui/statusbar/panel/widgets/mpris.lua
@@ -0,0 +1,429 @@
+local custom = require "assets.custom"
+local gdebug = require "gears.debug"
+local phosphor = require "assets.phosphor"
+local playerctl = require "services.playerctl"
+local qcolor = require "quarrel.color"
+local qpersistent = require "quarrel.persistent"
+local qui = require "quarrel.ui"
+local qvars = require "quarrel.vars"
+local simpleicons = require "assets.simpleicons"
+local wibox = require "wibox"
+
+local M = {}
+
+local client_icons = { -- this is used to map the mpris clients to icons
+ firefox = simpleicons.librewolf, -- librewolf uses the same player id as firefox
+ spotify = simpleicons.spotify,
+ mpd = custom.vinyl_record_fill,
+ __generic = phosphor.waveform_fill, -- used for any client not in the map
+}
+
+local DEFAULTS = {
+ ---@type string
+ title = "Nothing's playing",
+ artist = {},
+ ---@type string
+ album = "",
+ progresstext = {
+ ---@type string
+ position = "~",
+ ---@type string
+ length = "~",
+ },
+ ---@type number
+ position = 0,
+ ---@type number
+ length = math.huge,
+ client_icon = client_icons.__generic,
+ art = phosphor.music_notes_fill,
+}
+
+local bg_icon = qui.icon {
+ icon = DEFAULTS.client_icon,
+ widget = {
+ forced_width = 0,
+ forced_height = 0,
+ },
+ color = qcolor.palette.bg.highest,
+}
+
+local function to_hms(time)
+ local format = "%i:%02i"
+
+ local h = math.floor(time / 60 ^ 2)
+ local m = math.floor((time / 60) % 60)
+ local s = math.floor(time % 60)
+
+ if h > 0 then
+ format = "%i:" .. format
+ return string.format(format, h, m, s)
+ end
+
+ return string.format(format, m, s)
+end
+
+local function is_empty(str)
+ return (str == nil or #str == 0)
+end
+
+--- mirror of playerctl:list()
+--- why bother mirroring? simple: we need to track changes to the player list
+--- but vanished and appeared simply don't give us an old version to compare with
+---@type Playerctl.data[]
+local players = {}
+
+---@type number
+M.active_player_index = qpersistent.get "active_player_index" --[[@as number]]
+if not M.active_player_index then
+ M.active_player_index = 1
+ gdebug.print_error "failed to get active_player_index from qpersistent, falling back..."
+ qpersistent.store("active_player_index", M.active_player_index)
+end
+
+--- the reason we do this instead of some other hack is this is the only way to draw the icons *without* resizing the mpris container
+--- this happens because in the panel, height is unlimited, and imagebox grows until a hard limit
+---@class wibox.widget.base
+local client_background = wibox.container.background()
+
+function client_background:before_draw_children(_, cr, width, height)
+ cr:save()
+ cr:translate(width - (height / 1.25), -(height * 0.125))
+ ---@diagnostic disable-next-line: missing-parameter
+ wibox.widget.draw_to_cairo_context(bg_icon, cr, height * 1.25, height * 1.25)
+ cr:restore()
+end
+
+M.widget = wibox.widget(qui.styled {
+ widget = wibox.container.background,
+ bg = qcolor.palette.bg.high,
+ {
+ widget = client_background,
+ {
+ widget = wibox.container.margin,
+ margins = qui.BIG_PADDING,
+ {
+ nil,
+ {
+ {
+ {
+ widget = wibox.container.background,
+ bg = qcolor.palette.bg(),
+ shape = qui.shape,
+ {
+ widget = wibox.widget.imagebox,
+ image = DEFAULTS.art,
+ forced_height = qui.CHAR_HEIGHT * 5,
+ forced_width = qui.CHAR_HEIGHT * 5,
+ valign = "center",
+ halign = "center",
+ stylesheet = qui.recolor(qcolor.palette.bg.highest),
+ id = "cover",
+ },
+ },
+ {
+ widget = wibox.container.margin,
+ right = qui.BIG_PADDING,
+ left = qui.BIG_PADDING,
+ {
+ {
+ widget = wibox.container.constraint,
+ height = qui.CHAR_HEIGHT * 2.5,
+ strategy = "max",
+ {
+ widget = wibox.widget.textbox,
+ text = DEFAULTS.title, -- Song
+ id = "song",
+ valign = "top",
+ },
+ },
+ {
+ widget = wibox.container.constraint,
+ height = qui.CHAR_HEIGHT * 2.5,
+ strategy = "max",
+ {
+ widget = wibox.container.background,
+ fg = qcolor.palette.fg.low,
+ {
+ widget = wibox.widget.textbox,
+ text = DEFAULTS.artist_album, -- Artist - Album Name
+ id = "artist_album",
+ valign = "top",
+ },
+ },
+ },
+ layout = wibox.layout.fixed.vertical,
+ },
+ },
+ layout = wibox.layout.fixed.horizontal,
+ },
+ nil,
+ {
+ widget = wibox.container.margin,
+ top = qui.BIG_PADDING,
+ right = qui.BIG_PADDING,
+ {
+ {
+ widget = wibox.widget.textbox,
+ text = DEFAULTS.progresstext.position .. " / " .. DEFAULTS.progresstext.length, -- position / length
+ id = "progresstext",
+ },
+ {
+ widget = wibox.container.place,
+ {
+ widget = wibox.widget.progressbar,
+ forced_height = qui.PADDING,
+ color = qcolor.palette.yellow(),
+ value = DEFAULTS.position,
+ max_value = DEFAULTS.length,
+ background_color = qcolor.palette.bg.lowest,
+ bar_shape = qui.shape,
+ shape = qui.shape,
+ id = "progressbar",
+ },
+ },
+ layout = wibox.layout.fixed.horizontal,
+ spacing = qui.BIG_PADDING,
+ },
+ },
+ layout = wibox.layout.align.vertical,
+ },
+ {
+ layout = wibox.layout.flex.vertical,
+ spacing = qui.BIG_PADDING,
+ id = "client_list",
+ },
+ layout = wibox.layout.align.horizontal,
+ },
+ },
+ },
+})
+
+local layout = M.widget:get_children_by_id("client_list")[1] --[[@as wibox.layout.flex]]
+local progressbar = M.widget:get_children_by_id("progressbar")[1] --[[@as wibox.widget.progressbar]]
+local progresstext = M.widget:get_children_by_id("progresstext")[1] --[[@as wibox.widget.textbox]]
+local song = M.widget:get_children_by_id("song")[1] --[[@as wibox.widget.textbox]]
+local artist_album = M.widget:get_children_by_id("artist_album")[1] --[[@as wibox.widget.textbox]]
+local cover = M.widget:get_children_by_id("cover")[1] --[[@as wibox.widget.imagebox]]
+
+local function mirror_player_list()
+ players = playerctl:list()
+end
+
+mirror_player_list()
+
+local function handle_metadata(_, player)
+ local title, album, artist, art
+ if player ~= playerctl:list()[M.active_player_index] then
+ return
+ end
+ if player then
+ if player.metadata.title then
+ title = player.metadata.title
+ else
+ title = DEFAULTS.title
+ end
+
+ if player.metadata.album then
+ album = player.metadata.album
+ else
+ album = DEFAULTS.album
+ end
+
+ if player.metadata.artist then
+ artist = player.metadata.artist
+ else
+ artist = DEFAULTS.artist
+ end
+
+ if player.metadata.art and player.metadata.art:match "^file://" then
+ art = player.metadata.art:gsub("^file://", "")
+ else
+ art = DEFAULTS.art
+ end
+ else
+ title = DEFAULTS.title
+ album = DEFAULTS.album
+ artist = DEFAULTS.artist
+ art = DEFAULTS.art
+ end
+
+ artist_album.text = table.concat(artist, ", ") .. ((is_empty(artist) or is_empty(album)) and "" or " - ") .. album
+ song.text = title
+ ---@diagnostic disable-next-line:inject-field
+ cover.image = art
+end
+
+local function handle_position(_, player)
+ if player ~= playerctl:list()[M.active_player_index] then
+ return
+ end
+ local position, length
+ local content = ""
+
+ if player then
+ if player.position then
+ position = player.position / playerctl.unit
+ content = content .. to_hms(position)
+ else
+ position = DEFAULTS.position
+ content = content .. DEFAULTS.progresstext.position
+ end
+
+ content = content .. " / "
+
+ if player.metadata.length then
+ length = player.metadata.length / playerctl.unit
+ content = content .. to_hms(length)
+ else
+ length = DEFAULTS.length
+ content = content .. DEFAULTS.progresstext.length
+ end
+ else
+ position = DEFAULTS.position
+ length = DEFAULTS.length
+ content = DEFAULTS.progresstext.position .. " / " .. DEFAULTS.progresstext.length
+ end
+
+ progresstext.text = content
+ ---@diagnostic disable-next-line:inject-field
+ progressbar.value = position
+ ---@diagnostic disable-next-line:inject-field
+ progressbar.max_value = length
+end
+
+playerctl:connect_signal("player::metadata", handle_metadata)
+playerctl:connect_signal("player::position", handle_position)
+
+local function update_player(player)
+ handle_metadata(nil, player)
+ handle_position(nil, player)
+ qpersistent.store("active_player_index", M.active_player_index)
+
+ local client_icon = client_icons[(player or { name = "__generic" }).name]
+ bg_icon.image = client_icon or client_icons.__generic
+ client_background:emit_signal "widget::redraw_needed"
+
+ for i, child in ipairs(layout.children) do
+ ---@diagnostic disable-next-line:undefined-field
+ child:index_handler(i)
+ end
+end
+
+function M.next_player()
+ local players_length = #layout.children
+ if players_length == 0 then
+ return
+ end
+
+ if M.active_player_index + 1 > players_length then
+ M.active_player_index = 1
+ else
+ M.active_player_index = M.active_player_index + 1
+ end
+
+ update_player(playerctl:list()[M.active_player_index])
+end
+
+function M.previous_player()
+ local players_length = #layout.children
+ if players_length == 0 then
+ return
+ end
+
+ if M.active_player_index - 1 < 1 then
+ M.active_player_index = players_length
+ else
+ M.active_player_index = M.active_player_index - 1
+ end
+
+ update_player(playerctl:list()[M.active_player_index])
+end
+
+---@param diff_player Playerctl.data
+local function recalculate_active_player(diff_player, vanished)
+ if type(diff_player) ~= "table" then
+ return
+ end
+ if #layout.children == 0 then
+ M.active_player_index = 1
+ update_player()
+ return
+ end
+
+ local active_player = players[M.active_player_index]
+ if not active_player then -- we're recovering from a state with no players
+ update_player(diff_player)
+ return
+ end
+
+ if diff_player.instance == active_player.instance and vanished then -- active player vanished; fall back to previous player
+ M.previous_player()
+ else -- non-active player appeared/vanished; try to find active player
+ for i, p in ipairs(playerctl:list()) do
+ if p.instance == active_player.instance then
+ M.active_player_index = i
+ update_player(p)
+ return
+ end
+ end
+
+ gdebug.print_warning(
+ "failed to find active player:\n " .. gdebug.dump_return(active_player, nil, 2):gsub("\n", "\n ")
+ )
+ M.active_player_index = 1
+ update_player(playerctl:list()[M.active_player_index])
+ end
+end
+
+local function register_player(player)
+ local widget = wibox.widget {
+ widget = wibox.container.constraint,
+ width = qui.PADDING,
+ strategy = "min",
+ {
+ widget = wibox.container.background,
+ shape = qui.shape,
+ bg = qcolor.palette.bg.lowest,
+ },
+ index_handler = function(self, index)
+ if M.active_player_index == index then
+ self.widget.bg = qcolor.palette.yellow()
+ else
+ self.widget.bg = qcolor.palette.bg.lowest
+ end
+ end,
+ }
+
+ ---@diagnostic disable-next-line:undefined-field
+ layout:add(widget)
+
+ recalculate_active_player(player, false)
+ mirror_player_list()
+end
+
+local function unregister_player(player)
+ ---@diagnostic disable-next-line:undefined-field
+ layout:remove(#layout.children)
+ recalculate_active_player(player, true)
+ mirror_player_list()
+end
+
+for _, player in ipairs(playerctl:list()) do
+ register_player(player)
+end
+
+-- recover state
+---@diagnostic disable-next-line:undefined-field
+local last_active_player = playerctl:list()[M.active_player_index]
+
+update_player(last_active_player)
+
+playerctl:connect_signal("player::appeared", function(_, player)
+ register_player(player)
+end)
+
+playerctl:connect_signal("player::vanished", function(_, player)
+ unregister_player(player)
+end)
+
+return M
diff --git a/.config/awesome/ui/statusbar/panel/widgets/music.lua b/.config/awesome/ui/statusbar/panel/widgets/music.old.lua
index eea7335..5631f66 100644
--- a/.config/awesome/ui/statusbar/panel/widgets/music.lua
+++ b/.config/awesome/ui/statusbar/panel/widgets/music.old.lua
@@ -1,3 +1,4 @@
+-- TODO: update to the new color format
local qnative = require "quarrel.native"
local qui = require "quarrel.ui"
local qvars = require "quarrel.vars"
@@ -12,7 +13,7 @@ local w_title = wibox.widget {
local w_artist = wibox.widget {
widget = wibox.container.background,
- fg = qvars.colors.dim.fg,
+ fg = qcolor.palette.dim.fg,
{
widget = wibox.widget.textbox,
text = "",
@@ -23,35 +24,35 @@ local w_progress_bar = wibox.widget {
widget = wibox.widget.progressbar,
max_value = 0,
value = 0,
- forced_height = qvars.char_height / 2,
+ forced_height = qui.CHAR_HEIGHT / 2,
forced_width = qvars.expanded_bar_size
- - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2)
- - (qvars.char_height / 1.25 + qvars.padding) * 3,
- color = qvars.colors.yellow,
- background_color = qvars.colors.black,
- shape = qvars.shape,
+ - (qui.BIG_PADDING + qui.BIG_PADDING * 2 + qui.PADDING * 2)
+ - (qui.CHAR_HEIGHT / 1.25 + qui.PADDING) * 3,
+ color = qcolor.palette.yellow,
+ background_color = qcolor.palette.black,
+ shape = qui.shape,
}
local music = wibox.widget(qui.styled {
widget = wibox.container.background,
{
widget = wibox.container.margin,
- margins = qvars.big_padding,
+ margins = qui.BIG_PADDING,
{
{
{
widget = wibox.container.background,
- bg = qvars.colors.bg,
- shape = qvars.shape,
+ bg = qcolor.palette.bg,
+ shape = qui.shape,
{
widget = wibox.container.margin,
- margins = qvars.padding,
+ margins = qui.PADDING,
{
{
widget = wibox.container.constraint,
width = qvars.expanded_bar_size
- - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2),
- height = qvars.char_height,
+ - (qui.BIG_PADDING + qui.BIG_PADDING * 2 + qui.PADDING * 2),
+ height = qui.CHAR_HEIGHT,
{
widget = wibox.container.scroll.horizontal,
speed = 50,
@@ -62,8 +63,8 @@ local music = wibox.widget(qui.styled {
{
widget = wibox.container.constraint,
width = qvars.expanded_bar_size
- - (qvars.big_padding + qvars.big_padding * 2 + qvars.padding * 2),
- height = qvars.char_height,
+ - (qui.BIG_PADDING + qui.BIG_PADDING * 2 + qui.PADDING * 2),
+ height = qui.CHAR_HEIGHT,
{
widget = wibox.container.scroll.horizontal,
speed = 50,
@@ -82,8 +83,8 @@ local music = wibox.widget(qui.styled {
nil,
{
widget = wibox.container.background,
- bg = qvars.colors.bg,
- shape = qvars.shape,
+ bg = qcolor.palette.bg,
+ shape = qui.shape,
w_progress_bar,
},
layout = wibox.layout.align.vertical,
diff --git a/.config/awesome/ui/statusbar/panel/widgets/power_menu.lua b/.config/awesome/ui/statusbar/panel/widgets/power_menu.lua
index feea829..01171df 100644
--- a/.config/awesome/ui/statusbar/panel/widgets/power_menu.lua
+++ b/.config/awesome/ui/statusbar/panel/widgets/power_menu.lua
@@ -1,19 +1,17 @@
local q = require "quarrel"
local qbind = require "quarrel.bind"
local qui = require "quarrel.ui"
-local qvars = require "quarrel.vars"
local wibox = require "wibox"
return wibox.widget {
qui.styled {
widget = wibox.container.background,
- bg = qvars.colors.black,
{
widget = wibox.widget.textbox,
text = "1",
buttons = {
- qbind:new {
- triggers = qvars.btns.left,
+ qbind {
+ triggers = qbind.btns.left,
press = function()
q.debug "from 1"
end,
@@ -24,13 +22,12 @@ return wibox.widget {
},
qui.styled {
widget = wibox.container.background,
- bg = qvars.colors.black,
{
widget = wibox.widget.textbox,
text = "2",
buttons = {
- qbind:new {
- triggers = qvars.btns.left,
+ qbind {
+ triggers = qbind.btns.left,
press = function()
q.debug "from 2"
end,
@@ -41,13 +38,12 @@ return wibox.widget {
},
qui.styled {
widget = wibox.container.background,
- bg = qvars.colors.black,
{
widget = wibox.widget.textbox,
text = "3",
buttons = {
- qbind:new {
- triggers = qvars.btns.left,
+ qbind {
+ triggers = qbind.btns.left,
press = function()
q.debug "from 3"
end,
diff --git a/.config/awesome/ui/statusbar/panel/widgets/weather.lua b/.config/awesome/ui/statusbar/panel/widgets/weather.lua
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.config/awesome/ui/statusbar/panel/widgets/weather.lua
diff --git a/.config/awesome/ui/statusbar/panel/widgets/wifi.lua b/.config/awesome/ui/statusbar/panel/widgets/wifi.lua
index ad2234f..8ccff95 100644
--- a/.config/awesome/ui/statusbar/panel/widgets/wifi.lua
+++ b/.config/awesome/ui/statusbar/panel/widgets/wifi.lua
@@ -1,27 +1,30 @@
local lgi = require "lgi"
local phosphor = require "assets.phosphor"
+local qcolor = require "quarrel.color"
local qui = require "quarrel.ui"
-local qvars = require "quarrel.vars"
local wibox = require "wibox"
local glib = lgi.GLib
+local wifi_icon = qui.icon {
+ icon = phosphor.wifi_x_fill,
+ color = qcolor.palette.red(),
+ widget = {
+ id = "icon",
+ },
+}
+
local wifi = wibox.widget(qui.styled {
widget = wibox.container.background,
+ bg = qcolor.palette.bg.high,
{
widget = wibox.container.margin,
- margins = qvars.big_padding,
+ margins = qui.BIG_PADDING,
{
{
widget = wibox.container.place,
valign = "center",
halign = "center",
- qui.icon {
- icon = phosphor.wifi_x_fill,
- color = qvars.colors.red,
- widget = {
- id = "icon",
- },
- },
+ wifi_icon,
},
{
widget = wibox.widget.textbox,
@@ -29,7 +32,7 @@ local wifi = wibox.widget(qui.styled {
id = "essid",
},
layout = wibox.layout.fixed.horizontal,
- spacing = qvars.padding,
+ spacing = qui.PADDING,
},
},
})
@@ -43,8 +46,8 @@ awesome.connect_signal("services::wifi", function(essid, _, connected)
end)
awesome.connect_signal("services::wifi::icon", function(icon, color)
- wifi:get_children_by_id("icon")[1].image = icon
- wifi:get_children_by_id("icon")[1].stylesheet = qui.recolor(color)
+ wifi_icon.image = icon
+ wifi_icon.stylesheet = qui.recolor(color)
end)
return wifi
diff --git a/.config/awesome/ui/statusbar/widgets/displays.lua b/.config/awesome/ui/statusbar/widgets/displays.lua
index 62e4398..96cf84c 100644
--- a/.config/awesome/ui/statusbar/widgets/displays.lua
+++ b/.config/awesome/ui/statusbar/widgets/displays.lua
@@ -1,7 +1,7 @@
local battery = require "services.battery"
local phosphor = require "assets.phosphor"
+local qcolor = require "quarrel.color"
local qui = require "quarrel.ui"
-local qvars = require "quarrel.vars"
local wibox = require "wibox"
local function create_display(icon, color)
@@ -16,28 +16,32 @@ local function create_display(icon, color)
}
end
-local d_battery = create_display(phosphor.battery_vertical_warning_fill, qvars.colors.red)
+local d_battery = create_display(phosphor.battery_vertical_warning_fill, qcolor.palette.red())
battery:connect_signal("icon", function(_, icon, color)
d_battery.widget.image = icon
d_battery.widget.stylesheet = qui.recolor(color)
end)
-local d_brightness = create_display(phosphor.moon_fill, qvars.colors.fg)
-awesome.connect_signal("services::brightness::icon", function(icon, color)
- d_brightness.widget.image = icon
- d_brightness.widget.stylesheet = qui.recolor(color)
-end)
+-- local d_brightness = create_display(phosphor.moon_fill, qcolor.palette.fg())
+-- awesome.connect_signal("services::brightness::icon", function(icon, color)
+-- d_brightness.widget.image = icon
+-- d_brightness.widget.stylesheet = qui.recolor(color)
+-- end)
-local d_audio = create_display(phosphor.speaker_simple_slash_fill, qvars.colors.red)
+local d_audio = create_display(phosphor.speaker_simple_slash_fill, qcolor.palette.red())
awesome.connect_signal("services::audio::icon", function(icon, color)
d_audio.widget.image = icon
d_audio.widget.stylesheet = qui.recolor(color)
end)
-local d_wifi = create_display(phosphor.wifi_x_fill, qvars.colors.red)
+local d_wifi = create_display(phosphor.wifi_x_fill, qcolor.palette.red())
awesome.connect_signal("services::wifi::icon", function(icon, color)
d_wifi.widget.image = icon
d_wifi.widget.stylesheet = qui.recolor(color)
end)
-return { audio = d_audio, battery = d_battery, brightness = d_brightness, wifi = d_wifi }
+return {
+ audio = d_audio,
+ battery = d_battery, --[[brightness = d_brightness,]]
+ wifi = d_wifi,
+}
diff --git a/.config/awesome/ui/statusbar/widgets/taglist.lua b/.config/awesome/ui/statusbar/widgets/taglist.lua
index df114df..3b81173 100644
--- a/.config/awesome/ui/statusbar/widgets/taglist.lua
+++ b/.config/awesome/ui/statusbar/widgets/taglist.lua
@@ -10,7 +10,7 @@ return function(s)
screen = s,
filter = awful.widget.taglist.filter.all,
layout = {
- spacing = qvars.padding,
+ spacing = qui.PADDING,
layout = wibox.layout.fixed.vertical,
},
widget_template = {
@@ -21,8 +21,8 @@ return function(s)
self.widget = qui.icon {
icon = phosphor[next(tag:clients()) and "circle_fill" or "circle_bold"],
widget = {
- forced_height = qvars.char_height / 1.5,
- forced_width = qvars.char_height / 1.5,
+ forced_height = qui.CHAR_HEIGHT / 1.5,
+ forced_width = qui.CHAR_HEIGHT / 1.5,
},
}
-- self.widget = wibox.widget {
@@ -33,44 +33,44 @@ return function(s)
if tag.selected then
return
end
- self.widget.stylesheet = qui.recolor(qvars.colors.yellow)
+ self.widget.stylesheet = qui.recolor(qcolor.palette.yellow())
end)
self:connect_signal("mouse::leave", function()
if tag.selected then
return
end
- self.widget.stylesheet = qui.recolor(qvars.colors.fg)
+ self.widget.stylesheet = qui.recolor(qcolor.palette.fg())
end)
if tag.selected then
- self.widget.stylesheet = qui.recolor(qvars.colors.yellow)
+ self.widget.stylesheet = qui.recolor(qcolor.palette.yellow())
return
end
- self.widget.stylesheet = qui.recolor(qvars.colors.fg)
+ self.widget.stylesheet = qui.recolor(qcolor.palette.fg())
end,
update_callback = function(self, tag)
self.widget.image = phosphor[next(tag:clients()) and "circle_fill" or "circle_bold"]
if tag.selected then
- self.widget.stylesheet = qui.recolor(qvars.colors.yellow)
+ self.widget.stylesheet = qui.recolor(qcolor.palette.yellow())
else
- self.widget.stylesheet = qui.recolor(qvars.colors.fg)
+ self.widget.stylesheet = qui.recolor(qcolor.palette.fg())
end
end,
},
buttons = {
- qbind:new {
- triggers = qvars.btns.left,
+ qbind {
+ triggers = qbind.btns.left,
press = function(t)
t:view_only()
end,
hidden = true,
},
- qbind:new {
- mods = qvars.mods.M,
- triggers = qvars.btns.left,
+ qbind {
+ mods = qbind.mods.M,
+ triggers = qbind.btns.left,
press = function(t)
if client.focus then
client.focus:move_to_tag(t)
@@ -78,8 +78,8 @@ return function(s)
end,
hidden = true,
},
- qbind:new {
- triggers = qvars.btns.right,
+ qbind {
+ triggers = qbind.btns.right,
press = awful.tag.viewtoggle,
hidden = true,
},
diff --git a/.config/awesome/ui/statusbar/widgets/taglist_new.lua b/.config/awesome/ui/statusbar/widgets/taglist_new.lua
new file mode 100644
index 0000000..79b8a96
--- /dev/null
+++ b/.config/awesome/ui/statusbar/widgets/taglist_new.lua
@@ -0,0 +1,147 @@
+local awful = require "awful"
+local qbind = require "quarrel.bind"
+local qcolor = require "quarrel.color"
+local qui = require "quarrel.ui"
+local qvars = require "quarrel.vars"
+local wibox = require "wibox"
+
+local empty_indicator = wibox.widget {
+ widget = wibox.container.place,
+ {
+ widget = wibox.container.constraint,
+ strategy = "exact",
+ height = qui.PADDING,
+ width = qui.PADDING,
+ {
+ widget = wibox.container.background,
+ bg = qcolor.palette.border.variant,
+ shape = qui.shape,
+ },
+ },
+}
+
+local client_indicator = wibox.widget {
+ widget = wibox.container.place,
+ {
+ widget = wibox.container.constraint,
+ height = qui.CHAR_HEIGHT,
+ width = qui.PADDING,
+ strategy = "exact",
+ {
+ widget = wibox.container.background,
+ shape = qui.shape,
+ bg = qcolor.palette.fg(),
+ },
+ },
+}
+
+local function update_tasklist(layout, tag, indicated_empty)
+ local client_delta = #tag:clients() - #layout.children
+ if client_delta < 0 then
+ for _ = 1, -(client_delta + (indicated_empty and 1 or 0)) do
+ layout:remove(1)
+ end
+ elseif client_delta > 0 then
+ for _ = 1, client_delta do
+ layout:add(client_indicator)
+ end
+ end
+end
+
+return function(s)
+ return awful.widget.taglist {
+ screen = s,
+ filter = awful.widget.taglist.filter.all,
+ layout = {
+ spacing = qui.PADDING,
+ layout = wibox.layout.fixed.vertical,
+ },
+ widget_template = {
+ widget = wibox.container.constraint,
+ strategy = "min",
+ height = qui.CHAR_HEIGHT,
+ qui.styled {
+ widget = wibox.container.background,
+ bg = qcolor.palette.bg.high,
+ {
+ widget = wibox.container.margin,
+ margins = qui.PADDING,
+ {
+ layout = wibox.layout.flex.vertical,
+ spacing = qui.PADDING,
+ },
+ },
+ },
+ create_callback = function(
+ self,
+ tag --[[@as tag]]
+ )
+ if tag.selected then
+ self.widget.border_color = qcolor.palette.yellow()
+ end
+
+ if #tag:clients() ~= 0 then
+ self.widget.widget.spacing = qui.PADDING
+ if self.indicated_empty then
+ self.widget.widget.widget:remove(1)
+ self.indicated_empty = false
+ end
+ else
+ if not self.indicated_empty then
+ self.widget.widget.widget:add(empty_indicator)
+ self.indicated_empty = true
+ end
+ end
+
+ update_tasklist(self.widget.widget.widget, tag, self.indicated_empty)
+ end,
+ update_callback = function(self, tag)
+ if tag.selected then
+ self.widget.border_color = qcolor.palette.yellow()
+ else
+ self.widget.border_color = qcolor.palette.border()
+ end
+
+ if #tag:clients() ~= 0 then
+ self.widget.widget.spacing = qui.PADDING
+ if self.indicated_empty then
+ self.widget.widget.widget:remove(1)
+ self.indicated_empty = false
+ end
+ else
+ self.widget.widget.spacing = nil
+ if not self.indicated_empty then
+ self.widget.widget.widget:add(empty_indicator)
+ self.indicated_empty = true
+ end
+ end
+
+ update_tasklist(self.widget.widget.widget, tag, self.indicated_empty)
+ end,
+ },
+ buttons = {
+ qbind {
+ triggers = qbind.btns.left,
+ press = function(t)
+ t:view_only()
+ end,
+ hidden = true,
+ },
+ qbind {
+ mods = qbind.mods.M,
+ triggers = qbind.btns.left,
+ press = function(t)
+ if client.focus then
+ client.focus:move_to_tag(t)
+ end
+ end,
+ hidden = true,
+ },
+ qbind {
+ triggers = qbind.btns.right,
+ press = awful.tag.viewtoggle,
+ hidden = true,
+ },
+ },
+ }
+end
diff --git a/.config/awesome/ui/statusbar/widgets/tasklist.lua b/.config/awesome/ui/statusbar/widgets/tasklist.lua
deleted file mode 100644
index 9656185..0000000
--- a/.config/awesome/ui/statusbar/widgets/tasklist.lua
+++ /dev/null
@@ -1,30 +0,0 @@
-local awful = require "awful"
-local gears = require "gears"
-local qvars = require "quarrel.vars"
-local wibox = require "wibox"
-
-return function(s)
- return awful.widget.tasklist {
- screen = s,
- filter = awful.widget.tasklist.filter.currenttags,
- -- buttons = tasklist_buttons,
- layout = {
- spacing = qvars.padding,
- layout = wibox.layout.flex.vertical,
- },
- widget_template = {
- widget = wibox.container.place,
- valign = "center",
- halign = "center",
- -- {
- -- widget = awful.widget.clienticon,
-
- -- }
- {
- widget = awful.widget.clienticon,
- forced_width = qvars.char_height,
- forced_height = qvars.char_height,
- },
- },
- }
-end
diff --git a/.config/awesome/ui/wicked/consts.lua b/.config/awesome/ui/wicked/consts.lua
new file mode 100644
index 0000000..e00cae5
--- /dev/null
+++ b/.config/awesome/ui/wicked/consts.lua
@@ -0,0 +1,5 @@
+local C = {}
+
+C.NOTIF_TIMEOUT = 3
+
+return C
diff --git a/.config/awesome/ui/wicked/init.lua b/.config/awesome/ui/wicked/init.lua
index ddbf912..dea28d5 100644
--- a/.config/awesome/ui/wicked/init.lua
+++ b/.config/awesome/ui/wicked/init.lua
@@ -2,19 +2,22 @@ local awful = require "awful"
local beautiful = require "beautiful"
local gshape = require "gears.shape"
local naughty = require "naughty"
+local qanim = require "quarrel.animation"
local qui = require "quarrel.ui"
local qvars = require "quarrel.vars"
local wibox = require "wibox"
local rtimed = require("lib.rubato").timed
local easing = require("lib.rubato").easing
local gtimer = require "gears.timer"
+local qcolor = require "quarrel.color"
local qmarkup = require "quarrel.markup"
-return function(n)
- local intertext_margin = (n.title ~= "" or n.message ~= "") and qvars.padding or 0
- local title_height = n.title ~= "" and qvars.char_height or 0
- local message_height = n.message ~= "" and qvars.char_height or 0
- -- local app_name_height = n.app_name ~= "" and
+local M = require "ui.wicked.consts"
+
+function M.new(n)
+ local intertext_margin = (n.title ~= "" or n.message ~= "") and qui.PADDING or 0
+ local title_height = n.title ~= "" and qui.CHAR_HEIGHT or 0
+ local message_height = n.message ~= "" and qui.CHAR_HEIGHT or 0
local app_name
if n.app_name == "" then
app_name = n._private._foreign and "Unknown" or "Awesome"
@@ -29,22 +32,19 @@ return function(n)
margins = beautiful.useless_gap * 2,
})
end,
- bg = qvars.colors.transparent,
+ bg = qcolor.palette.transparent,
border_width = 0,
shape = gshape.rectangle,
widget_template = {
widget = wibox.container.constraint,
- height = qvars.big_padding * 2
- + qvars.char_height
- + qvars.border_width
- + qvars.big_padding * 2
+ height = qui.BIG_PADDING * 2
+ + qui.CHAR_HEIGHT
+ + qui.BORDER_WIDTH
+ + qui.BIG_PADDING * 2
-- + title_height
-- + message_height
-- + intertext_margin,
- + (
- n.icon and qvars.char_height * 2 + qvars.padding
- or (title_height + message_height + intertext_margin)
- ),
+ + (n.icon and qui.CHAR_HEIGHT * 2 + qui.PADDING or (title_height + message_height + intertext_margin)),
strategy = "exact",
{
@@ -59,7 +59,7 @@ return function(n)
{
{
widget = wibox.container.margin,
- margins = qvars.big_padding,
+ margins = qui.BIG_PADDING,
{
{
widget = wibox.widget.textbox,
@@ -69,12 +69,12 @@ return function(n)
{
widget = wibox.container.radialprogressbar,
- max_value = qvars.notif_timeout,
- border_color = qvars.colors.black,
- color = qvars.colors.yellow,
- border_width = qvars.border_width,
- forced_height = qvars.char_height,
- forced_width = qvars.char_height,
+ max_value = M.NOTIF_TIMEOUT,
+ border_color = qcolor.palette.bg.lowest,
+ color = qcolor.palette.yellow(),
+ border_width = qui.BORDER_WIDTH,
+ forced_height = qui.CHAR_HEIGHT,
+ forced_width = qui.CHAR_HEIGHT,
id = "progress",
},
layout = wibox.layout.align.horizontal,
@@ -82,20 +82,20 @@ return function(n)
},
{
widget = wibox.container.constraint,
- height = qvars.border_width,
+ height = qui.BORDER_WIDTH,
width = beautiful.notification_max_width,
strategy = "exact",
{
widget = wibox.container.background,
- bg = qvars.colors.bright.black,
+ bg = qcolor.palette.border(),
},
},
{
widget = wibox.container.margin,
- margins = qvars.big_padding,
+ margins = qui.BIG_PADDING,
{
widget = wibox.container.constraint,
- height = n.icon and qvars.char_height * 2 + qvars.padding
+ height = n.icon and qui.CHAR_HEIGHT * 2 + qui.PADDING
or (title_height + message_height + intertext_margin),
strategy = "exact",
{
@@ -103,7 +103,7 @@ return function(n)
widget = wibox.container.background,
{
widget = naughty.widget.icon,
- shape = qvars.shape,
+ shape = qui.shape,
notification = n,
},
},
@@ -130,7 +130,7 @@ return function(n)
layout = wibox.layout.fixed.vertical,
},
fill_space = true,
- spacing = n.icon and qvars.big_padding or nil,
+ spacing = n.icon and qui.BIG_PADDING or nil,
layout = wibox.layout.fixed.horizontal,
},
},
@@ -158,12 +158,10 @@ return function(n)
end,
}
- local position = rtimed {
- duration = qvars.anim_duration * 2,
- intro = qvars.anim_intro * 2,
+ local position = qanim:new {
+ duration = qvars.anim_duration,
pos = 0,
- easing = easing.quadratic,
- clamp_position = true,
+ easing = qvars.easing,
subscribed = function(pos)
gtimer.delayed_call(function()
notif.widget.widget:move(1, function(geo, args)
@@ -179,8 +177,8 @@ return function(n)
end,
}
local opacity = rtimed {
- duration = qvars.anim_duration * 2,
- intro = qvars.anim_intro * 2,
+ duration = qvars.anim_duration,
+ intro = qvars.anim_intro,
easing = easing.quadratic,
pos = 0,
clamp_position = true,
@@ -192,12 +190,20 @@ return function(n)
n:disconnect_signal("destroyed", notif._private.destroy_callback)
function notif._private.destroy_callback()
opacity.target = 0
- position.target = 0
+ position:set(0)
hiding = true
end
n:weak_connect_signal("destroyed", notif._private.destroy_callback)
+ --- for some reason when urgency is critical, it somehow makes it not disappear
+ --- why? dunno, FIXME
opacity.target = 1
- position.target = qvars.char_width * 48
- progress.target = qvars.notif_timeout
+ position:set(beautiful.notification_max_width)
+ progress.target = M.NOTIF_TIMEOUT
end
+
+return setmetatable(M, {
+ __call = function(_, ...)
+ return M.new(...)
+ end,
+})
diff --git a/.config/fish/bindings.fish b/.config/fish/bindings.fish
index 1ed7313..1a371e1 100644
--- a/.config/fish/bindings.fish
+++ b/.config/fish/bindings.fish
@@ -1 +1,3 @@
bind \b backward-kill-word
+bind \eq fish_commandline_toggle
+bind alt-s "fish_commandline_prepend doas"
diff --git a/.config/fish/colors.fish b/.config/fish/colors.fish
index de32d6d..381b0e9 100644
--- a/.config/fish/colors.fish
+++ b/.config/fish/colors.fish
@@ -9,15 +9,15 @@ set fish_color_error white
set fish_color_param white
set fish_color_valid_path white
set fish_color_option white
-set fish_color_comment "#8893a5"
+set fish_color_comment "#77828c"
set fish_color_operator white
set fish_color_escape yellow
-set fish_color_autosuggestion "#8893a5"
+set fish_color_autosuggestion "#77828c"
set fish_color_search_match --background=black
# pager
-set fish_pager_color_progress "#8893a5"
+set fish_pager_color_progress "#77828c"
set fish_pager_color_prefix white
-set fish_pager_color_completion "#8893a5"
-set fish_pager_color_description "#8893a5"
+set fish_pager_color_completion "#77828c"
+set fish_pager_color_description "#77828c"
set fish_pager_color_selected_background --background=black
diff --git a/.config/fish/completions/beet.fish b/.config/fish/completions/beet.fish
new file mode 100644
index 0000000..8cc38e0
--- /dev/null
+++ b/.config/fish/completions/beet.fish
@@ -0,0 +1,523 @@
+
+function __fish_beet_needs_command
+ set cmd (commandline -opc)
+ if test (count $cmd) -eq 1
+ return 0
+ end
+ return 1
+end
+
+function __fish_beet_using_command
+ set cmd (commandline -opc)
+ set needle (count $cmd)
+ if test $needle -gt 1
+ if begin test $argv[1] = $cmd[2];
+ and not contains -- $cmd[$needle] $FIELDS; end
+ return 0
+ end
+ end
+ return 1
+end
+
+function __fish_beet_use_extra
+ set cmd (commandline -opc)
+ set needle (count $cmd)
+ if test $argv[2] = $cmd[$needle]
+ return 0
+ end
+ return 1
+end
+
+set CMDS bpmanalyser clearart completion config convert dup duplicates embedart extractart fetchart fields fish ? help imp im import info ls list miss missing mod modify mv move rm remove spotify stats upd up update version write
+
+set FIELDS acoustid_fingerprint: acoustid_id: added: album: album_id: albumartist: albumartist_credit: albumartist_sort: albumdisambig: albumstatus: albumtotal: albumtype: albumtypes: arranger: artist: artist_credit: artist_sort: artpath: asin: bitdepth: bitrate: bpm: catalognum: channels: comments: comp: composer: composer_sort: country: day: disc: discogs_albumid: discogs_artistid: discogs_labelid: disctitle: disctotal: encoder: filesize: format: genre: grouping: id: initial_key: isrc: label: language: length: lyricist: lyrics: mb_albumartistid: mb_albumid: mb_artistid: mb_releasegroupid: mb_releasetrackid: mb_trackid: mb_workid: media: missing: month: mtime: original_day: original_month: original_year: path: r128_album_gain: r128_track_gain: releasegroupdisambig: rg_album_gain: rg_album_peak: rg_track_gain: rg_track_peak: samplerate: script: singleton: style: title: track: trackdisambig: tracktotal: work: work_disambig: year:
+
+
+# ====== setup basic beet completion =====
+
+complete -c beet -n '__fish_beet_needs_command' -l format-item -f -d 'print with custom format'
+complete -c beet -n '__fish_beet_needs_command' -l format-album -f -d 'print with custom format'
+complete -c beet -n '__fish_beet_needs_command' -s l -l library -f -r -d 'library database file to use'
+complete -c beet -n '__fish_beet_needs_command' -s d -l directory -f -r -d 'destination music directory'
+complete -c beet -n '__fish_beet_needs_command' -s v -l verbose -f -d 'print debugging information'
+complete -c beet -n '__fish_beet_needs_command' -s c -l config -f -r -d 'path to configuration file'
+complete -c beet -n '__fish_beet_needs_command' -s h -l help -f -d 'print this help message and exit'
+
+# ====== setup field completion for subcommands =====
+
+# ------ fieldsetups for bpmanalyser -------
+complete -c beet -n '__fish_beet_needs_command' -a bpmanalyser -f -d 'analyse your songs for tempo and write it into the bpm tag'
+complete -c beet -n '__fish_beet_using_command bpmanalyser' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for clearart -------
+complete -c beet -n '__fish_beet_needs_command' -a clearart -f -d 'remove images from file metadata'
+complete -c beet -n '__fish_beet_using_command clearart' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for completion -------
+complete -c beet -n '__fish_beet_needs_command' -a completion -f -d 'print shell script that provides command line completion'
+complete -c beet -n '__fish_beet_using_command completion' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for config -------
+complete -c beet -n '__fish_beet_needs_command' -a config -f -d 'show or edit the user configuration'
+complete -c beet -n '__fish_beet_using_command config' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for convert -------
+complete -c beet -n '__fish_beet_needs_command' -a convert -f -d 'convert to external location'
+complete -c beet -n '__fish_beet_using_command convert' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for dup -------
+complete -c beet -n '__fish_beet_needs_command' -a dup -f -d 'List duplicate tracks or albums.'
+complete -c beet -n '__fish_beet_using_command dup' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for duplicates -------
+complete -c beet -n '__fish_beet_needs_command' -a duplicates -f -d 'List duplicate tracks or albums.'
+complete -c beet -n '__fish_beet_using_command duplicates' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for embedart -------
+complete -c beet -n '__fish_beet_needs_command' -a embedart -f -d 'embed image files into file metadata'
+complete -c beet -n '__fish_beet_using_command embedart' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for extractart -------
+complete -c beet -n '__fish_beet_needs_command' -a extractart -f -d 'extract an image from file metadata'
+complete -c beet -n '__fish_beet_using_command extractart' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for fetchart -------
+complete -c beet -n '__fish_beet_needs_command' -a fetchart -f -d 'download album art'
+complete -c beet -n '__fish_beet_using_command fetchart' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for fields -------
+complete -c beet -n '__fish_beet_needs_command' -a fields -f -d 'show fields available for queries and format strings'
+complete -c beet -n '__fish_beet_using_command fields' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for fish -------
+complete -c beet -n '__fish_beet_needs_command' -a fish -f -d 'generate Fish shell tab completions'
+complete -c beet -n '__fish_beet_using_command fish' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for \? -------
+complete -c beet -n '__fish_beet_needs_command' -a \? -f -d 'give detailed help on a specific sub-command'
+complete -c beet -n '__fish_beet_using_command \?' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for help -------
+complete -c beet -n '__fish_beet_needs_command' -a help -f -d 'give detailed help on a specific sub-command'
+complete -c beet -n '__fish_beet_using_command help' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for imp -------
+complete -c beet -n '__fish_beet_needs_command' -a imp -f -d 'import new music'
+complete -c beet -n '__fish_beet_using_command imp' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for im -------
+complete -c beet -n '__fish_beet_needs_command' -a im -f -d 'import new music'
+complete -c beet -n '__fish_beet_using_command im' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for import -------
+complete -c beet -n '__fish_beet_needs_command' -a import -f -d 'import new music'
+complete -c beet -n '__fish_beet_using_command import' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for info -------
+complete -c beet -n '__fish_beet_needs_command' -a info -f -d 'show file metadata'
+complete -c beet -n '__fish_beet_using_command info' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for ls -------
+complete -c beet -n '__fish_beet_needs_command' -a ls -f -d 'query the library'
+complete -c beet -n '__fish_beet_using_command ls' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for list -------
+complete -c beet -n '__fish_beet_needs_command' -a list -f -d 'query the library'
+complete -c beet -n '__fish_beet_using_command list' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for miss -------
+complete -c beet -n '__fish_beet_needs_command' -a miss -f -d 'List missing tracks.'
+complete -c beet -n '__fish_beet_using_command miss' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for missing -------
+complete -c beet -n '__fish_beet_needs_command' -a missing -f -d 'List missing tracks.'
+complete -c beet -n '__fish_beet_using_command missing' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for mod -------
+complete -c beet -n '__fish_beet_needs_command' -a mod -f -d 'change metadata fields'
+complete -c beet -n '__fish_beet_using_command mod' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for modify -------
+complete -c beet -n '__fish_beet_needs_command' -a modify -f -d 'change metadata fields'
+complete -c beet -n '__fish_beet_using_command modify' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for mv -------
+complete -c beet -n '__fish_beet_needs_command' -a mv -f -d 'move or copy items'
+complete -c beet -n '__fish_beet_using_command mv' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for move -------
+complete -c beet -n '__fish_beet_needs_command' -a move -f -d 'move or copy items'
+complete -c beet -n '__fish_beet_using_command move' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for rm -------
+complete -c beet -n '__fish_beet_needs_command' -a rm -f -d 'remove matching items from the library'
+complete -c beet -n '__fish_beet_using_command rm' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for remove -------
+complete -c beet -n '__fish_beet_needs_command' -a remove -f -d 'remove matching items from the library'
+complete -c beet -n '__fish_beet_using_command remove' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for spotify -------
+complete -c beet -n '__fish_beet_needs_command' -a spotify -f -d 'build a Spotify playlist'
+complete -c beet -n '__fish_beet_using_command spotify' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for stats -------
+complete -c beet -n '__fish_beet_needs_command' -a stats -f -d 'show statistics about the library or a query'
+complete -c beet -n '__fish_beet_using_command stats' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for upd -------
+complete -c beet -n '__fish_beet_needs_command' -a upd -f -d 'update the library'
+complete -c beet -n '__fish_beet_using_command upd' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for up -------
+complete -c beet -n '__fish_beet_needs_command' -a up -f -d 'update the library'
+complete -c beet -n '__fish_beet_using_command up' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for update -------
+complete -c beet -n '__fish_beet_needs_command' -a update -f -d 'update the library'
+complete -c beet -n '__fish_beet_using_command update' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for version -------
+complete -c beet -n '__fish_beet_needs_command' -a version -f -d 'output version information'
+complete -c beet -n '__fish_beet_using_command version' -a '$FIELDS' -f -d 'fieldname'
+
+# ------ fieldsetups for write -------
+complete -c beet -n '__fish_beet_needs_command' -a write -f -d 'write tag information to files'
+complete -c beet -n '__fish_beet_using_command write' -a '$FIELDS' -f -d 'fieldname'
+
+
+
+# ====== completions for bpmanalyser =====
+complete -c beet -n '__fish_beet_using_command bpmanalyser' -s d -l dry-run -f -d '[default: False] display the bpm values but do not update the library items'
+complete -c beet -n '__fish_beet_using_command bpmanalyser' -s w -l write -f -d '[default: True] write the bpm values to the media files'
+complete -c beet -n '__fish_beet_using_command bpmanalyser' -r -s t -l threads -f -d '[default: 8] the number of threads to run in parallel'
+complete -c beet -n '__fish_beet_using_command bpmanalyser' -s f -l force -f -d '[default: False] force analysis of items with non-zero bpm values'
+complete -c beet -n '__fish_beet_using_command bpmanalyser' -s q -l quiet -f -d '[default: False] mute all output'
+complete -c beet -n '__fish_beet_using_command bpmanalyser' -s v -l version -f -d 'show plugin version'
+complete -c beet -n '__fish_beet_using_command bpmanalyser' -s h -l help -f -d 'print help'
+
+
+# ====== completions for clearart =====
+complete -c beet -n '__fish_beet_using_command clearart' -s y -l yes -f -d 'skip confirmation'
+complete -c beet -n '__fish_beet_using_command clearart' -s h -l help -f -d 'print help'
+
+
+# ====== completions for completion =====
+complete -c beet -n '__fish_beet_using_command completion' -s h -l help -f -d 'print help'
+
+
+# ====== completions for config =====
+complete -c beet -n '__fish_beet_using_command config' -s p -l paths -f -d 'show files that configuration was loaded from'
+complete -c beet -n '__fish_beet_using_command config' -s e -l edit -f -d 'edit user configuration with $EDITOR'
+complete -c beet -n '__fish_beet_using_command config' -s d -l defaults -f -d 'include the default configuration'
+complete -c beet -n '__fish_beet_using_command config' -s c -l clear -f -d 'do not redact sensitive fields'
+complete -c beet -n '__fish_beet_using_command config' -s h -l help -f -d 'print help'
+
+
+# ====== completions for convert =====
+complete -c beet -n '__fish_beet_using_command convert' -s p -l pretend -f -d 'show actions but do nothing'
+complete -c beet -n '__fish_beet_using_command convert' -r -s t -l threads -f -d 'change the number of threads, defaults to maximum available processors'
+complete -c beet -n '__fish_beet_using_command convert' -s k -l keep-new -f -d 'keep only the converted and move the old files'
+complete -c beet -n '__fish_beet_using_command convert' -r -s d -l dest -f -d 'set the destination directory'
+complete -c beet -n '__fish_beet_using_command convert' -r -s f -l format -f -d 'set the target format of the tracks'
+complete -c beet -n '__fish_beet_using_command convert' -s y -l yes -f -d 'do not ask for confirmation'
+complete -c beet -n '__fish_beet_using_command convert' -s l -l link -f -d 'symlink files that do not need transcoding.'
+complete -c beet -n '__fish_beet_using_command convert' -s H -l hardlink -f -d 'hardlink files that do not need transcoding. Overrides --link.'
+complete -c beet -n '__fish_beet_using_command convert' -s a -l album -f -d 'match albums instead of tracks'
+complete -c beet -n '__fish_beet_using_command convert' -s h -l help -f -d 'print help'
+
+
+# ====== completions for dup =====
+complete -c beet -n '__fish_beet_using_command dup' -s c -l count -f -d 'show duplicate counts'
+complete -c beet -n '__fish_beet_using_command dup' -r -s C -l checksum -f -d 'report duplicates based on arbitrary command'
+complete -c beet -n '__fish_beet_using_command dup' -s d -l delete -f -d 'delete items from library and disk'
+complete -c beet -n '__fish_beet_using_command dup' -s F -l full -f -d 'show all versions of duplicate tracks or albums'
+complete -c beet -n '__fish_beet_using_command dup' -s s -l strict -f -d 'report duplicates only if all attributes are set'
+complete -c beet -n '__fish_beet_using_command dup' -r -s k -l key -f -d 'report duplicates based on keys (use multiple times)'
+complete -c beet -n '__fish_beet_using_command dup' -s M -l merge -f -d 'merge duplicate items'
+complete -c beet -n '__fish_beet_using_command dup' -r -s m -l move -f -d 'move items to dest'
+complete -c beet -n '__fish_beet_using_command dup' -r -s o -l copy -f -d 'copy items to dest'
+complete -c beet -n '__fish_beet_using_command dup' -r -s t -l tag -f -d "tag matched items with 'k=v' attribute"
+complete -c beet -n '__fish_beet_using_command dup' -s a -l album -f -d 'match albums instead of tracks'
+complete -c beet -n '__fish_beet_using_command dup' -s p -l path -f -d 'print paths for matched items or albums'
+complete -c beet -n '__fish_beet_using_command dup' -r -s f -l format -f -d 'print with custom format'
+complete -c beet -n '__fish_beet_using_command dup' -s h -l help -f -d 'print help'
+
+
+# ====== completions for duplicates =====
+complete -c beet -n '__fish_beet_using_command duplicates' -s c -l count -f -d 'show duplicate counts'
+complete -c beet -n '__fish_beet_using_command duplicates' -r -s C -l checksum -f -d 'report duplicates based on arbitrary command'
+complete -c beet -n '__fish_beet_using_command duplicates' -s d -l delete -f -d 'delete items from library and disk'
+complete -c beet -n '__fish_beet_using_command duplicates' -s F -l full -f -d 'show all versions of duplicate tracks or albums'
+complete -c beet -n '__fish_beet_using_command duplicates' -s s -l strict -f -d 'report duplicates only if all attributes are set'
+complete -c beet -n '__fish_beet_using_command duplicates' -r -s k -l key -f -d 'report duplicates based on keys (use multiple times)'
+complete -c beet -n '__fish_beet_using_command duplicates' -s M -l merge -f -d 'merge duplicate items'
+complete -c beet -n '__fish_beet_using_command duplicates' -r -s m -l move -f -d 'move items to dest'
+complete -c beet -n '__fish_beet_using_command duplicates' -r -s o -l copy -f -d 'copy items to dest'
+complete -c beet -n '__fish_beet_using_command duplicates' -r -s t -l tag -f -d "tag matched items with 'k=v' attribute"
+complete -c beet -n '__fish_beet_using_command duplicates' -s a -l album -f -d 'match albums instead of tracks'
+complete -c beet -n '__fish_beet_using_command duplicates' -s p -l path -f -d 'print paths for matched items or albums'
+complete -c beet -n '__fish_beet_using_command duplicates' -r -s f -l format -f -d 'print with custom format'
+complete -c beet -n '__fish_beet_using_command duplicates' -s h -l help -f -d 'print help'
+
+
+# ====== completions for embedart =====
+complete -c beet -n '__fish_beet_using_command embedart' -r -s f -l file -f -d 'the image file to embed'
+complete -c beet -n '__fish_beet_using_command embedart' -s y -l yes -f -d 'skip confirmation'
+complete -c beet -n '__fish_beet_using_command embedart' -s h -l help -f -d 'print help'
+
+
+# ====== completions for extractart =====
+complete -c beet -n '__fish_beet_using_command extractart' -r -s o -f -d 'image output file'
+complete -c beet -n '__fish_beet_using_command extractart' -r -s n -f -d 'image filename to create for all matched albums'
+complete -c beet -n '__fish_beet_using_command extractart' -s a -f -d 'associate the extracted images with the album'
+complete -c beet -n '__fish_beet_using_command extractart' -s h -l help -f -d 'print help'
+
+
+# ====== completions for fetchart =====
+complete -c beet -n '__fish_beet_using_command fetchart' -s f -l force -f -d 're-download art when already present'
+complete -c beet -n '__fish_beet_using_command fetchart' -s q -l quiet -f -d 'quiet mode: do not output albums that already have artwork'
+complete -c beet -n '__fish_beet_using_command fetchart' -s h -l help -f -d 'print help'
+
+
+# ====== completions for fields =====
+complete -c beet -n '__fish_beet_using_command fields' -s h -l help -f -d 'print help'
+
+
+# ====== completions for fish =====
+complete -c beet -n '__fish_beet_using_command fish' -s f -l noFields -f -d 'omit album/track field completions'
+complete -c beet -n '__fish_beet_using_command fish' -r -s e -l extravalues -f -a 'id path album_id title artist artist_sort artist_credit album albumartist albumartist_sort albumartist_credit genre style discogs_albumid discogs_artistid discogs_labelid lyricist composer composer_sort work mb_workid work_disambig arranger grouping year month day track tracktotal disc disctotal lyrics comments bpm comp mb_trackid mb_albumid mb_artistid mb_albumartistid mb_releasetrackid trackdisambig albumtype albumtypes label acoustid_fingerprint acoustid_id mb_releasegroupid asin isrc catalognum script language country albumstatus media albumdisambig releasegroupdisambig disctitle encoder rg_track_gain rg_track_peak rg_album_gain rg_album_peak r128_track_gain r128_album_gain original_year original_month original_day initial_key length bitrate format samplerate bitdepth channels mtime added singleton filesize id artpath added albumartist albumartist_sort albumartist_credit album genre style discogs_albumid discogs_artistid discogs_labelid year month day disctotal comp mb_albumid mb_albumartistid albumtype albumtypes label mb_releasegroupid asin catalognum script language country albumstatus albumdisambig releasegroupdisambig rg_album_gain rg_album_peak r128_album_gain original_year original_month original_day missing path albumtotal' -d 'include specified field *values* in completions'
+complete -c beet -n '__fish_beet_using_command fish' -s h -l help -f -d 'print help'
+
+
+# ====== completions for \? =====
+complete -c beet -n '__fish_beet_using_command \?' -s h -l help -f -d 'print help'
+
+
+# ====== completions for help =====
+complete -c beet -n '__fish_beet_using_command help' -s h -l help -f -d 'print help'
+
+
+# ====== completions for imp =====
+complete -c beet -n '__fish_beet_using_command imp' -s c -l copy -f -d 'copy tracks into library directory (default)'
+complete -c beet -n '__fish_beet_using_command imp' -s C -l nocopy -f -d "don't copy tracks (opposite of -c)"
+complete -c beet -n '__fish_beet_using_command imp' -s m -l move -f -d 'move tracks into the library (overrides -c)'
+complete -c beet -n '__fish_beet_using_command imp' -s w -l write -f -d "write new metadata to files' tags (default)"
+complete -c beet -n '__fish_beet_using_command imp' -s W -l nowrite -f -d "don't write metadata (opposite of -w)"
+complete -c beet -n '__fish_beet_using_command imp' -s a -l autotag -f -d 'infer tags for imported files (default)'
+complete -c beet -n '__fish_beet_using_command imp' -s A -l noautotag -f -d "don't infer tags for imported files (opposite of -a)"
+complete -c beet -n '__fish_beet_using_command imp' -s p -l resume -f -d 'resume importing if interrupted'
+complete -c beet -n '__fish_beet_using_command imp' -s P -l noresume -f -d 'do not try to resume importing'
+complete -c beet -n '__fish_beet_using_command imp' -s q -l quiet -f -d 'never prompt for input: skip albums instead'
+complete -c beet -n '__fish_beet_using_command imp' -r -s l -l log -f -d 'file to log untaggable albums for later review'
+complete -c beet -n '__fish_beet_using_command imp' -s s -l singletons -f -d 'import individual tracks instead of full albums'
+complete -c beet -n '__fish_beet_using_command imp' -s t -l timid -f -d 'always confirm all actions'
+complete -c beet -n '__fish_beet_using_command imp' -s L -l library -f -d 'retag items matching a query'
+complete -c beet -n '__fish_beet_using_command imp' -s i -l incremental -f -d 'skip already-imported directories'
+complete -c beet -n '__fish_beet_using_command imp' -s I -l noincremental -f -d 'do not skip already-imported directories'
+complete -c beet -n '__fish_beet_using_command imp' -l from-scratch -f -d 'erase existing metadata before applying new metadata'
+complete -c beet -n '__fish_beet_using_command imp' -l flat -f -d 'import an entire tree as a single album'
+complete -c beet -n '__fish_beet_using_command imp' -s g -l group-albums -f -d 'group tracks in a folder into separate albums'
+complete -c beet -n '__fish_beet_using_command imp' -l pretend -f -d 'just print the files to import'
+complete -c beet -n '__fish_beet_using_command imp' -r -s S -l search-id -f -d 'restrict matching to a specific metadata backend ID'
+complete -c beet -n '__fish_beet_using_command imp' -r -l set -f -d 'set the given fields to the supplied values'
+complete -c beet -n '__fish_beet_using_command imp' -s h -l help -f -d 'print help'
+
+
+# ====== completions for im =====
+complete -c beet -n '__fish_beet_using_command im' -s c -l copy -f -d 'copy tracks into library directory (default)'
+complete -c beet -n '__fish_beet_using_command im' -s C -l nocopy -f -d "don't copy tracks (opposite of -c)"
+complete -c beet -n '__fish_beet_using_command im' -s m -l move -f -d 'move tracks into the library (overrides -c)'
+complete -c beet -n '__fish_beet_using_command im' -s w -l write -f -d "write new metadata to files' tags (default)"
+complete -c beet -n '__fish_beet_using_command im' -s W -l nowrite -f -d "don't write metadata (opposite of -w)"
+complete -c beet -n '__fish_beet_using_command im' -s a -l autotag -f -d 'infer tags for imported files (default)'
+complete -c beet -n '__fish_beet_using_command im' -s A -l noautotag -f -d "don't infer tags for imported files (opposite of -a)"
+complete -c beet -n '__fish_beet_using_command im' -s p -l resume -f -d 'resume importing if interrupted'
+complete -c beet -n '__fish_beet_using_command im' -s P -l noresume -f -d 'do not try to resume importing'
+complete -c beet -n '__fish_beet_using_command im' -s q -l quiet -f -d 'never prompt for input: skip albums instead'
+complete -c beet -n '__fish_beet_using_command im' -r -s l -l log -f -d 'file to log untaggable albums for later review'
+complete -c beet -n '__fish_beet_using_command im' -s s -l singletons -f -d 'import individual tracks instead of full albums'
+complete -c beet -n '__fish_beet_using_command im' -s t -l timid -f -d 'always confirm all actions'
+complete -c beet -n '__fish_beet_using_command im' -s L -l library -f -d 'retag items matching a query'
+complete -c beet -n '__fish_beet_using_command im' -s i -l incremental -f -d 'skip already-imported directories'
+complete -c beet -n '__fish_beet_using_command im' -s I -l noincremental -f -d 'do not skip already-imported directories'
+complete -c beet -n '__fish_beet_using_command im' -l from-scratch -f -d 'erase existing metadata before applying new metadata'
+complete -c beet -n '__fish_beet_using_command im' -l flat -f -d 'import an entire tree as a single album'
+complete -c beet -n '__fish_beet_using_command im' -s g -l group-albums -f -d 'group tracks in a folder into separate albums'
+complete -c beet -n '__fish_beet_using_command im' -l pretend -f -d 'just print the files to import'
+complete -c beet -n '__fish_beet_using_command im' -r -s S -l search-id -f -d 'restrict matching to a specific metadata backend ID'
+complete -c beet -n '__fish_beet_using_command im' -r -l set -f -d 'set the given fields to the supplied values'
+complete -c beet -n '__fish_beet_using_command im' -s h -l help -f -d 'print help'
+
+
+# ====== completions for import =====
+complete -c beet -n '__fish_beet_using_command import' -s c -l copy -f -d 'copy tracks into library directory (default)'
+complete -c beet -n '__fish_beet_using_command import' -s C -l nocopy -f -d "don't copy tracks (opposite of -c)"
+complete -c beet -n '__fish_beet_using_command import' -s m -l move -f -d 'move tracks into the library (overrides -c)'
+complete -c beet -n '__fish_beet_using_command import' -s w -l write -f -d "write new metadata to files' tags (default)"
+complete -c beet -n '__fish_beet_using_command import' -s W -l nowrite -f -d "don't write metadata (opposite of -w)"
+complete -c beet -n '__fish_beet_using_command import' -s a -l autotag -f -d 'infer tags for imported files (default)'
+complete -c beet -n '__fish_beet_using_command import' -s A -l noautotag -f -d "don't infer tags for imported files (opposite of -a)"
+complete -c beet -n '__fish_beet_using_command import' -s p -l resume -f -d 'resume importing if interrupted'
+complete -c beet -n '__fish_beet_using_command import' -s P -l noresume -f -d 'do not try to resume importing'
+complete -c beet -n '__fish_beet_using_command import' -s q -l quiet -f -d 'never prompt for input: skip albums instead'
+complete -c beet -n '__fish_beet_using_command import' -r -s l -l log -f -d 'file to log untaggable albums for later review'
+complete -c beet -n '__fish_beet_using_command import' -s s -l singletons -f -d 'import individual tracks instead of full albums'
+complete -c beet -n '__fish_beet_using_command import' -s t -l timid -f -d 'always confirm all actions'
+complete -c beet -n '__fish_beet_using_command import' -s L -l library -f -d 'retag items matching a query'
+complete -c beet -n '__fish_beet_using_command import' -s i -l incremental -f -d 'skip already-imported directories'
+complete -c beet -n '__fish_beet_using_command import' -s I -l noincremental -f -d 'do not skip already-imported directories'
+complete -c beet -n '__fish_beet_using_command import' -l from-scratch -f -d 'erase existing metadata before applying new metadata'
+complete -c beet -n '__fish_beet_using_command import' -l flat -f -d 'import an entire tree as a single album'
+complete -c beet -n '__fish_beet_using_command import' -s g -l group-albums -f -d 'group tracks in a folder into separate albums'
+complete -c beet -n '__fish_beet_using_command import' -l pretend -f -d 'just print the files to import'
+complete -c beet -n '__fish_beet_using_command import' -r -s S -l search-id -f -d 'restrict matching to a specific metadata backend ID'
+complete -c beet -n '__fish_beet_using_command import' -r -l set -f -d 'set the given fields to the supplied values'
+complete -c beet -n '__fish_beet_using_command import' -s h -l help -f -d 'print help'
+
+
+# ====== completions for info =====
+complete -c beet -n '__fish_beet_using_command info' -s l -l library -f -d 'show library fields instead of tags'
+complete -c beet -n '__fish_beet_using_command info' -s a -l album -f -d 'show album fields instead of tracks (implies "--library")'
+complete -c beet -n '__fish_beet_using_command info' -s s -l summarize -f -d 'summarize the tags of all files'
+complete -c beet -n '__fish_beet_using_command info' -r -s i -l include-keys -f -d 'comma separated list of keys to show'
+complete -c beet -n '__fish_beet_using_command info' -s k -l keys-only -f -d 'show only the keys'
+complete -c beet -n '__fish_beet_using_command info' -r -s f -l format -f -d 'print with custom format'
+complete -c beet -n '__fish_beet_using_command info' -s h -l help -f -d 'print help'
+
+
+# ====== completions for ls =====
+complete -c beet -n '__fish_beet_using_command ls' -s a -l album -f -d 'match albums instead of tracks'
+complete -c beet -n '__fish_beet_using_command ls' -s p -l path -f -d 'print paths for matched items or albums'
+complete -c beet -n '__fish_beet_using_command ls' -r -s f -l format -f -d 'print with custom format'
+complete -c beet -n '__fish_beet_using_command ls' -s h -l help -f -d 'print help'
+
+
+# ====== completions for list =====
+complete -c beet -n '__fish_beet_using_command list' -s a -l album -f -d 'match albums instead of tracks'
+complete -c beet -n '__fish_beet_using_command list' -s p -l path -f -d 'print paths for matched items or albums'
+complete -c beet -n '__fish_beet_using_command list' -r -s f -l format -f -d 'print with custom format'
+complete -c beet -n '__fish_beet_using_command list' -s h -l help -f -d 'print help'
+
+
+# ====== completions for miss =====
+complete -c beet -n '__fish_beet_using_command miss' -s c -l count -f -d 'count missing tracks per album'
+complete -c beet -n '__fish_beet_using_command miss' -s t -l total -f -d 'count total of missing tracks'
+complete -c beet -n '__fish_beet_using_command miss' -s a -l album -f -d 'show missing albums for artist instead of tracks'
+complete -c beet -n '__fish_beet_using_command miss' -r -s f -l format -f -d 'print with custom format'
+complete -c beet -n '__fish_beet_using_command miss' -s h -l help -f -d 'print help'
+
+
+# ====== completions for missing =====
+complete -c beet -n '__fish_beet_using_command missing' -s c -l count -f -d 'count missing tracks per album'
+complete -c beet -n '__fish_beet_using_command missing' -s t -l total -f -d 'count total of missing tracks'
+complete -c beet -n '__fish_beet_using_command missing' -s a -l album -f -d 'show missing albums for artist instead of tracks'
+complete -c beet -n '__fish_beet_using_command missing' -r -s f -l format -f -d 'print with custom format'
+complete -c beet -n '__fish_beet_using_command missing' -s h -l help -f -d 'print help'
+
+
+# ====== completions for mod =====
+complete -c beet -n '__fish_beet_using_command mod' -s m -l move -f -d 'move files in the library directory'
+complete -c beet -n '__fish_beet_using_command mod' -s M -l nomove -f -d "don't move files in library"
+complete -c beet -n '__fish_beet_using_command mod' -s w -l write -f -d "write new metadata to files' tags (default)"
+complete -c beet -n '__fish_beet_using_command mod' -s W -l nowrite -f -d "don't write metadata (opposite of -w)"
+complete -c beet -n '__fish_beet_using_command mod' -s a -l album -f -d 'match albums instead of tracks'
+complete -c beet -n '__fish_beet_using_command mod' -r -s f -l format -f -d 'print with custom format'
+complete -c beet -n '__fish_beet_using_command mod' -s y -l yes -f -d 'skip confirmation'
+complete -c beet -n '__fish_beet_using_command mod' -s h -l help -f -d 'print help'
+
+
+# ====== completions for modify =====
+complete -c beet -n '__fish_beet_using_command modify' -s m -l move -f -d 'move files in the library directory'
+complete -c beet -n '__fish_beet_using_command modify' -s M -l nomove -f -d "don't move files in library"
+complete -c beet -n '__fish_beet_using_command modify' -s w -l write -f -d "write new metadata to files' tags (default)"
+complete -c beet -n '__fish_beet_using_command modify' -s W -l nowrite -f -d "don't write metadata (opposite of -w)"
+complete -c beet -n '__fish_beet_using_command modify' -s a -l album -f -d 'match albums instead of tracks'
+complete -c beet -n '__fish_beet_using_command modify' -r -s f -l format -f -d 'print with custom format'
+complete -c beet -n '__fish_beet_using_command modify' -s y -l yes -f -d 'skip confirmation'
+complete -c beet -n '__fish_beet_using_command modify' -s h -l help -f -d 'print help'
+
+
+# ====== completions for mv =====
+complete -c beet -n '__fish_beet_using_command mv' -r -s d -l dest -f -d 'destination directory'
+complete -c beet -n '__fish_beet_using_command mv' -s c -l copy -f -d 'copy instead of moving'
+complete -c beet -n '__fish_beet_using_command mv' -s p -l pretend -f -d "show how files would be moved, but don't touch anything"
+complete -c beet -n '__fish_beet_using_command mv' -s t -l timid -f -d 'always confirm all actions'
+complete -c beet -n '__fish_beet_using_command mv' -s e -l export -f -d 'copy without changing the database path'
+complete -c beet -n '__fish_beet_using_command mv' -s a -l album -f -d 'match albums instead of tracks'
+complete -c beet -n '__fish_beet_using_command mv' -s h -l help -f -d 'print help'
+
+
+# ====== completions for move =====
+complete -c beet -n '__fish_beet_using_command move' -r -s d -l dest -f -d 'destination directory'
+complete -c beet -n '__fish_beet_using_command move' -s c -l copy -f -d 'copy instead of moving'
+complete -c beet -n '__fish_beet_using_command move' -s p -l pretend -f -d "show how files would be moved, but don't touch anything"
+complete -c beet -n '__fish_beet_using_command move' -s t -l timid -f -d 'always confirm all actions'
+complete -c beet -n '__fish_beet_using_command move' -s e -l export -f -d 'copy without changing the database path'
+complete -c beet -n '__fish_beet_using_command move' -s a -l album -f -d 'match albums instead of tracks'
+complete -c beet -n '__fish_beet_using_command move' -s h -l help -f -d 'print help'
+
+
+# ====== completions for rm =====
+complete -c beet -n '__fish_beet_using_command rm' -s d -l delete -f -d 'also remove files from disk'
+complete -c beet -n '__fish_beet_using_command rm' -s f -l force -f -d 'do not ask when removing items'
+complete -c beet -n '__fish_beet_using_command rm' -s a -l album -f -d 'match albums instead of tracks'
+complete -c beet -n '__fish_beet_using_command rm' -s h -l help -f -d 'print help'
+
+
+# ====== completions for remove =====
+complete -c beet -n '__fish_beet_using_command remove' -s d -l delete -f -d 'also remove files from disk'
+complete -c beet -n '__fish_beet_using_command remove' -s f -l force -f -d 'do not ask when removing items'
+complete -c beet -n '__fish_beet_using_command remove' -s a -l album -f -d 'match albums instead of tracks'
+complete -c beet -n '__fish_beet_using_command remove' -s h -l help -f -d 'print help'
+
+
+# ====== completions for spotify =====
+complete -c beet -n '__fish_beet_using_command spotify' -r -s m -l mode -f -d '"open" to open Spotify with playlist, "list" to print (default)'
+complete -c beet -n '__fish_beet_using_command spotify' -s f -l show-failures -f -d 'list tracks that did not match a Spotify ID'
+complete -c beet -n '__fish_beet_using_command spotify' -s h -l help -f -d 'print help'
+
+
+# ====== completions for stats =====
+complete -c beet -n '__fish_beet_using_command stats' -s e -l exact -f -d 'exact size and time'
+complete -c beet -n '__fish_beet_using_command stats' -s h -l help -f -d 'print help'
+
+
+# ====== completions for upd =====
+complete -c beet -n '__fish_beet_using_command upd' -s a -l album -f -d 'match albums instead of tracks'
+complete -c beet -n '__fish_beet_using_command upd' -r -s f -l format -f -d 'print with custom format'
+complete -c beet -n '__fish_beet_using_command upd' -s m -l move -f -d 'move files in the library directory'
+complete -c beet -n '__fish_beet_using_command upd' -s M -l nomove -f -d "don't move files in library"
+complete -c beet -n '__fish_beet_using_command upd' -s p -l pretend -f -d 'show all changes but do nothing'
+complete -c beet -n '__fish_beet_using_command upd' -r -s F -l field -f -d 'list of fields to update'
+complete -c beet -n '__fish_beet_using_command upd' -s h -l help -f -d 'print help'
+
+
+# ====== completions for up =====
+complete -c beet -n '__fish_beet_using_command up' -s a -l album -f -d 'match albums instead of tracks'
+complete -c beet -n '__fish_beet_using_command up' -r -s f -l format -f -d 'print with custom format'
+complete -c beet -n '__fish_beet_using_command up' -s m -l move -f -d 'move files in the library directory'
+complete -c beet -n '__fish_beet_using_command up' -s M -l nomove -f -d "don't move files in library"
+complete -c beet -n '__fish_beet_using_command up' -s p -l pretend -f -d 'show all changes but do nothing'
+complete -c beet -n '__fish_beet_using_command up' -r -s F -l field -f -d 'list of fields to update'
+complete -c beet -n '__fish_beet_using_command up' -s h -l help -f -d 'print help'
+
+
+# ====== completions for update =====
+complete -c beet -n '__fish_beet_using_command update' -s a -l album -f -d 'match albums instead of tracks'
+complete -c beet -n '__fish_beet_using_command update' -r -s f -l format -f -d 'print with custom format'
+complete -c beet -n '__fish_beet_using_command update' -s m -l move -f -d 'move files in the library directory'
+complete -c beet -n '__fish_beet_using_command update' -s M -l nomove -f -d "don't move files in library"
+complete -c beet -n '__fish_beet_using_command update' -s p -l pretend -f -d 'show all changes but do nothing'
+complete -c beet -n '__fish_beet_using_command update' -r -s F -l field -f -d 'list of fields to update'
+complete -c beet -n '__fish_beet_using_command update' -s h -l help -f -d 'print help'
+
+
+# ====== completions for version =====
+complete -c beet -n '__fish_beet_using_command version' -s h -l help -f -d 'print help'
+
+
+# ====== completions for write =====
+complete -c beet -n '__fish_beet_using_command write' -s p -l pretend -f -d 'show all changes but do nothing'
+complete -c beet -n '__fish_beet_using_command write' -s f -l force -f -d 'write tags even if the existing tags match the database'
+complete -c beet -n '__fish_beet_using_command write' -s h -l help -f -d 'print help' \ No newline at end of file
diff --git a/.config/fish/config.fish b/.config/fish/config.fish
index 2fddce3..59a861c 100644
--- a/.config/fish/config.fish
+++ b/.config/fish/config.fish
@@ -1,12 +1,9 @@
if not status is-interactive
exit
end
-
-# fish
-set -gx FISH_CFG $XDG_CONFIG_HOME/fish
-set -g fish_greeting
-source $FISH_CFG/path.fish
-set -gx LANG "en_US.UTF-8"
+# for agent in $(pidof ssh-agent | string split " ")
+# kill $agent
+# end
# xdg
set -gx XDG_CONFIG_HOME "$HOME/.config"
@@ -14,15 +11,39 @@ set -gx XDG_CACHE_HOME "$HOME/.cache"
set -gx XDG_DATA_HOME "$HOME/.local/share"
set -gx XDG_STATE_HOME "$HOME/.local/state"
+# fish
+set -gx FISH_CFG $XDG_CONFIG_HOME/fish
+set -g fish_greeting
+set -gx LANG "en_US.UTF-8"
+
+# home pollution fixes
+set -gx CARGO_HOME "$XDG_DATA_HOME/cargo"
+set -gx RUSTUP_HOME "$XDG_DATA_HOME/rustup"
+set -gx GOPATH "$XDG_DATA_HOME/go"
+set -gx GRADLE_USER_HOME "$XDG_DATA_HOME/gradle"
+set -gx GTK2_RC_FILES "$XDG_CONFIG_HOME/gtk-2.0/gtkrc"
+set -gx XCURSOR_PATH "/usr/share/icons:$XDG_DATA_HOME/icons"
+set -gx LESSHISTFILE "$XDG_STATE_HOME/less/history"
+set -gx NODE_REPL_HISTORY "$XDG_DATA_HOME/node_repl_history"
+set -gx NPM_CONFIG_USERCONFIG "$XDG_CONFIG_HOME/npm/npmrc"
+set -gx _JAVA_OPTIONS "-Djava.util.prefs.userRoot=$XDG_CONFIG_HOME/java"
+set -gx NUGET_PACKAGES "$XDG_CACHE_HOME/NuGetPackages"
+set -gx PASSWORD_STORE_DIR "$XDG_DATA_HOME/pass"
+set -gx PYTHONSTARTUP "/etc/python/pythonrc"
+set -gx W3M_DIR "$XDG_DATA_HOME/w3m"
+set -gx PNPM_HOME "$HOME/.local/share/pnpm"
+
# program
set -gx EDITOR "nvim"
-set -gx VISUAL "lite-xl"
+# set -gx VISUAL "lite-xl"
+set -gx MANPAGER "nvim +Man!"
+set -gx MANWIDTH 999
set -gx PF_INFO "ascii title os wm editor shell kernel palette"
set -gx BAT_THEME "ansi"
-set -gx PNPM_HOME "$HOME/.local/share/pnpm"
-set -gx GTK_USE_PORTAL 1
set -gx LITE_SCALE 1.3
set -gx RANGER_LOAD_DEFAULT_RC "FALSE"
+# set -gx MANPATH /usr/local/texlive/2023/texmf-dist/doc/man
+set -gx INFOPATH /usr/local/texlive/2023/texmf-dist/doc/info
# $PATH
source $FISH_CFG/path.fish
@@ -36,20 +57,44 @@ source $FISH_CFG/bindings.fish
# configs
source $FISH_CFG/done_cfg.fish
+# source /opt/asdf-vm/asdf.fish
+
# abbreviations & aliases
abbr -a paru paru --limit 10
-abbr -a projects cd ~/DesktopTemp/RealProjects
+abbr -a projects cd ~/Documents/RealProjects
abbr -a gc git commit -m
abbr -a ga git add
abbr -a gcl git clone
+abbr -a pq pacman -Qqi
+abbr -a gt git log --oneline --graph --decorate --all
alias imgcat="wezterm imgcat"
alias dots="git --git-dir=$HOME/.dots --work-tree=$HOME"
-alias sudo="doas"
+# alias sudo="doas"
alias ls="ls --color=auto"
+alias grep="grep -P --color=always"
+alias molest="sudo touch"
+alias wget="wget --hsts-file=$XDG_DATA_HOME/wget-hsts"
+alias stylua="stylua -s"
+alias cd="z"
+alias n="nvim ."
+function y
+ set tmp (mktemp -t "yazi-cwd.XXXXXX")
+ yazi $argv --cwd-file="$tmp"
+ if set cwd (command cat -- "$tmp"); and [ -n "$cwd" ]; and [ "$cwd" != "$PWD" ]
+ builtin cd -- "$cwd"
+ end
+ rm -f -- "$tmp"
+end
function ssh
export TERM=xterm-color
/usr/bin/ssh $argv
- export TERM=xterm-kitty
+ export TERM=xterm-256color
+end
+function flac_preview
+ # nsxiv (ffmpeg -i $argv[1] -v 8 -t 30 -lavfi showspectrumpic=s=hd720 -f image2 pipe: | psub)
+ nsxiv (ffmpeg -i $argv[1] -v 8 -t 30 -lavfi showspectrumpic=s=2k -f image2 pipe: | psub)
+
+
end
function multicd
echo (string repeat -n (math (string length -- $argv[1]) - 1) ../)
@@ -58,8 +103,16 @@ abbr -a dotdot --regex '^\.\.+$' --position anywhere --function multicd
function last_history_item
echo $history[1]
end
+function post_exec_line --on-event fish_postexec
+ if not string match -qr "^clear" $argv[1]
+ echo ""
+ end
+end
abbr -a !! --position anywhere --function last_history_item
# shell init
-eval "$(ssh-agent -c)"
+# eval "$(ssh-agent -c)" &>/dev/null
+fish_ssh_agent
starship init fish | source
+zoxide init fish | source
+direnv hook fish | source
diff --git a/.config/fish/fish_variables b/.config/fish/fish_variables
index fc6415f..2ac8f35 100644
--- a/.config/fish/fish_variables
+++ b/.config/fish/fish_variables
@@ -1,11 +1,11 @@
# This file contains fish universal variable definitions.
# VERSION: 3.0
SETUVAR __done_exclude:lite\x2dxl
-SETUVAR __fish_initialized:3400
-SETUVAR fish_color_autosuggestion:\x238893a5
+SETUVAR __fish_initialized:3800
+SETUVAR fish_color_autosuggestion:\x2377828c
SETUVAR fish_color_cancel:\x2dr
SETUVAR fish_color_command:white
-SETUVAR fish_color_comment:\x238893a5
+SETUVAR fish_color_comment:\x2377828c
SETUVAR fish_color_cwd:green
SETUVAR fish_color_cwd_root:red
SETUVAR fish_color_end:pink
@@ -26,9 +26,9 @@ SETUVAR fish_color_user:brgreen
SETUVAR fish_color_valid_path:white
SETUVAR fish_greeting:\x1d
SETUVAR fish_key_bindings:fish_default_key_bindings
-SETUVAR fish_pager_color_completion:\x238893a5
-SETUVAR fish_pager_color_description:\x238893a5
+SETUVAR fish_pager_color_completion:\x2377828c
+SETUVAR fish_pager_color_description:\x2377828c
SETUVAR fish_pager_color_prefix:white
-SETUVAR fish_pager_color_progress:\x238893a5
+SETUVAR fish_pager_color_progress:\x2377828c
SETUVAR fish_pager_color_selected_background:\x2d\x2dbackground\x3dblack
-SETUVAR fish_user_paths:/home/delta/\x2elocal/share/pnpm\x1e/home/delta/\x2elocal/bin\x1e/home/delta/\x2ecargo/bin\x1e/home/delta/\x2elocal/share/gem/ruby/3\x2e0\x2e0/bin\x1e/home/delta/\x2espicetify\x1e/home/delta/go/bin\x1e/home/delta/\x2edeno/bin
+SETUVAR fish_user_paths:/home/delta/\x2elocal/share/pnpm\x1e/home/delta/\x2elocal/bin\x1e/home/delta/\x2elocal/share/cargo/bin\x1e/home/delta/\x2elocal/share/gem/ruby/3\x2e0\x2e0/bin\x1e/home/delta/\x2elocal/share/go/bin\x1e/home/delta/\x2edetaspace/bin\x1e/usr/local/texlive/2023/bin/x86_64\x2dlinux
diff --git a/.config/fish/functions/fish_commandline_toggle.fish b/.config/fish/functions/fish_commandline_toggle.fish
new file mode 100644
index 0000000..688c97e
--- /dev/null
+++ b/.config/fish/functions/fish_commandline_toggle.fish
@@ -0,0 +1,26 @@
+function __commandline_stash -d 'Stash current command line'
+ set -g __stash_command_position (commandline -C)
+ set -g __stash_command (commandline -b)
+ commandline -r ""
+end
+
+function __commandline_pop -d 'Pop last stashed command line'
+ if not set -q __stash_command
+ return
+ end
+ commandline -r $__stash_command
+ if set -q __stash_command_position
+ commandline -C $__stash_command_position
+ end
+ set -e __stash_command
+ set -e __stash_command_position
+end
+
+function fish_commandline_toggle -d 'Stash current commandline if not empty, otherwise pop last stashed commandline'
+ set -l cmd (commandline -b)
+ if test "$cmd"
+ __commandline_stash
+ else
+ __commandline_pop
+ end
+end
diff --git a/.config/fish/functions/fish_ssh_agent.fish b/.config/fish/functions/fish_ssh_agent.fish
new file mode 100644
index 0000000..5960b75
--- /dev/null
+++ b/.config/fish/functions/fish_ssh_agent.fish
@@ -0,0 +1,32 @@
+function __ssh_agent_is_started -d "check if ssh agent is already started"
+ if begin; test -f $SSH_ENV; and test -z "$SSH_AGENT_PID"; end
+ source $SSH_ENV > /dev/null
+ end
+
+ if test -z "$SSH_AGENT_PID"
+ return 1
+ end
+
+ ps -ef | grep $SSH_AGENT_PID | grep -v grep | grep -q ssh-agent
+ #pgrep ssh-agent
+ return $status
+end
+
+
+function __ssh_agent_start -d "start a new ssh agent"
+ ssh-agent -c | sed 's/^echo/#echo/' > $SSH_ENV
+ chmod 600 $SSH_ENV
+ source $SSH_ENV > /dev/null
+ true # suppress errors from setenv, i.e. set -gx
+end
+
+
+function fish_ssh_agent --description "Start ssh-agent if not started yet, or uses already started ssh-agent."
+ if test -z "$SSH_ENV"
+ set -xg SSH_ENV $HOME/.ssh/environment
+ end
+
+ if not __ssh_agent_is_started
+ __ssh_agent_start
+ end
+end
diff --git a/.config/fish/path.fish b/.config/fish/path.fish
index d0d3144..b977e7c 100644
--- a/.config/fish/path.fish
+++ b/.config/fish/path.fish
@@ -1,20 +1,14 @@
alias fp="fish_add_path -a"
-# function pa
-# set -x PATH PATH $argv[1]
-# end
-#
-# function pp
-# set -x PATH $argv[1] PATH
-# end
-
-# ik this is not the "correct" way to set path but persistent path sucks
+#set -gx PATH $PNPM_HOME ~/.local/bin $CARGO_HOME/bin ~/.local/share/gem/ruby/3.0.0/bin $GOPATH/bin ~/.detaspace/bin $PATH
fp $PNPM_HOME
-fp ~/.deno/bin
fp ~/.local/bin
fp ~/usr/bin
-fp ~/.cargo/bin
+fp $CARGO_HOME/bin
fp ~/.local/share/gem/ruby/3.0.0/bin
-fp ~/.spicetify
-fp ~/go/bin
+#fp ~/.spicetify
+fp $GOPATH/bin
+fp ~/.detaspace/bin
+fp /usr/local/texlive/2023/bin/x86_64-linux
+fp ~/.nix-profile/bin
diff --git a/.config/nvim/.luarc.json b/.config/nvim/.luarc.json
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.config/nvim/.luarc.json
diff --git a/.config/nvim/.nvim.lua b/.config/nvim/.nvim.lua
new file mode 100644
index 0000000..9c7049d
--- /dev/null
+++ b/.config/nvim/.nvim.lua
@@ -0,0 +1,2 @@
+vim.env.GIT_DIR = vim.fn.expand("~/.dots")
+vim.env.GIT_WORK_TREE = vim.fn.expand("~")
diff --git a/.config/nvim/init.lua b/.config/nvim/init.lua
index b74b4af..91bfca2 100644
--- a/.config/nvim/init.lua
+++ b/.config/nvim/init.lua
@@ -2,4 +2,13 @@ local lazypath = vim.fn.stdpath "data" .. "/lazy/lazy.nvim"
vim.opt.rtp:prepend(lazypath)
require "options"
-require("lazy").setup "plugins"
+require "cmds"
+require "autocmd"
+require "binds"
+require("lazy").setup {
+ spec = "plugins",
+ dev = {
+ path = vim.fn.stdpath "config" .. "/lua/local_plugins"
+ }
+}
+vim.cmd[[colo prismite]]
diff --git a/.config/nvim/lazy-lock.json b/.config/nvim/lazy-lock.json
index ff8a865..229bff2 100644
--- a/.config/nvim/lazy-lock.json
+++ b/.config/nvim/lazy-lock.json
@@ -1,17 +1,35 @@
{
- "alpha-nvim": { "branch": "main", "commit": "63a860e7ed3ae41ee92481ea65a48fb35431ae21" },
- "better-escape.nvim": { "branch": "master", "commit": "7031dc734add47bb71c010e0551829fa5799375f" },
- "dressing.nvim": { "branch": "master", "commit": "ee571505f3566f84fd252e76c4ce6df6eaf2fb94" },
- "indent-blankline.nvim": { "branch": "master", "commit": "9637670896b68805430e2f72cf5d16be5b97a22a" },
- "lazy.nvim": { "branch": "main", "commit": "dac844ed617dda4f9ec85eb88e9629ad2add5e05" },
- "lualine.nvim": { "branch": "master", "commit": "45e27ca739c7be6c49e5496d14fcf45a303c3a63" },
- "mini.move": { "branch": "main", "commit": "3afd39873eb9171684e554a214c055482444a47d" },
- "neo-tree.nvim": { "branch": "main", "commit": "8a0f795bac6618e4fe59eda61b15f8c95d9625ad" },
- "noice.nvim": { "branch": "main", "commit": "894db25ec726d32047799d4d0a982b701bec453b" },
- "nui.nvim": { "branch": "main", "commit": "9e3916e784660f55f47daa6f26053ad044db5d6a" },
- "nvim-treesitter": { "branch": "master", "commit": "d0b17cc0b9c8c3055530770a9dd4de659232c692" },
- "nvim-web-devicons": { "branch": "master", "commit": "cfc8824cc1db316a276b36517f093baccb8e799a" },
- "plenary.nvim": { "branch": "master", "commit": "0dbe561ae023f02c2fb772b879e905055b939ce3" },
- "telescope.nvim": { "branch": "master", "commit": "207285ccec21b69996a4d3bcfa59df35d48610e8" },
- "tokyonight.nvim": { "branch": "main", "commit": "1ee11019f8a81dac989ae1db1a013e3d582e2033" }
-} \ No newline at end of file
+ "LuaSnip": { "branch": "master", "commit": "fb525166ccc30296fb3457441eb979113de46b00" },
+ "alpha-nvim": { "branch": "main", "commit": "a35468cd72645dbd52c0624ceead5f301c566dff" },
+ "better-escape.nvim": { "branch": "master", "commit": "19a38aab94961016430905ebec30d272a01e9742" },
+ "cmp-nvim-lsp": { "branch": "main", "commit": "a8912b88ce488f411177fc8aed358b04dc246d7b" },
+ "cmp-path": { "branch": "main", "commit": "c6635aae33a50d6010bf1aa756ac2398a2d54c32" },
+ "cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" },
+ "dressing.nvim": { "branch": "master", "commit": "2d7c2db2507fa3c4956142ee607431ddb2828639" },
+ "git-blame.nvim": { "branch": "master", "commit": "8503b199edf9a666fe7b1a989cf14e3c26b2eb03" },
+ "indentmini.nvim": { "branch": "main", "commit": "6211f93b0c8161d2a2b4000b9bf0c01c0a115455" },
+ "lazy.nvim": { "branch": "main", "commit": "6c3bda4aca61a13a9c63f1c1d1b16b9d3be90d7a" },
+ "leap.nvim": { "branch": "main", "commit": "10c14af4ddfb34dbd7721f0bfb2b4d91f0558907" },
+ "lualine.nvim": { "branch": "master", "commit": "a94fc68960665e54408fe37dcf573193c4ce82c9" },
+ "marks.nvim": { "branch": "master", "commit": "f353e8c08c50f39e99a9ed474172df7eddd89b72" },
+ "mason-lspconfig.nvim": { "branch": "main", "commit": "f54e3c11fc9ebfcfc27e696182b0295b071d0811" },
+ "mason.nvim": { "branch": "main", "commit": "8024d64e1330b86044fed4c8494ef3dcd483a67c" },
+ "mini.colors": { "branch": "main", "commit": "ef76867adda63d6010acdc8732a816c8527d276b" },
+ "mini.comment": { "branch": "main", "commit": "51c173dffa17dc14c81169deaeea430bd394ab51" },
+ "mini.surround": { "branch": "main", "commit": "1a2b59c77a0c4713a5bd8972da322f842f4821b1" },
+ "noice.nvim": { "branch": "main", "commit": "0427460c2d7f673ad60eb02b35f5e9926cf67c59" },
+ "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" },
+ "nvim-cmp": { "branch": "main", "commit": "b5311ab3ed9c846b585c0c15b7559be131ec4be9" },
+ "nvim-cokeline": { "branch": "main", "commit": "9fbed130683b7b6f73198c09e35ba4b33f547c08" },
+ "nvim-colorizer.lua": { "branch": "master", "commit": "517df88cf2afb36652830df2c655df2da416a0ae" },
+ "nvim-lspconfig": { "branch": "master", "commit": "314b35335cc84bc2a085c84c69da955ba22da163" },
+ "nvim-notify": { "branch": "master", "commit": "b5825cf9ee881dd8e43309c93374ed5b87b7a896" },
+ "nvim-tree.lua": { "branch": "master", "commit": "0a06f65bf06157972f20ca1dee03c97a0efcb188" },
+ "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" },
+ "nvim-web-devicons": { "branch": "master", "commit": "1fb58cca9aebbc4fd32b086cb413548ce132c127" },
+ "plenary.nvim": { "branch": "master", "commit": "857c5ac632080dba10aae49dba902ce3abf91b35" },
+ "sessions.nvim": { "branch": "master", "commit": "f13158483e0b6255c6dfe473145ce4ee3495d844" },
+ "telescope.nvim": { "branch": "master", "commit": "b4da76be54691e854d3e0e02c36b0245f945c2c7" },
+ "trouble.nvim": { "branch": "main", "commit": "85bedb7eb7fa331a2ccbecb9202d8abba64d37b3" },
+ "vim-repeat": { "branch": "master", "commit": "65846025c15494983dafe5e3b46c8f88ab2e9635" }
+}
diff --git a/.config/nvim/lua/autocmd.lua b/.config/nvim/lua/autocmd.lua
new file mode 100644
index 0000000..f13e2e9
--- /dev/null
+++ b/.config/nvim/lua/autocmd.lua
@@ -0,0 +1,34 @@
+local group = vim.api.nvim_create_augroup("PrismiteNvim", { clear = true })
+vim.api.nvim_create_autocmd({ "BufEnter", "BufWinEnter" }, {
+ group = group,
+ callback = function()
+ if require("nvim-tree.utils").is_nvim_tree_buf() then
+ vim.cmd "stopinsert"
+ end
+ end,
+})
+
+-- By https://github.com/marvinth01, taken from https://github.com/nvim-tree/nvim-tree.lua/wiki/Auto-Close#marvinth01
+vim.api.nvim_create_autocmd("QuitPre", {
+ group = group,
+ callback = function()
+ local tree_wins = {}
+ local floating_wins = {}
+ local wins = vim.api.nvim_list_wins()
+ for _, w in ipairs(wins) do
+ local bufname = vim.api.nvim_buf_get_name(vim.api.nvim_win_get_buf(w))
+ if bufname:match "NvimTree_" ~= nil then
+ table.insert(tree_wins, w)
+ end
+ if vim.api.nvim_win_get_config(w).relative ~= "" then
+ table.insert(floating_wins, w)
+ end
+ end
+ if 1 == #wins - #floating_wins - #tree_wins then
+ -- Should quit, so we close all invalid windows.
+ for _, w in ipairs(tree_wins) do
+ vim.api.nvim_win_close(w, true)
+ end
+ end
+ end,
+})
diff --git a/.config/nvim/lua/binds.lua b/.config/nvim/lua/binds.lua
new file mode 100644
index 0000000..55da905
--- /dev/null
+++ b/.config/nvim/lua/binds.lua
@@ -0,0 +1,54 @@
+local map = vim.keymap.set
+-- local color_converter = require "color_converter"
+
+-- toggle nvim-tree
+map("n", "<Leader>t", "<cmd>NvimTreeToggle<CR>")
+-- toggle trouble
+map("n", "<Leader>e", "<cmd>TroubleToggle<CR>")
+-- undo
+map("n", "U", "<C-r>")
+
+map("c", "<CR>", function()
+ if vim.fn.pumvisible() == 1 then
+ return "<C-y>"
+ end
+ return "<CR>"
+end, { expr = true })
+
+map("n", "H", "5h")
+map("n", "J", "5j")
+map("n", "K", "5k")
+map("n", "L", "5l")
+
+-- disable arrow keys
+map("n", "<UP>", "<NOP>")
+map("n", "<DOWN>", "<NOP>")
+map("n", "<LEFT>", "<NOP>")
+map("n", "<RIGHT>", "<NOP>")
+
+map("i", "<UP>", "<NOP>")
+map("i", "<DOWN>", "<NOP>")
+map("i", "<LEFT>", "<NOP>")
+map("i", "<RIGHT>", "<NOP>")
+
+map("n", "<Leader>h", vim.lsp.buf.hover)
+map("n", "<Leader>gd", vim.lsp.buf.definition)
+
+map("n", "<leader>bp", function()
+ require('cokeline.mappings').pick("focus")
+end, { desc = "Pick a buffer to focus" })
+map("n", "<leader>bq", function()
+ require('cokeline.mappings').pick("close")
+end, { desc = "Pick a buffer to close" })
+map("n", "<Leader>p", function ()
+ require("cokeline.mappings").by_step("switch", -1)
+end, { silent = true })
+map("n", "<Leader>n", function ()
+ require("cokeline.mappings").by_step("switch", 1)
+end, { silent = true })
+
+
+-- local function setup_lsp_keys(_client, buffer)
+-- map("<Leader>d", vim.diagnostic.open_float, "Line diagnostics")
+--
+-- end
diff --git a/.config/nvim/lua/cmds.lua b/.config/nvim/lua/cmds.lua
new file mode 100644
index 0000000..1426b99
--- /dev/null
+++ b/.config/nvim/lua/cmds.lua
@@ -0,0 +1,20 @@
+local M = {}
+local fn = vim.fn
+
+local function cabbrev(input, replace)
+ local cmd = 'cnoreabbrev <expr> %s v:lua.cmds.command("%s", "%s")'
+
+ vim.cmd(cmd:format(input, input, replace))
+end
+
+function M.command(cmd, match)
+ if fn.getcmdtype() == ":" and fn.getcmdline():match("^" .. cmd) then
+ return match
+ else
+ return cmd
+ end
+end
+
+cabbrev("vs", "vert sb")
+
+return M
diff --git a/.config/nvim/lua/dash.lua b/.config/nvim/lua/dash.lua
new file mode 100644
index 0000000..fd9f03c
--- /dev/null
+++ b/.config/nvim/lua/dash.lua
@@ -0,0 +1,147 @@
+local lazy = require "lazy"
+local alpha = require "alpha"
+
+vim.api.nvim_create_autocmd("ColorScheme", {
+ callback = function()
+ local groups = {
+ Red = "#ff928a",
+ Orange = "#ff9f6f",
+ Yellow = "#ecb256",
+ Green = "#8bd294",
+ Cyan = "#6dd3c2",
+ Blue = "#8bc3fc",
+ Purple = "#c4b1f6",
+ Pink = "#e5acb4",
+ }
+ for group, color in pairs(groups) do
+ vim.api.nvim_set_hl(0, "Dash" .. group, { fg = color, bold = true })
+ end
+ local comment = vim.api.nvim_get_hl(0, { name = "Comment" })
+ vim.api.nvim_set_hl(0, "DashEmphasis", vim.tbl_deep_extend("keep", comment, { bold = true }))
+ end,
+})
+
+
+return {
+ layout = {
+ {
+ type = "padding",
+ val = 4,
+ },
+ {
+ type = "text",
+ val = {
+ [[ ___ ___ ___ ___ ___ ___ ___ ___ ]],
+ [[ /\ \ /\ \ /\ \ /\ \ /\__\ /\ \ /\ \ /\ \ ]],
+ [[ /::\ \ /::\ \ _\:\ \ /::\ \ /::L_L_ _\:\ \ \:\ \ /::\ \ ]],
+ [[ /::\:\__\ /::\:\__\ /\/::\__\ /\:\:\__\ /:/L:\__\ /\/::\__\ /::\__\ /::\:\__\ ]],
+ [[ \/\::/ / \;:::/ / \::/\/__/ \:\:\/__/ \/_/:/ / \::/\/__/ /:/\/__/ \:\:\/ / ]],
+ [[ \/__/ |:\/__/ \:\__\ \::/ / /:/ / \:\__\ \/__/ \:\/ / ]],
+ [[ \|__| \/__/ \/__/ \/__/ \/__/ \/__/ ]],
+ [[ ]],
+ },
+ opts = {
+ position = "center",
+ hl = {
+ {
+ { "DashRed", 1, 10 },
+ { "DashOrange", 10, 20 },
+ { "DashYellow", 20, 30 },
+ { "DashGreen", 30, 40 },
+ { "DashCyan", 40, 50 },
+ { "DashBlue", 50, 60 },
+ { "DashPurple", 60, 70 },
+ { "DashPink", 70, 80 },
+ },
+ {
+ { "DashRed", 1, 10 },
+ { "DashOrange", 10, 20 },
+ { "DashYellow", 20, 30 },
+ { "DashGreen", 30, 40 },
+ { "DashCyan", 40, 50 },
+ { "DashBlue", 50, 60 },
+ { "DashPurple", 60, 70 },
+ { "DashPink", 70, 80 },
+ },
+ {
+ { "DashRed", 1, 10 },
+ { "DashOrange", 10, 20 },
+ { "DashYellow", 20, 30 },
+ { "DashGreen", 30, 40 },
+ { "DashCyan", 40, 50 },
+ { "DashBlue", 50, 60 },
+ { "DashPurple", 60, 70 },
+ { "DashPink", 70, 80 },
+ },
+ {
+ { "DashRed", 1, 10 },
+ { "DashOrange", 10, 20 },
+ { "DashYellow", 20, 30 },
+ { "DashGreen", 30, 40 },
+ { "DashCyan", 40, 50 },
+ { "DashBlue", 50, 60 },
+ { "DashPurple", 60, 70 },
+ { "DashPink", 70, 80 },
+ },
+ {
+ { "DashRed", 1, 10 },
+ { "DashOrange", 10, 20 },
+ { "DashYellow", 20, 30 },
+ { "DashGreen", 30, 40 },
+ { "DashCyan", 40, 50 },
+ { "DashBlue", 50, 60 },
+ { "DashPurple", 60, 70 },
+ { "DashPink", 70, 80 },
+ },
+ {
+ { "DashRed", 1, 10 },
+ { "DashOrange", 10, 20 },
+ { "DashYellow", 20, 30 },
+ { "DashGreen", 30, 40 },
+ { "DashCyan", 40, 50 },
+ { "DashBlue", 50, 60 },
+ { "DashPurple", 60, 70 },
+ { "DashPink", 70, 80 },
+ },
+ {
+ { "DashRed", 1, 10 },
+ { "DashOrange", 10, 20 },
+ { "DashYellow", 20, 30 },
+ { "DashGreen", 30, 40 },
+ { "DashCyan", 40, 50 },
+ { "DashBlue", 50, 60 },
+ { "DashPurple", 60, 70 },
+ { "DashPink", 70, 80 },
+ },
+ },
+ },
+ },
+ {
+ type = "padding",
+ val = 2,
+ },
+ {
+ type = "group",
+ val = {
+ {
+ type = "text",
+ val = function() return lazy.stats().loaded .. " plugin" .. (lazy.stats().loaded == 1 and "" or "s") .. " loaded" end,
+ opts = {
+ position = "center",
+ }
+ },
+ {
+ type = "text",
+ val = "prismite | a theme made by delta___",
+ opts = {
+ position = "center",
+ hl = {
+ { "Comment", 0, 27 },
+ { "DashEmphasis", 27, 35 }
+ }
+ }
+ }
+ },
+ },
+ },
+}
diff --git a/.config/nvim/lua/icons.lua b/.config/nvim/lua/icons.lua
new file mode 100644
index 0000000..e058e29
--- /dev/null
+++ b/.config/nvim/lua/icons.lua
@@ -0,0 +1,45 @@
+return {
+ diagnostics = {
+ Error = " ",
+ Warn = " ",
+ Hint = " ",
+ Info = " ",
+ },
+ kinds = {
+ Array = " ",
+ Boolean = " ",
+ Class = " ",
+ Color = " ",
+ Constant = " ",
+ Constructor = " ",
+ Copilot = " ",
+ Enum = " ",
+ EnumMember = " ",
+ Event = " ",
+ Field = " ",
+ File = " ",
+ Folder = " ",
+ Function = " ",
+ Interface = " ",
+ Key = " ",
+ Keyword = " ",
+ Method = " ",
+ Module = " ",
+ Namespace = " ",
+ Null = " ",
+ Number = " ",
+ Object = " ",
+ Operator = " ",
+ Package = " ",
+ Property = " ",
+ Reference = " ",
+ Snippet = " ",
+ String = " ",
+ Struct = " ",
+ Text = " ",
+ TypeParameter = " ",
+ Unit = " ",
+ Value = " ",
+ Variable = " ",
+ },
+}
diff --git a/.config/nvim/lua/local_plugins/color_converter/lua/color_converter.lua b/.config/nvim/lua/local_plugins/color_converter/lua/color_converter.lua
new file mode 100644
index 0000000..ff8aa70
--- /dev/null
+++ b/.config/nvim/lua/local_plugins/color_converter/lua/color_converter.lua
@@ -0,0 +1,14 @@
+local menu = require "ui"
+
+return {
+ setup = function ()
+ vim.api.nvim_create_user_command(
+ "ColorConverter",
+ function()
+ menu:mount()
+ end,
+ {}
+ )
+ end,
+ config = function(opt) end
+}
diff --git a/.config/nvim/lua/local_plugins/color_converter/lua/ui.lua b/.config/nvim/lua/local_plugins/color_converter/lua/ui.lua
new file mode 100644
index 0000000..a2bf115
--- /dev/null
+++ b/.config/nvim/lua/local_plugins/color_converter/lua/ui.lua
@@ -0,0 +1,47 @@
+local mcolors = require "mini.colors"
+local Menu = require("nui.menu")
+local event = require("nui.utils.autocmd").event
+
+local popup_options = {
+ relative = "cursor",
+ position = {
+ row = 2,
+ col = 1,
+ },
+ border = {
+ style = "rounded",
+ text = {
+ top = "[Choose Item]",
+ top_align = "center",
+ },
+ },
+}
+
+local menu = Menu(popup_options, {
+ lines = {
+ Menu.separator("Group One"),
+ Menu.item("Item 1"),
+ Menu.item("Item 2"),
+ Menu.separator("Group Two", {
+ char = "-",
+ text_align = "right",
+ }),
+ Menu.item("Item 3"),
+ Menu.item("Item 4"),
+ },
+ -- max_width = 20,
+ keymap = {
+ focus_next = { "j", "<Down>", "<Tab>" },
+ focus_prev = { "k", "<Up>", "<S-Tab>" },
+ close = { "<Esc>", "<C-c>" },
+ submit = { "<CR>", "<Space>" },
+ },
+ on_close = function()
+ print("CLOSED")
+ end,
+ on_submit = function(item)
+ print("SUBMITTED", vim.inspect(item))
+ end,
+})
+
+return menu
diff --git a/.config/nvim/lua/options.lua b/.config/nvim/lua/options.lua
index 94ded94..6153825 100644
--- a/.config/nvim/lua/options.lua
+++ b/.config/nvim/lua/options.lua
@@ -1,23 +1,28 @@
local o = vim.o
local opt = vim.opt
-local wo = vim.wo
-local bo = vim.bo
--- Global options --
o.smarttab = true
+o.exrc = true
o.clipboard = "unnamedplus"
-o.termguicolors = 24
+o.termguicolors = true
o.list = true
-opt.listchars = { space = "⋅", tab = " " }
o.autochdir = true
+o.wrap = false
+o.number = true
+o.relativenumber = true
+o.expandtab = true
+o.tabstop = 4
+o.smartindent = true
+o.shiftwidth = 4
+o.sidescroll = 5
+o.timeout= false
+o.scrolloff = 4
+o.sidescrolloff = 4
+o.sidescroll = 1
+o.cursorline = true
+o.mouse = ""
+o.fillchars = 'eob: '
+vim.g.mapleader = " "
+vim.g.maplocalleader = vim.g.mapleader
--- Window options --
-wo.number = true
-wo.relativenumber = true
-
--- Buffer options --
-bo.expandtab = true
-bo.tabstop = 4
-bo.smartindent = true
---bo.softtabstop = 0
-bo.shiftwidth = 2
+opt.listchars = { space = "⋅", tab = "--", precedes = "…", extends = "…" }
diff --git a/.config/nvim/lua/plugins.lua b/.config/nvim/lua/plugins.lua
deleted file mode 100644
index 7cc8321..0000000
--- a/.config/nvim/lua/plugins.lua
+++ /dev/null
@@ -1,83 +0,0 @@
-return {
- { "echasnovski/mini.move", config = true },
- -- QoL
- { "max397574/better-escape.nvim", config = true },
-
- { "nvim-treesitter/nvim-treesitter", build = ":TSUpdate" },
-
- -- UI
- {
- "lukas-reineke/indent-blankline.nvim",
- config = true
- },
- {
- "folke/noice.nvim",
- event = "VeryLazy",
- opts = {
- lsp = {
- -- override markdown rendering so that **cmp** and other plugins use **Treesitter**
- override = {
- ["vim.lsp.util.convert_input_to_markdown_lines"] = true,
- ["vim.lsp.util.stylize_markdown"] = true,
- ["cmp.entry.get_documentation"] = true,
- },
- },
- -- you can enable a preset for easier configuration
- presets = {
- -- bottom_search = true, -- use a classic bottom cmdline for search
- command_palette = true, -- position the cmdline and popupmenu together
- long_message_to_split = true, -- long messages will be sent to a split
- inc_rename = false, -- enables an input dialog for inc-rename.nvim
- lsp_doc_border = false, -- add a border to hover docs and signature help
- },
- },
- dependencies = {
- "MunifTanjim/nui.nvim",
- },
- },
- {
- "nvim-telescope/telescope.nvim",
- config = true,
- dependencies = { "nvim-lua/plenary.nvim" }
- },
- {
- "nvim-lualine/lualine.nvim",
- opts = {
- options = {
- section_separators = { left = " ", right = " " },
- },
- sections = {
- lualine_a = { "mode" },
- lualine_b = { "filename" },
- lualine_c = {},
- lualine_x = { "filetype" },
- lualine_y = { "progress" },
- lualine_z = { "location" },
- },
- },
- },
- { "stevearc/dressing.nvim", config = true },
- {
- "folke/tokyonight.nvim",
- lazy = false,
- priority = 1000,
- config = function()
- vim.cmd [[colorscheme tokyonight-night]]
- end,
- },
- {
- "nvim-neo-tree/neo-tree.nvim",
- dependencies = {
- "nvim-lua/plenary.nvim",
- "nvim-tree/nvim-web-devicons", -- not strictly required, but recommended
- "MunifTanjim/nui.nvim",
- },
- },
- {
- "goolord/alpha-nvim",
- dependencies = { "nvim-tree/nvim-web-devicons" },
- config = function()
- require("alpha").setup(require("alpha.themes.dashboard").config)
- end,
- },
-}
diff --git a/.config/nvim/lua/plugins/coding.lua b/.config/nvim/lua/plugins/coding.lua
new file mode 100644
index 0000000..fd636fa
--- /dev/null
+++ b/.config/nvim/lua/plugins/coding.lua
@@ -0,0 +1,150 @@
+return {
+ -- { "echasnovski/mini.move", config = true },
+ { "echasnovski/mini.comment", config = true },
+ { "max397574/better-escape.nvim", config = true },
+ {
+ "nvim-treesitter/nvim-treesitter",
+ build = ":TSUpdate",
+ config = function()
+ local configs = require "nvim-treesitter.configs"
+ configs.setup {
+ ensure_installed = {
+ "lua",
+ "c",
+ "vim",
+ "vimdoc",
+ "query",
+ "rust",
+ "fish",
+ "json",
+ "javascript",
+ "latex",
+ "markdown",
+ "markdown_inline",
+ "zig",
+ "typescript",
+ "toml",
+ "svelte",
+ "comment",
+ "html",
+ "typst",
+ "ron"
+ },
+ highlight = { enable = true },
+ indent = { enable = true },
+ }
+ end,
+ },
+ {
+ "ggandor/leap.nvim",
+ config = function()
+ local leap = require "leap"
+ leap.add_default_mappings()
+ leap.opts.highlight_unlabeled_phase_one_targets = true
+ end,
+ dependencies = {
+ "tpope/vim-repeat",
+ },
+ },
+ {
+ "hrsh7th/nvim-cmp",
+ opts = function()
+ vim.api.nvim_set_hl(0, "CmpGhostText", { link = "Comment", default = true })
+ local cmp = require "cmp"
+ local defaults = require "cmp.config.default"()
+ return {
+ enabled = function()
+ local context = require "cmp.config.context"
+ if vim.api.nvim_get_mode().mode == "c" then
+ return true
+ else
+ return not context.in_treesitter_capture "comment" and not context.in_syntax_group "Comment"
+ end
+ end,
+
+ completion = {
+ completeopt = "menu,menuone,noinsert",
+ },
+ snippet = {
+ expand = function(args)
+ require("luasnip").lsp_expand(args.body)
+ end,
+ },
+ window = {
+ completion = cmp.config.window.bordered(),
+ documentation = cmp.config.window.bordered(),
+ },
+ mapping = cmp.mapping.preset.insert {
+ ["<C-j>"] = cmp.mapping.select_next_item { behavior = cmp.SelectBehavior.Insert },
+ ["<C-k>"] = cmp.mapping.select_prev_item { behavior = cmp.SelectBehavior.Insert },
+ ["<C-b>"] = cmp.mapping.scroll_docs(-4),
+ ["<C-f>"] = cmp.mapping.scroll_docs(4),
+ ["<S-CR>"] = cmp.mapping.abort(),
+ ["<CR>"] = cmp.mapping.confirm { select = false },
+ },
+ sources = cmp.config.sources {
+ { name = "nvim_lsp" },
+ { name = "path" },
+ },
+ formatting = {
+ format = function(_, item)
+ local icons = require("icons").kinds
+ if icons[item.kind] then
+ item.kind = icons[item.kind] .. item.kind
+ end
+ return item
+ end,
+ },
+ experimental = {
+ ghost_text = {
+ hl_group = "CmpGhostText",
+ },
+ },
+ sorting = defaults.sorting,
+ }
+ end,
+ dependencies = {
+ { "L3MON4D3/LuaSnip", build = "make install_jsregexp" },
+ "hrsh7th/cmp-path",
+ "saadparwaiz1/cmp_luasnip",
+ },
+ disabled = true
+ },
+ {
+ "NvChad/nvim-colorizer.lua",
+ opts = {
+ user_default_options = { RGB = true, RRGGBB = true, RRGGBBAA = true, always_update = true, names = false },
+ },
+ },
+ {
+ "echasnovski/mini.surround",
+ version = false,
+ opts = {
+ mappings = {
+ add = "\\a", -- Add surrounding in Normal and Visual modes
+ delete = "\\d", -- Delete surrounding
+ find = "\\f", -- Find surrounding (to the right)
+ find_left = "\\F", -- Find surrounding (to the left)
+ highlight = "\\h", -- Highlight surrounding
+ replace = "\\r", -- Replace surrounding
+ update_n_lines = "\\n", -- Update `n_lines`
+ }
+ }
+ },
+ {
+ "color_converter",
+ dev = true,
+ dependencies = {
+ { 'echasnovski/mini.colors', version = '*' },
+ "MunifTanjim/nui.nvim",
+ },
+ -- lazy = false,
+ cmd = "ColorConverter",
+ config = true
+ },
+ -- {
+ -- "m4xshen/hardtime.nvim",
+ -- config = true,
+ -- disabled = true
+ -- }
+}
diff --git a/.config/nvim/lua/plugins/colorscheme.lua b/.config/nvim/lua/plugins/colorscheme.lua
new file mode 100644
index 0000000..e0f2a96
--- /dev/null
+++ b/.config/nvim/lua/plugins/colorscheme.lua
@@ -0,0 +1,57 @@
+return {
+ -- {
+ -- "folke/tokyonight.nvim",
+ -- lazy = false,
+ -- priority = 1000,
+ -- config = function()
+ -- vim.cmd [[colorscheme tokyonight-night]]
+ -- end,
+ -- },
+ -- {
+ -- "chadcat7/prism",
+ -- config = function()
+ -- require("prism"):setup {
+ -- customSchemes = {
+ -- {
+ -- name = "prismite",
+ -- background = "#1b2026",
+ -- foreground = "#d9dfe4",
+ -- cursorline = "#1f242b",
+ -- comment = "#434754",
+ -- darker = "#161b22",
+ -- cursor = "#fdc267",
+ -- black = "#1b2026", -- useful when background is transparent
+ -- color0 = "#1f242b",
+ -- color1 = "#df625d",
+ -- color2 = "#91d89a",
+ -- color3 = "#fdc267",
+ -- color4 = "#8ec6ff",
+ -- color5 = "#f2b9c1",
+ -- color6 = "#77e2e3",
+ -- color7 = "#d9dfe4",
+ -- color8 = "#373D41",
+ -- color9 = "#f1726b",
+ -- color10 = "#a1e9aa",
+ -- color11 = "#ffd79d",
+ -- color12 = "#add6ff",
+ -- color13 = "#ffcbd2",
+ -- color14 = "#88f3f3",
+ -- color15 = "#d9dfe4",
+ -- },
+ -- },
+ -- currentTheme = "prismite",
+ -- reset = true,
+ -- reload = { "lualine" },
+ -- }
+ -- end,
+ -- enabled = false
+ -- },
+ {
+ dir = "/home/delta/Documents/RealProjects/lua/prismite.nvim",
+ lazy = false,
+ priority = 1000,
+ config = function()
+ vim.cmd [[colo prismite]]
+ end,
+ },
+}
diff --git a/.config/nvim/lua/plugins/editor.lua b/.config/nvim/lua/plugins/editor.lua
new file mode 100644
index 0000000..47fe8b4
--- /dev/null
+++ b/.config/nvim/lua/plugins/editor.lua
@@ -0,0 +1,24 @@
+return {
+ -- { "Saecki/crates.nvim", config = true },
+ {
+ "nvim-telescope/telescope.nvim",
+ config = true,
+ dependencies = { "nvim-lua/plenary.nvim" },
+ },
+ {
+ "natecraddock/sessions.nvim",
+ config = true
+ },
+ {
+ "f-person/git-blame.nvim",
+ event = "VeryLazy",
+ opts = {
+ date_format = "%r"
+ },
+ },
+ {
+ "chentoast/marks.nvim",
+ -- event = "VeryLazy",
+ config = true
+ }
+}
diff --git a/.config/nvim/lua/plugins/lsp.lua b/.config/nvim/lua/plugins/lsp.lua
new file mode 100644
index 0000000..1063c5d
--- /dev/null
+++ b/.config/nvim/lua/plugins/lsp.lua
@@ -0,0 +1,254 @@
+local icons = require("icons")
+
+-- this is horrible and it should be redone someday
+-- but that day is not today
+
+return {
+ {
+ "neovim/nvim-lspconfig",
+ -- enabled = false,
+ event = { "BufReadPre", "BufNewFile" },
+ dependencies = {
+ { "williamboman/mason.nvim", config = true, lazy = false },
+ { "mason-org/mason-lspconfig.nvim", opts = { automatic_enable = false } },
+ "hrsh7th/cmp-nvim-lsp",
+ },
+ opts = {
+ diagnostics = {
+ },
+ servers = {
+ lua_ls = {
+ settings = {
+ Lua = {
+ -- workspace = {
+ -- checkThirdParty = false,
+ -- library = {
+ -- vim.env.VIMRUNTIME,
+ -- },
+ -- },
+ completion = {
+ showWord = "Disable",
+ displayContext = 8,
+ },
+ hint = {
+ enable = true,
+ },
+ diagnostics = { "trailing-space" },
+ },
+ },
+ },
+ rust_analyzer = { settings = { completion = { fullFunctionSignatures = true } } },
+ ts_ls = {},
+ },
+ setup = {}
+ },
+ config = function(_, opts)
+ local function on_attach(fn)
+ vim.api.nvim_create_autocmd("LspAttach", {
+ callback = function(args)
+ local buffer = args.buf
+ local client = vim.lsp.get_client_by_id(args.data.client_id)
+ fn(client, buffer)
+ end,
+ })
+ end
+
+ ---@param method string
+ ---@param fn fun(client:vim.lsp.Client, buffer)
+ local function on_supports_method(method, fn)
+ -- cache[method] = cache[method] or setmetatable({}, { __mode = "k" })
+ return vim.api.nvim_create_autocmd("User", {
+ pattern = "LspSupportsMethod",
+ callback = function(args)
+ local client = vim.lsp.get_client_by_id(args.data.client_id)
+ local buffer = args.data.buffer ---@type number
+ if client and method == args.data.method then
+ return fn(client, buffer)
+ end
+ end,
+ })
+ end
+
+ -- function _check_methods(client, buffer)
+ -- if
+ -- -- don't trigger on invalid buffers
+ -- not vim.api.nvim_buf_is_valid(buffer) or
+ -- -- don't trigger on non-listed buffers
+ -- vim.bo[buffer].buflisted or
+ -- -- don't trigger on nofile buffers
+ -- vim.bo[buffer].buftype == "nofile"
+ -- then
+ -- return
+ -- end
+ -- for method, clients in pairs(M._supports_method) do
+ -- clients[client] = clients[client] or {}
+ -- if not clients[client][buffer] then
+ -- if client.supports_method and client.supports_method(method, { bufnr = buffer }) then
+ -- clients[client][buffer] = true
+ -- vim.api.nvim_exec_autocmds("User", {
+ -- pattern = "LspSupportsMethod",
+ -- data = { client_id = client.id, buffer = buffer, method = method },
+ -- })
+ -- end
+ -- end
+ -- end
+ -- end
+
+ on_supports_method("textDocument/inlayHint", function(client, buffer)
+ if
+ vim.api.nvim_buf_is_valid(buffer)
+ and vim.bo[buffer].buftype == ""
+ and not vim.tbl_contains(opts.inlay_hints.exclude, vim.bo[buffer].filetype)
+ then
+ vim.lsp.inlay_hint.enable(true, { bufnr = buffer })
+ end
+ end)
+
+ vim.diagnostic.config({
+ underline = true,
+ update_in_insert = false,
+ virtual_text = {
+ spacing = 4,
+ source = "if_many",
+ prefix = function(diagnostic)
+ for name, icon in pairs(icons.diagnostics) do
+ if diagnostic.severity == vim.diagnostic.severity[name:upper()] then
+ return icon
+ end
+ end
+ end
+
+ },
+ severity_sort = true,
+ })
+
+ local servers = opts.servers
+ local cmp_nvim_lsp = require "cmp_nvim_lsp"
+ local capabilities = vim.tbl_deep_extend(
+ "force",
+ {},
+ vim.lsp.protocol.make_client_capabilities(),
+ cmp_nvim_lsp.default_capabilities()
+ )
+
+ local function setup(server)
+ local server_opts = vim.tbl_deep_extend("force", {
+ capabilities = vim.deepcopy(capabilities),
+ }, servers[server] or {})
+
+ if opts.setup[server] then
+ if opts.setup[server](server, server_opts) then
+ return
+ end
+ elseif opts.setup["*"] then
+ if opts.setup["*"](server, server_opts) then
+ return
+ end
+ end
+ require("lspconfig")[server].setup(server_opts)
+ end
+
+ local mlsp = require "mason-lspconfig"
+ local all_mslp_servers = require("mason-lspconfig").get_mappings().lspconfig_to_package
+
+ local ensure_installed = {}
+ for server, server_opts in pairs(servers) do
+ if server_opts then
+ server_opts = server_opts == true and {} or server_opts
+ if server_opts.mason == false or not vim.tbl_contains(all_mslp_servers, server) then
+ setup(server)
+ else
+ ensure_installed[#ensure_installed + 1] = server
+ end
+ end
+ end
+
+ -- mlsp.setup { ensure_installed = ensure_installed, handlers = { setup } }
+ mlsp.setup { ensure_installed = ensure_installed }
+ end,
+ -- config = function(_, opts)
+ -- local cache = {}
+ --
+ --
+ --
+ --
+ -- on_attach(function(client, buffer)
+ -- -- TODO: add keybinds
+ -- end)
+ --
+ -- local register_capability = vim.lsp.handlers["client/registerCapability"]
+ --
+ -- vim.lsp.handlers["client/registerCapability"] = function(err, res, ctx)
+ -- local ret = register_capability(err, res, ctx)
+ -- local client_id = ctx.client_id
+ -- local client = vim.lsp.get_client_by_id(client_id)
+ -- local buffer = vim.api.nvim_get_current_buf()
+ -- -- TODO: add keybinds
+ -- return ret
+ -- end
+ --
+ -- for name, icon in pairs(require("icons").diagnostics) do
+ -- name = "DiagnosticSign" .. name
+ -- vim.fn.sign_define(name, { text = icon, texthl = name, numhl = "" })
+ -- end
+ --
+ -- local inlay_hint = vim.lsp.buf.inlay_hint or vim.lsp.inlay_hint
+ --
+ --
+ -- if type(opts.diagnostics.virtual_text) == "table" and opts.diagnostics.virtual_text.prefix == "icons" then
+ -- opts.diagnostics.virtual_text.prefix = vim.fn.has "nvim-0.10.0" == 0 and "●"
+ -- end
+ --
+-- )
+ --
+ -- local servers = opts.servers
+ -- local has_cmp, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp")
+ -- local capabilities = vim.tbl_deep_extend(
+ -- "force",
+ -- {},
+ -- vim.lsp.protocol.make_client_capabilities(),
+ -- has_cmp and cmp_nvim_lsp.default_capabilities() or {},
+ -- opts.capabilities or {}
+ -- )
+ --
+ -- local function setup(server)
+ -- local server_opts = vim.tbl_deep_extend("force", {
+ -- capabilities = vim.deepcopy(capabilities),
+ -- }, servers[server] or {})
+ --
+ -- if opts.setup[server] then
+ -- if opts.setup[server](server, server_opts) then
+ -- return
+ -- end
+ -- elseif opts.setup["*"] then
+ -- if opts.setup["*"](server, server_opts) then
+ -- return
+ -- end
+ -- end
+ -- require("lspconfig")[server].setup(server_opts)
+ -- end
+ --
+ -- local have_mason, mlsp = pcall(require, "mason-lspconfig")
+ -- local all_mslp_servers = {}
+ -- if have_mason then
+ -- all_mslp_servers = vim.tbl_keys(require("mason-lspconfig.mappings.server").lspconfig_to_package)
+ -- end
+ --
+ -- local ensure_installed = {}
+ -- for server, server_opts in pairs(servers) do
+ -- if server_opts then
+ -- server_opts = server_opts == true and {} or server_opts
+ -- if server_opts.mason == false or not vim.tbl_contains(all_mslp_servers, server) then
+ -- setup(server)
+ -- else
+ -- ensure_installed[#ensure_installed + 1] = server
+ -- end
+ -- end
+ -- end
+ --
+ -- if have_mason then
+ -- mlsp.setup { ensure_installed = ensure_installed, handlers = { setup } }
+ -- end
+ -- end,
+ },
+}
diff --git a/.config/nvim/lua/plugins/ui.lua b/.config/nvim/lua/plugins/ui.lua
new file mode 100644
index 0000000..e79ee3a
--- /dev/null
+++ b/.config/nvim/lua/plugins/ui.lua
@@ -0,0 +1,477 @@
+local leap_active = false
+vim.api.nvim_create_autocmd("User", {
+ pattern = "LeapEnter",
+ callback = function()
+ leap_active = true
+ end,
+})
+
+vim.api.nvim_create_autocmd("User", {
+ pattern = "LeapLeave",
+ callback = function()
+ leap_active = false
+ end,
+})
+
+local nvim_tree_root = nil
+
+return {
+ {
+ "willothy/nvim-cokeline",
+
+ config = function()
+ local hl = require("cokeline.hlgroups")
+ local is_picking_focus = require('cokeline.mappings').is_picking_focus
+ local is_picking_close = require('cokeline.mappings').is_picking_close
+
+ local function has_diagnostics(buffer)
+ return buffer.diagnostics.errors > 0 or buffer.diagnostics.warnings > 0 or buffer.diagnostics.infos > 0 or buffer.diagnostics.hints > 0
+ end
+
+ require("cokeline").setup {
+ show_if_buffers_are_at_least = 0,
+ components = {
+ {
+ text = function(buffer)
+ return buffer.is_first and (require "cokeline.sidebar".get_width("left") == 0 and "" or "│") or "▎"
+ end,
+ fg = function()
+ return hl.get_hl_attr("WinSeparator", "fg")
+ end,
+ },
+ {
+ text = function (buffer)
+ return (is_picking_focus() or is_picking_close()) and " " .. buffer.pick_letter or ""
+ end,
+ fg = function ()
+ return hl.get_hl_attr("PrismiteYellow", "fg")
+ end
+ },
+ {
+ text = function(buffer)
+ return " " .. buffer.devicon.icon
+ end,
+ fg = function(buffer)
+ return buffer.devicon.color
+ end,
+ },
+ {
+ text = function(buffer)
+ return buffer.unique_prefix
+ end,
+ fg = function()
+ return hl.get_hl_attr("Comment", "fg")
+ end,
+ italic = true,
+ },
+ {
+ text = function(buffer)
+ return buffer.filename
+ end,
+ },
+ {
+ ---@param buffer Buffer
+ text = function(buffer)
+ return buffer.is_modified and " " or ""
+ end,
+ },
+ {
+ text = function(buffer)
+ return has_diagnostics(buffer) and " [" or ""
+ end,
+ fg = function()
+ return hl.get_hl_attr("WinSeparator", "fg")
+ end,
+ },
+ {
+ text = function(buffer)
+ local errors = buffer.diagnostics.errors
+ return errors > 0 and errors or ""
+ end,
+ fg = function ()
+ return hl.get_hl_attr("DiagnosticError", "fg")
+
+ end
+ },
+ {
+ text = function (buffer)
+ local bd = buffer.diagnostics
+ return (bd.errors > 0 and (bd.warnings > 0 or bd.infos > 0 or bd.hints > 0)) and "|" or ""
+ end,
+ fg = function()
+ return hl.get_hl_attr("WinSeparator", "fg")
+ end,
+ },
+ {
+ text = function(buffer)
+ local warnings = buffer.diagnostics.warnings
+ return warnings > 0 and warnings or ""
+ end,
+ fg = function ()
+ return hl.get_hl_attr("DiagnosticWarn", "fg")
+ end
+ },
+ {
+ text = function (buffer)
+ local bd = buffer.diagnostics
+ return (bd.warnings > 0 and (bd.infos > 0 or bd.hints > 0)) and "|" or ""
+ end,
+ fg = function()
+ return hl.get_hl_attr("WinSeparator", "fg")
+ end,
+ },
+ {
+ text = function(buffer)
+ local infos = buffer.diagnostics.infos
+ return infos > 0 and infos or ""
+ end,
+ fg = function ()
+ return hl.get_hl_attr("DiagnosticInfo", "fg")
+ end
+ },
+ {
+ text = function (buffer)
+ local bd = buffer.diagnostics
+ return (bd.infos > 0 and bd.hints > 0) and "|" or ""
+ end,
+ fg = function()
+ return hl.get_hl_attr("WinSeparator", "fg")
+ end,
+ },
+ {
+ text = function(buffer)
+ local hints = buffer.diagnostics.hints
+ return hints > 0 and hints or ""
+ end,
+ fg = function ()
+ return hl.get_hl_attr("DiagnosticHint", "fg")
+ end
+ },
+ {
+ text = function(buffer)
+ return has_diagnostics(buffer) and "]" or ""
+ end,
+ fg = function()
+ return hl.get_hl_attr("WinSeparator", "fg")
+ end,
+ },
+ {
+ text = " "
+ }
+ },
+ buffers = {
+ delete_on_right_click = false
+ },
+ mappings = {
+ disable_mouse = true
+ },
+ sidebar = {
+ components = {
+ {
+ text = function()
+ local width = require("cokeline.sidebar").get_width("left")
+ if #nvim_tree_root > width then
+ return nvim_tree_root:sub(1, width - 2) .. "…"
+ else
+ return nvim_tree_root
+ end
+ end,
+ bg = function()
+ return hl.get_hl_attr("TabLineSel", "bg")
+ end,
+ },
+ {
+ text = function()
+ return string.rep(
+ " ",
+ math.max(0, require("cokeline.sidebar").get_width("left") - #nvim_tree_root)
+ )
+ end,
+ bg = function()
+ return hl.get_hl_attr("TabLineSel", "bg")
+ end
+ },
+ }
+ }
+ }
+ end,
+ dependencies = { "nvim-tree/nvim-web-devicons", "nvim-lua/plenary.nvim" },
+ },
+ {
+ "folke/trouble.nvim",
+ opts = {},
+ dependencies = { "nvim-tree/nvim-web-devicons" },
+ },
+ {
+ "nvimdev/indentmini.nvim",
+ config = true,
+ opts = {
+ char = "▏"
+ }
+ },
+ {
+ "folke/noice.nvim",
+ event = "VeryLazy",
+ opts = {
+ cmdline = {
+ format = {
+ cmdline = { icon = ":" },
+ },
+ },
+ lsp = {
+ override = {
+ ["vim.lsp.util.convert_input_to_markdown_lines"] = true,
+ ["vim.lsp.util.stylize_markdown"] = true,
+ ["cmp.entry.get_documentation"] = true,
+ },
+ },
+ presets = {
+ command_palette = true,
+ long_message_to_split = true,
+ inc_rename = false,
+ lsp_doc_border = true,
+ },
+ messages = {
+ view_search = false,
+ },
+ routes = {
+ {
+ filter = {
+ event = "msg_show",
+ kind = "",
+ find = "Already at newest change",
+ },
+ view = "mini",
+ },
+ {
+ filter = {
+ event = "msg_show",
+ kind = "",
+ find = "Already at oldest change",
+ },
+ view = "mini",
+ },
+ {
+ filter = {
+ event = "msg_show",
+ kind = "",
+ find = "E486: Pattern not found:"
+ },
+ view = "mini"
+ },
+ -- normal search
+ {
+ filter = {
+ event = "msg_show",
+ kind = "",
+ find = "E486: Pattern not found:"
+ },
+ view = "mini"
+ },
+ -- failed search
+ {
+ filter = {
+ event = "msg_show",
+ kind = "",
+ find = "E486: Pattern not found:"
+ },
+ view = "mini"
+ },
+ -- undo
+ {
+ filter = {
+ event = "msg_show",
+ kind = "",
+ find = "%d+ .+ line.?; before #%d+",
+ },
+ view = "mini",
+ },
+ {
+ filter = {
+ event = "msg_show",
+ kind = "",
+ find = "%d+ .+ line.?; before #%d+",
+ },
+ view = "mini",
+ },
+ -- redo
+ {
+ filter = {
+ event = "msg_show",
+ kind = "",
+ find = "%d+ change; after #%d+",
+ },
+ view = "mini"
+ },
+ },
+ },
+ dependencies = {
+ "MunifTanjim/nui.nvim",
+ "rcarriga/nvim-notify",
+ },
+ },
+ {
+ "nvim-lualine/lualine.nvim",
+ event = "VeryLazy",
+ opts = {
+ options = {
+ section_separators = { left = "", right = "" },
+ component_separators = { left = "", right = "" },
+ fmt = function(str)
+ return vim.trim(str)
+ end,
+ globalstatus = true,
+ disabled_filetypes = { "NvimTree" },
+ },
+ sections = {
+ lualine_a = {
+ "mode",
+ {
+ function()
+ return "[L]"
+ end,
+ cond = function ()
+ return leap_active
+ end
+ },
+ {
+ function()
+ local reg = vim.fn.reg_recording()
+ if reg == "" then return "" end -- not recording
+ return "[@" .. reg .. "]"
+ end
+ }
+ },
+ lualine_b = { "filename" },
+ lualine_c = { "diagnostics", "diff" },
+ lualine_x = {
+ {
+ function ()
+ local noice_loaded, noice = pcall(require, "noice")
+ if noice_loaded then
+ return noice.api.status.command.get()
+ else
+ return ""
+ end
+ end,
+ cond = function()
+ local noice_loaded, noice = pcall(require, "noice")
+ if noice_loaded then
+ return noice.api.status.command.has()
+ else
+ return false
+ end
+ end
+ }
+ },
+ lualine_y = { "filetype" },
+ lualine_z = { "searchcount", "progress", "location" },
+ },
+ },
+ dependencies = {
+ "nvim-tree/nvim-web-devicons",
+ },
+ },
+ { "stevearc/dressing.nvim", config = true },
+ {
+ "nvim-tree/nvim-tree.lua",
+ opts = {
+ sort_by = "case_sensitive",
+ view = {
+ width = 30,
+ },
+ renderer = {
+ root_folder_label = function() return ".." end,
+ indent_markers = {
+ enable = true,
+ },
+ icons = {
+ show = {
+ folder_arrow = false,
+ },
+ glyphs = {
+ git = {
+ unstaged = "-",
+ staged = "+",
+ unmerged = "",
+ renamed = "󰏪",
+ untracked = "",
+ deleted = "󰆴",
+ ignored = "",
+ },
+ },
+ },
+ },
+ filters = {
+ git_ignored = false,
+ custom = {
+ "^.git$",
+ },
+ },
+ },
+ config = function(_, opts)
+ local api = require("nvim-tree.api")
+ local Event = api.events.Event
+
+ api.events.subscribe(Event.Ready, function()
+ nvim_tree_root = api.tree.get_nodes().absolute_path
+ end)
+
+ api.events.subscribe(Event.TreeRendered, function()
+ nvim_tree_root = api.tree.get_nodes().absolute_path
+ end)
+
+ require("nvim-tree").setup(vim.tbl_extend("force", opts, {
+ -- on_attach = function ()
+ -- local function opts(desc)
+ -- return { desc = "nvim-tree: " .. desc, buffer = bufnr, noremap = true, silent = true, nowait = true }
+ -- end
+ --
+ -- local function edit_or_open()
+ -- local node = api.tree.get_node_under_cursor()
+ --
+ -- if node.nodes ~= nil then
+ -- -- expand or collapse folder
+ -- api.node.open.edit()
+ -- else
+ -- -- open file
+ -- api.node.open.edit()
+ -- -- Close the tree if file was opened
+ -- api.tree.close()
+ -- end
+ -- end
+ --
+ -- -- open as vsplit on current node
+ -- local function vsplit_preview()
+ -- local node = api.tree.get_node_under_cursor()
+ --
+ -- if node.nodes ~= nil then
+ -- -- expand or collapse folder
+ -- api.node.open.edit()
+ -- else
+ -- -- open file as vsplit
+ -- api.node.open.vertical()
+ -- end
+ --
+ -- -- Finally refocus on tree if it was lost
+ -- api.tree.focus()
+ -- end
+ --
+ -- vim.keymap.set("n", "l", edit_or_open, opts("Edit Or Open"))
+ -- vim.keymap.set("n", "h", api.tree.close, opts("Close"))
+ -- end
+ --
+ }))
+ end,
+ dependencies = {
+ "nvim-tree/nvim-web-devicons",
+ },
+ },
+ {
+ "goolord/alpha-nvim",
+ dependencies = { "nvim-tree/nvim-web-devicons" },
+ config = function()
+ require("alpha").setup(require "dash")
+ end,
+ },
+}
diff --git a/.config/nvim/lua/plugins/utils.lua b/.config/nvim/lua/plugins/utils.lua
new file mode 100644
index 0000000..3c16433
--- /dev/null
+++ b/.config/nvim/lua/plugins/utils.lua
@@ -0,0 +1,4 @@
+return {
+ { 'echasnovski/mini.colors', version = '*' },
+ "MunifTanjim/nui.nvim",
+}
diff --git a/.config/rmpc/config.ron b/.config/rmpc/config.ron
new file mode 100644
index 0000000..7822c58
--- /dev/null
+++ b/.config/rmpc/config.ron
@@ -0,0 +1,167 @@
+#![enable(implicit_some)]
+#![enable(unwrap_newtypes)]
+#![enable(unwrap_variant_newtypes)]
+(
+ address: "127.0.0.1:6600",
+ password: None,
+ theme: "prismite",
+ cache_dir: None,
+ on_song_change: None,
+ volume_step: 5,
+ max_fps: 30,
+ scrolloff: 0,
+ wrap_navigation: false,
+ enable_mouse: true,
+ enable_config_hot_reload: true,
+ status_update_interval_ms: 1000,
+ rewind_to_start_sec: None,
+ reflect_changes_to_playlist: false,
+ select_current_song_on_change: false,
+ browser_song_sort: [Disc, Track, Artist, Title],
+ directories_sort: SortFormat(group_by_type: true, reverse: false),
+ album_art: (
+ method: Auto,
+ max_size_px: (width: 1200, height: 1200),
+ disabled_protocols: ["http://", "https://"],
+ vertical_align: Center,
+ horizontal_align: Center,
+ ),
+ keybinds: (
+ global: {
+ ":": CommandMode,
+ ",": VolumeDown,
+ "s": Stop,
+ ".": VolumeUp,
+ "<Tab>": NextTab,
+ "<S-Tab>": PreviousTab,
+ "1": SwitchToTab("Queue"),
+ "2": SwitchToTab("Directories"),
+ "3": SwitchToTab("Artists"),
+ "4": SwitchToTab("Album Artists"),
+ "5": SwitchToTab("Albums"),
+ "6": SwitchToTab("Playlists"),
+ "7": SwitchToTab("Search"),
+ "q": Quit,
+ ">": NextTrack,
+ "p": TogglePause,
+ "<": PreviousTrack,
+ "f": SeekForward,
+ "z": ToggleRepeat,
+ "x": ToggleRandom,
+ "c": ToggleConsume,
+ "v": ToggleSingle,
+ "b": SeekBack,
+ "~": ShowHelp,
+ "u": Update,
+ "U": Rescan,
+ "I": ShowCurrentSongInfo,
+ "O": ShowOutputs,
+ "P": ShowDecoders,
+ "R": AddRandom,
+ },
+ navigation: {
+ "k": Up,
+ "j": Down,
+ "h": Left,
+ "l": Right,
+ "<Up>": Up,
+ "<Down>": Down,
+ "<Left>": Left,
+ "<Right>": Right,
+ "<C-k>": PaneUp,
+ "<C-j>": PaneDown,
+ "<C-h>": PaneLeft,
+ "<C-l>": PaneRight,
+ "<C-u>": UpHalf,
+ "N": PreviousResult,
+ "a": Add,
+ "A": AddAll,
+ "r": Rename,
+ "n": NextResult,
+ "g": Top,
+ "<Space>": Select,
+ "<C-Space>": InvertSelection,
+ "G": Bottom,
+ "<CR>": Confirm,
+ "i": FocusInput,
+ "J": MoveDown,
+ "<C-d>": DownHalf,
+ "/": EnterSearch,
+ "<C-c>": Close,
+ "<Esc>": Close,
+ "K": MoveUp,
+ "D": Delete,
+ "B": ShowInfo,
+ },
+ queue: {
+ "D": DeleteAll,
+ "<CR>": Play,
+ "<C-s>": Save,
+ "a": AddToPlaylist,
+ "d": Delete,
+ "C": JumpToCurrent,
+ "X": Shuffle,
+ },
+ ),
+ search: (
+ case_sensitive: false,
+ mode: Contains,
+ tags: [
+ (value: "any", label: "Any Tag"),
+ (value: "artist", label: "Artist"),
+ (value: "album", label: "Album"),
+ (value: "albumartist", label: "Album Artist"),
+ (value: "title", label: "Title"),
+ (value: "filename", label: "Filename"),
+ (value: "genre", label: "Genre"),
+ ],
+ ),
+ artists: (
+ album_display_mode: SplitByDate,
+ album_sort_by: Date,
+ ),
+ tabs: [
+ (
+ name: "Queue",
+ pane: Split(
+ direction: Horizontal,
+ panes: [
+ (
+ size: "40%",
+ pane: Pane(AlbumArt)
+ ),
+ (
+ size: "60%",
+ pane: Pane(Queue),
+ borders: "LEFT"
+ )
+ ],
+ ),
+ ),
+ (
+ name: "Directories",
+ pane: Pane(Directories),
+ ),
+ (
+ name: "Artists",
+ pane: Pane(Artists),
+ ),
+ (
+ name: "Album Artists",
+ pane: Pane(AlbumArtists),
+ ),
+ (
+ name: "Albums",
+ pane: Pane(Albums),
+ ),
+ (
+ name: "Playlists",
+ pane: Pane(Playlists),
+ ),
+ (
+ name: "Search",
+ pane: Pane(Search),
+ ),
+ ],
+)
+
diff --git a/.config/rmpc/themes/prismite.ron b/.config/rmpc/themes/prismite.ron
new file mode 100644
index 0000000..88fbc4b
--- /dev/null
+++ b/.config/rmpc/themes/prismite.ron
@@ -0,0 +1,195 @@
+#![enable(implicit_some)]
+#![enable(unwrap_newtypes)]
+#![enable(unwrap_variant_newtypes)]
+(
+ default_album_art_path: None,
+ show_song_table_header: true,
+ draw_borders: true,
+ format_tag_separator: " | ",
+ browser_column_widths: [20, 38, 42],
+ background_color: None,
+ text_color: None,
+ header_background_color: None,
+ modal_background_color: None,
+ modal_backdrop: false,
+ preview_label_style: (fg: "yellow"),
+ preview_metadata_group_style: (fg: "yellow", modifiers: "Bold"),
+ tab_bar: (
+ enabled: true,
+ active_style: (fg: "#12161c", bg: "yellow", modifiers: "Bold"),
+ inactive_style: (),
+ ),
+ highlighted_item_style: (fg: "yellow", modifiers: "Bold"),
+ current_item_style: (fg: "gray", bg: "#171c22"),
+ borders_style: (fg: "#323b46"),
+ highlight_border_style: (fg: "#323b46"),
+ symbols: (
+ song: "",
+ dir: "󰉋",
+ playlist: "󱍙",
+ marker: "+",
+ ellipsis: "...",
+ song_style: None,
+ dir_style: None,
+ playlist_style: None,
+ ),
+ level_styles: (
+ info: (fg: "yellow"),
+ warn: (fg: "#ff9f6f"),
+ error: (fg: "red"),
+ debug: (fg: "light_green"),
+ trace: (fg: "magenta"),
+ ),
+ progress_bar: (
+ symbols: ["<", "=", ">", "=", ">"],
+ track_style: (fg: "#070c11"),
+ elapsed_style: (fg: "yellow"),
+ thumb_style: (fg: "yellow"),
+ ),
+ scrollbar: (
+ symbols: ["│", "┃", "∧", "∨"],
+ track_style: (fg: "#070c11"),
+ ends_style: (fg: "#a8afb7"),
+ thumb_style: (fg: "yellow"),
+ ),
+ song_table_format: [
+ (
+ prop: (kind: Property(Artist),
+ default: (kind: Text("Unknown"))
+ ),
+ width: "20%",
+ ),
+ (
+ prop: (kind: Property(Title),
+ default: (kind: Text("Unknown"))
+ ),
+ width: "35%",
+ ),
+ (
+ prop: (kind: Property(Album), style: (fg: "gray"),
+ default: (kind: Text("Unknown Album"), style: (fg: "gray"))
+ ),
+ width: "30%",
+ ),
+ (
+ prop: (kind: Property(Duration),
+ default: (kind: Text("-"))
+ ),
+ width: "15%",
+ alignment: Right,
+ ),
+ ],
+ components: {},
+ layout: Split(
+ direction: Vertical,
+ panes: [
+ (
+ pane: Pane(Header),
+ size: "1",
+ ),
+ (
+ pane: Pane(Tabs),
+ size: "3",
+ ),
+ (
+ pane: Pane(TabContent),
+ size: "100%",
+ ),
+ (
+ pane: Split(
+ direction: Horizontal,
+ panes: [
+ (
+ pane: Pane(
+ Property(content: [
+ (kind: Property(
+ Status(
+ StateV2(
+ playing_label: "",
+ paused_label: "",
+ stopped_label: ""
+ )
+ )
+ ))
+ ])
+ ),
+ size: "3"
+ ),
+ (
+ pane: Pane(
+ Property(content: [
+ (kind: Property(Status(Elapsed))),
+ (kind: Text(" / ")),
+ (kind: Property(Status(Duration))),
+ (kind: Text(" (")),
+ (kind: Property(Status(Bitrate))),
+ (kind: Text(" kbps)"))
+ ])
+ ),
+ size: "20%",
+ ),
+ (
+ pane: Pane(ProgressBar),
+ size: "100%"
+ ),
+ ]
+ ),
+ size: "2",
+ borders: "TOP"
+ ),
+ ],
+ ),
+ header: (
+ rows: [
+ (
+ left: [
+ (kind: Property(Widget(ScanStatus))),
+ (kind: Property(Widget(Volume))),
+ ],
+ center: [
+ (kind: Property(Song(Artist)), style: (fg: "yellow", modifiers: "Bold"),
+ default: (kind: Text("Unknown"), style: (fg: "yellow", modifiers: "Bold"))
+ ),
+ (kind: Text(" - ")),
+ (kind: Property(Song(Album)),
+ default: (kind: Text("Unknown Album"))
+ ),
+ (kind: Text(" | ")),
+ (kind: Property(Song(Title)), style: (modifiers: "Bold"),
+ default: (kind: Text("No Song"), style: (modifiers: "Bold"))
+ )
+ ],
+ right: [
+ (kind: Text("[ ")),
+ (
+ kind: Property(Widget(States(
+ active_style: (fg: "yellow", modifiers: "Bold"),
+ separator_style: (fg: "gray")))
+ ),
+ style: (fg: "gray")
+ ),
+ (kind: Text(" ]")),
+ ]
+ ),
+ ],
+ ),
+ browser_song_format: [
+ (
+ kind: Group([
+ (kind: Property(Track)),
+ (kind: Text(" ")),
+ ])
+ ),
+ (
+ kind: Group([
+ (kind: Property(Artist)),
+ (kind: Text(" - ")),
+ (kind: Property(Title)),
+ ]),
+ default: (kind: Property(Filename))
+ ),
+ ],
+ lyrics: (
+ timestamp: false
+ )
+)
diff --git a/.config/wezterm/.nvim.lua b/.config/wezterm/.nvim.lua
new file mode 100644
index 0000000..9c7049d
--- /dev/null
+++ b/.config/wezterm/.nvim.lua
@@ -0,0 +1,2 @@
+vim.env.GIT_DIR = vim.fn.expand("~/.dots")
+vim.env.GIT_WORK_TREE = vim.fn.expand("~")
diff --git a/.config/wezterm/appearance.lua b/.config/wezterm/appearance.lua
index 2376174..f786e85 100644
--- a/.config/wezterm/appearance.lua
+++ b/.config/wezterm/appearance.lua
@@ -1,62 +1,51 @@
-local wezterm = require "wezterm"
+local wezterm = require("wezterm")
local function basename(s)
- return string.gsub(s, '(.*[/\\])(.*)', '%2')
+ return string.gsub(s, "(.*[/\\])(.*)", "%2")
end
local function home_or_path(path)
- local realpath = path:sub(14, -1) -- file://lambda
- local home = os.getenv("HOME")
- return realpath == home and "~" or realpath
+ local realpath = tostring(path):sub(14, -1) -- file://lambda
+ local home = os.getenv("HOME")
+ return realpath == home and "~" or realpath
end
wezterm.on("format-tab-title", function(tab)
- local tab_format = {
- { Text = " " },
- { Text = tostring(tab.tab_index + 1) },
- { Text = " " },
- { Text = basename(home_or_path(tab.active_pane.current_working_dir)) },
- { Text = " " },
- "ResetAttributes",
- }
-
- if tab.is_active then
- table.insert(tab_format, 1, { Background = { Color = C.black } })
- end
-
- return wezterm.format(tab_format)
+ local tab_format = {
+ { Text = " " },
+ { Text = tostring(tab.tab_index + 1) },
+ { Text = " " },
+ { Text = basename(home_or_path(tab.active_pane.current_working_dir)) },
+ { Text = " " },
+ "ResetAttributes",
+ }
+
+ return wezterm.format(tab_format)
end)
return {
- -- tabs
- show_new_tab_button_in_tab_bar = false,
- use_fancy_tab_bar = false,
- tab_bar_at_bottom = true,
- hide_tab_bar_if_only_one_tab = true,
-
- -- font
- font = wezterm.font {
- family = "FiraCode Nerd Font Mono",
- harfbuzz_features = { "ss02", "ss03", "ss04", "ss08" }
- },
- font_size = 10,
- adjust_window_size_when_changing_font_size = false,
-
- -- cursor
- default_cursor_style = "BlinkingUnderline",
- cursor_blink_rate = 500,
- animation_fps = 1,
-
- -- window
- window_padding = {
- left = 10,
- right = 10,
- top = 10,
- bottom = 10
- },
-
- -- colors
- colors = R "colors",
- bold_brightens_ansi_colors = false,
-
+ -- tabs
+ show_new_tab_button_in_tab_bar = false,
+ use_fancy_tab_bar = false,
+ tab_bar_at_bottom = true,
+ hide_tab_bar_if_only_one_tab = true,
+
+ -- font
+ font = wezterm.font({
+ -- family = "FiraCode Nerd Font Mono",
+ family = "Iosevka Comfy",
+ -- harfbuzz_features = { "ss02", "ss03", "ss04", "ss08" }
+ }),
+ font_size = 10.5,
+ adjust_window_size_when_changing_font_size = false,
+ allow_square_glyphs_to_overflow_width = "Never",
+
+ -- cursor
+ default_cursor_style = "BlinkingUnderline",
+ cursor_blink_rate = 500,
+ animation_fps = 1,
+
+ -- colors
+ colors = R("colors"),
+ bold_brightens_ansi_colors = false,
}
diff --git a/.config/wezterm/colors.lua b/.config/wezterm/colors.lua
index 58ae639..e290a12 100644
--- a/.config/wezterm/colors.lua
+++ b/.config/wezterm/colors.lua
@@ -1,48 +1,48 @@
return {
- foreground = C.fg,
- background = C.bg,
+ foreground = C.fg(),
+ background = C.bg(),
- cursor_bg = C.fg,
- cursor_fg = C.bg,
- cursor_border = C.bg,
+ cursor_bg = C.fg(),
+ cursor_fg = C.bg(),
+ cursor_border = C.bg(),
- selection_bg = C.bright.black,
- selection_fg = C.fg,
+ selection_bg = C.bg.high,
+ selection_fg = C.fg(),
- visual_bell = C.white,
+ visual_bell = C.fg(),
- ansi = {
- C.black,
- C.red,
- C.green,
- C.yellow,
- C.blue,
- C.pink,
- C.cyan,
- C.white,
- },
- brights = {
- C.bright.black,
- C.bright.red,
- C.bright.green,
- C.bright.yellow,
- C.bright.blue,
- C.bright.pink,
- C.bright.cyan,
- C.bright.white,
- },
+ ansi = {
+ C.bg.lowest,
+ C.red(),
+ C.green(),
+ C.yellow(),
+ C.blue(),
+ C.pink(),
+ C.cyan(),
+ C.fg(),
+ },
+ brights = {
+ C.bg.low,
+ C.red.bright,
+ C.green.bright,
+ C.yellow.bright,
+ C.blue.bright,
+ C.pink.bright,
+ C.cyan.bright,
+ C.fg.high,
+ },
- tab_bar = {
- background = C.bg,
+ tab_bar = {
+ background = C.bg(),
- active_tab = {
- bg_color = C.bg,
- fg_color = C.fg
- },
+ active_tab = {
+ bg_color = C.bg.high,
+ fg_color = C.fg(),
+ },
- inactive_tab = {
- bg_color = C.bg,
- fg_color = C.fg_dark
- },
- }
+ inactive_tab = {
+ bg_color = C.bg(),
+ fg_color = C.fg.low,
+ },
+ },
}
diff --git a/.config/wezterm/inspect.lua b/.config/wezterm/inspect.lua
index ce90145..f744939 100644
--- a/.config/wezterm/inspect.lua
+++ b/.config/wezterm/inspect.lua
@@ -1,25 +1,18 @@
-local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local math = _tl_compat and _tl_compat.math or math; local string = _tl_compat and _tl_compat.string or string; local table = _tl_compat and _tl_compat.table or table
-local inspect = {Options = {}, }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-inspect._VERSION = 'inspect.lua 3.1.0'
-inspect._URL = 'http://github.com/kikito/inspect.lua'
-inspect._DESCRIPTION = 'human-readable representations of tables'
+local _tl_compat
+if (tonumber((_VERSION or ""):match("[%d.]*$")) or 0) < 5.3 then
+ local p, m = pcall(require, "compat53.module")
+ if p then
+ _tl_compat = m
+ end
+end
+local math = _tl_compat and _tl_compat.math or math
+local string = _tl_compat and _tl_compat.string or string
+local table = _tl_compat and _tl_compat.table or table
+local inspect = { Options = {} }
+
+inspect._VERSION = "inspect.lua 3.1.0"
+inspect._URL = "http://github.com/kikito/inspect.lua"
+inspect._DESCRIPTION = "human-readable representations of tables"
inspect._LICENSE = [[
MIT LICENSE
@@ -44,8 +37,16 @@ inspect._LICENSE = [[
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]]
-inspect.KEY = setmetatable({}, { __tostring = function() return 'inspect.KEY' end })
-inspect.METATABLE = setmetatable({}, { __tostring = function() return 'inspect.METATABLE' end })
+inspect.KEY = setmetatable({}, {
+ __tostring = function()
+ return "inspect.KEY"
+ end,
+})
+inspect.METATABLE = setmetatable({}, {
+ __tostring = function()
+ return "inspect.METATABLE"
+ end,
+})
local tostring = tostring
local rep = string.rep
@@ -56,317 +57,310 @@ local fmt = string.format
local _rawget
if rawget then
- _rawget = rawget
+ _rawget = rawget
else
- _rawget = function(t, k) return t[k] end
+ _rawget = function(t, k)
+ return t[k]
+ end
end
local function rawpairs(t)
- return next, t, nil
+ return next, t, nil
end
-
-
local function smartQuote(str)
- if match(str, '"') and not match(str, "'") then
- return "'" .. str .. "'"
- end
- return '"' .. gsub(str, '"', '\\"') .. '"'
+ if match(str, '"') and not match(str, "'") then
+ return "'" .. str .. "'"
+ end
+ return '"' .. gsub(str, '"', '\\"') .. '"'
end
-
local shortControlCharEscapes = {
- ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
- ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v", ["\127"] = "\\127",
+ ["\a"] = "\\a",
+ ["\b"] = "\\b",
+ ["\f"] = "\\f",
+ ["\n"] = "\\n",
+ ["\r"] = "\\r",
+ ["\t"] = "\\t",
+ ["\v"] = "\\v",
+ ["\127"] = "\\127",
}
local longControlCharEscapes = { ["\127"] = "\127" }
for i = 0, 31 do
- local ch = char(i)
- if not shortControlCharEscapes[ch] then
- shortControlCharEscapes[ch] = "\\" .. i
- longControlCharEscapes[ch] = fmt("\\%03d", i)
- end
+ local ch = char(i)
+ if not shortControlCharEscapes[ch] then
+ shortControlCharEscapes[ch] = "\\" .. i
+ longControlCharEscapes[ch] = fmt("\\%03d", i)
+ end
end
local function escape(str)
- return (gsub(gsub(gsub(str, "\\", "\\\\"),
- "(%c)%f[0-9]", longControlCharEscapes),
- "%c", shortControlCharEscapes))
+ return (gsub(gsub(gsub(str, "\\", "\\\\"), "(%c)%f[0-9]", longControlCharEscapes), "%c", shortControlCharEscapes))
end
local luaKeywords = {
- ['and'] = true,
- ['break'] = true,
- ['do'] = true,
- ['else'] = true,
- ['elseif'] = true,
- ['end'] = true,
- ['false'] = true,
- ['for'] = true,
- ['function'] = true,
- ['goto'] = true,
- ['if'] = true,
- ['in'] = true,
- ['local'] = true,
- ['nil'] = true,
- ['not'] = true,
- ['or'] = true,
- ['repeat'] = true,
- ['return'] = true,
- ['then'] = true,
- ['true'] = true,
- ['until'] = true,
- ['while'] = true,
+ ["and"] = true,
+ ["break"] = true,
+ ["do"] = true,
+ ["else"] = true,
+ ["elseif"] = true,
+ ["end"] = true,
+ ["false"] = true,
+ ["for"] = true,
+ ["function"] = true,
+ ["goto"] = true,
+ ["if"] = true,
+ ["in"] = true,
+ ["local"] = true,
+ ["nil"] = true,
+ ["not"] = true,
+ ["or"] = true,
+ ["repeat"] = true,
+ ["return"] = true,
+ ["then"] = true,
+ ["true"] = true,
+ ["until"] = true,
+ ["while"] = true,
}
local function isIdentifier(str)
- return type(str) == "string" and
- not not str:match("^[_%a][_%a%d]*$") and
- not luaKeywords[str]
+ return type(str) == "string" and not not str:match("^[_%a][_%a%d]*$") and not luaKeywords[str]
end
local flr = math.floor
local function isSequenceKey(k, sequenceLength)
- return type(k) == "number" and
- flr(k) == k and
- 1 <= (k) and
- k <= sequenceLength
+ return type(k) == "number" and flr(k) == k and 1 <= k and k <= sequenceLength
end
local defaultTypeOrders = {
- ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
- ['function'] = 5, ['userdata'] = 6, ['thread'] = 7,
+ ["number"] = 1,
+ ["boolean"] = 2,
+ ["string"] = 3,
+ ["table"] = 4,
+ ["function"] = 5,
+ ["userdata"] = 6,
+ ["thread"] = 7,
}
local function sortKeys(a, b)
- local ta, tb = type(a), type(b)
-
+ local ta, tb = type(a), type(b)
- if ta == tb and (ta == 'string' or ta == 'number') then
- return (a) < (b)
- end
+ if ta == tb and (ta == "string" or ta == "number") then
+ return a < b
+ end
- local dta = defaultTypeOrders[ta] or 100
- local dtb = defaultTypeOrders[tb] or 100
+ local dta = defaultTypeOrders[ta] or 100
+ local dtb = defaultTypeOrders[tb] or 100
-
- return dta == dtb and ta < tb or dta < dtb
+ return dta == dtb and ta < tb or dta < dtb
end
local function getKeys(t)
-
- local seqLen = 1
- while _rawget(t, seqLen) ~= nil do
- seqLen = seqLen + 1
- end
- seqLen = seqLen - 1
-
- local keys, keysLen = {}, 0
- for k in rawpairs(t) do
- if not isSequenceKey(k, seqLen) then
- keysLen = keysLen + 1
- keys[keysLen] = k
- end
- end
- table.sort(keys, sortKeys)
- return keys, keysLen, seqLen
+ local seqLen = 1
+ while _rawget(t, seqLen) ~= nil do
+ seqLen = seqLen + 1
+ end
+ seqLen = seqLen - 1
+
+ local keys, keysLen = {}, 0
+ for k in rawpairs(t) do
+ if not isSequenceKey(k, seqLen) then
+ keysLen = keysLen + 1
+ keys[keysLen] = k
+ end
+ end
+ table.sort(keys, sortKeys)
+ return keys, keysLen, seqLen
end
local function countCycles(x, cycles)
- if type(x) == "table" then
- if cycles[x] then
- cycles[x] = cycles[x] + 1
- else
- cycles[x] = 1
- for k, v in rawpairs(x) do
- countCycles(k, cycles)
- countCycles(v, cycles)
- end
- countCycles(getmetatable(x), cycles)
- end
- end
+ if type(x) == "table" then
+ if cycles[x] then
+ cycles[x] = cycles[x] + 1
+ else
+ cycles[x] = 1
+ for k, v in rawpairs(x) do
+ countCycles(k, cycles)
+ countCycles(v, cycles)
+ end
+ countCycles(getmetatable(x), cycles)
+ end
+ end
end
local function makePath(path, a, b)
- local newPath = {}
- local len = #path
- for i = 1, len do newPath[i] = path[i] end
+ local newPath = {}
+ local len = #path
+ for i = 1, len do
+ newPath[i] = path[i]
+ end
- newPath[len + 1] = a
- newPath[len + 2] = b
+ newPath[len + 1] = a
+ newPath[len + 2] = b
- return newPath
+ return newPath
end
-
-local function processRecursive(process,
- item,
- path,
- visited)
- if item == nil then return nil end
- if visited[item] then return visited[item] end
-
- local processed = process(item, path)
- if type(processed) == "table" then
- local processedCopy = {}
- visited[item] = processedCopy
- local processedKey
-
- for k, v in rawpairs(processed) do
- processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited)
- if processedKey ~= nil then
- processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited)
- end
- end
-
- local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
- if type(mt) ~= 'table' then mt = nil end
- setmetatable(processedCopy, mt)
- processed = processedCopy
- end
- return processed
+local function processRecursive(process, item, path, visited)
+ if item == nil then
+ return nil
+ end
+ if visited[item] then
+ return visited[item]
+ end
+
+ local processed = process(item, path)
+ if type(processed) == "table" then
+ local processedCopy = {}
+ visited[item] = processedCopy
+ local processedKey
+
+ for k, v in rawpairs(processed) do
+ processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited)
+ if processedKey ~= nil then
+ processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited)
+ end
+ end
+
+ local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
+ if type(mt) ~= "table" then
+ mt = nil
+ end
+ setmetatable(processedCopy, mt)
+ processed = processedCopy
+ end
+ return processed
end
local function puts(buf, str)
- buf.n = buf.n + 1
- buf[buf.n] = str
+ buf.n = buf.n + 1
+ buf[buf.n] = str
end
-
-
local Inspector = {}
-
-
-
-
-
-
-
-
-
local Inspector_mt = { __index = Inspector }
local function tabify(inspector)
- puts(inspector.buf, inspector.newline .. rep(inspector.indent, inspector.level))
+ puts(inspector.buf, inspector.newline .. rep(inspector.indent, inspector.level))
end
function Inspector:getId(v)
- local id = self.ids[v]
- local ids = self.ids
- if not id then
- local tv = type(v)
- id = (ids[tv] or 0) + 1
- ids[v], ids[tv] = id, id
- end
- return tostring(id)
+ local id = self.ids[v]
+ local ids = self.ids
+ if not id then
+ local tv = type(v)
+ id = (ids[tv] or 0) + 1
+ ids[v], ids[tv] = id, id
+ end
+ return tostring(id)
end
function Inspector:putValue(v)
- local buf = self.buf
- local tv = type(v)
- if tv == 'string' then
- puts(buf, smartQuote(escape(v)))
- elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or
- tv == 'cdata' or tv == 'ctype' then
- puts(buf, tostring(v))
- elseif tv == 'table' and not self.ids[v] then
- local t = v
-
- if t == inspect.KEY or t == inspect.METATABLE then
- puts(buf, tostring(t))
- elseif self.level >= self.depth then
- puts(buf, '{...}')
- else
- if self.cycles[t] > 1 then puts(buf, fmt('<%d>', self:getId(t))) end
-
- local keys, keysLen, seqLen = getKeys(t)
-
- puts(buf, '{')
- self.level = self.level + 1
-
- for i = 1, seqLen + keysLen do
- if i > 1 then puts(buf, ',') end
- if i <= seqLen then
- puts(buf, ' ')
- self:putValue(t[i])
- else
- local k = keys[i - seqLen]
- tabify(self)
- if isIdentifier(k) then
- puts(buf, k)
- else
- puts(buf, "[")
- self:putValue(k)
- puts(buf, "]")
- end
- puts(buf, ' = ')
- self:putValue(t[k])
- end
- end
-
- local mt = getmetatable(t)
- if type(mt) == 'table' then
- if seqLen + keysLen > 0 then puts(buf, ',') end
- tabify(self)
- puts(buf, '<metatable> = ')
- self:putValue(mt)
- end
-
- self.level = self.level - 1
-
- if keysLen > 0 or type(mt) == 'table' then
- tabify(self)
- elseif seqLen > 0 then
- puts(buf, ' ')
- end
-
- puts(buf, '}')
- end
-
- else
- puts(buf, fmt('<%s %d>', tv, self:getId(v)))
- end
+ local buf = self.buf
+ local tv = type(v)
+ if tv == "string" then
+ puts(buf, smartQuote(escape(v)))
+ elseif tv == "number" or tv == "boolean" or tv == "nil" or tv == "cdata" or tv == "ctype" then
+ puts(buf, tostring(v))
+ elseif tv == "table" and not self.ids[v] then
+ local t = v
+
+ if t == inspect.KEY or t == inspect.METATABLE then
+ puts(buf, tostring(t))
+ elseif self.level >= self.depth then
+ puts(buf, "{...}")
+ else
+ if self.cycles[t] > 1 then
+ puts(buf, fmt("<%d>", self:getId(t)))
+ end
+
+ local keys, keysLen, seqLen = getKeys(t)
+
+ puts(buf, "{")
+ self.level = self.level + 1
+
+ for i = 1, seqLen + keysLen do
+ if i > 1 then
+ puts(buf, ",")
+ end
+ if i <= seqLen then
+ puts(buf, " ")
+ self:putValue(t[i])
+ else
+ local k = keys[i - seqLen]
+ tabify(self)
+ if isIdentifier(k) then
+ puts(buf, k)
+ else
+ puts(buf, "[")
+ self:putValue(k)
+ puts(buf, "]")
+ end
+ puts(buf, " = ")
+ self:putValue(t[k])
+ end
+ end
+
+ local mt = getmetatable(t)
+ if type(mt) == "table" then
+ if seqLen + keysLen > 0 then
+ puts(buf, ",")
+ end
+ tabify(self)
+ puts(buf, "<metatable> = ")
+ self:putValue(mt)
+ end
+
+ self.level = self.level - 1
+
+ if keysLen > 0 or type(mt) == "table" then
+ tabify(self)
+ elseif seqLen > 0 then
+ puts(buf, " ")
+ end
+
+ puts(buf, "}")
+ end
+ else
+ puts(buf, fmt("<%s %d>", tv, self:getId(v)))
+ end
end
-
-
-
function inspect.inspect(root, options)
- options = options or {}
+ options = options or {}
- local depth = options.depth or (math.huge)
- local newline = options.newline or '\n'
- local indent = options.indent or ' '
- local process = options.process
+ local depth = options.depth or math.huge
+ local newline = options.newline or "\n"
+ local indent = options.indent or " "
+ local process = options.process
- if process then
- root = processRecursive(process, root, {}, {})
- end
+ if process then
+ root = processRecursive(process, root, {}, {})
+ end
- local cycles = {}
- countCycles(root, cycles)
+ local cycles = {}
+ countCycles(root, cycles)
- local inspector = setmetatable({
- buf = { n = 0 },
- ids = {},
- cycles = cycles,
- depth = depth,
- level = 0,
- newline = newline,
- indent = indent,
- }, Inspector_mt)
+ local inspector = setmetatable({
+ buf = { n = 0 },
+ ids = {},
+ cycles = cycles,
+ depth = depth,
+ level = 0,
+ newline = newline,
+ indent = indent,
+ }, Inspector_mt)
- inspector:putValue(root)
+ inspector:putValue(root)
- return table.concat(inspector.buf)
+ return table.concat(inspector.buf)
end
setmetatable(inspect, {
- __call = function(_, root, options)
- return inspect.inspect(root, options)
- end,
+ __call = function(_, root, options)
+ return inspect.inspect(root, options)
+ end,
})
return inspect
-
diff --git a/.config/wezterm/keys.lua b/.config/wezterm/keys.lua
index 0bba603..c92054b 100644
--- a/.config/wezterm/keys.lua
+++ b/.config/wezterm/keys.lua
@@ -1,40 +1,50 @@
-local wezterm = require "wezterm"
+local wezterm = require("wezterm")
local act = wezterm.action
local act_callback = wezterm.action_callback
local function kb(mods, key, action)
- return { mods = mods, key = key, action = action }
+ return { mods = mods, key = key, action = action }
end
return {
- disable_default_key_bindings = true,
- keys = {
- -- tabs
- kb("SHIFT|CTRL", "t", act.SpawnTab "CurrentPaneDomain"),
- kb("SHIFT|CTRL", "q", act.CloseCurrentTab { confirm = false }),
- kb("SHIFT|CTRL", "LeftArrow", act.ActivateTabRelative(-1)),
- kb("SHIFT|CTRL", "RightArrow", act.ActivateTabRelative(1)),
+ disable_default_key_bindings = true,
+ keys = {
+ -- tabs
+ kb("SHIFT|CTRL", "t", act.SpawnTab("CurrentPaneDomain")),
+ kb("SHIFT|CTRL", "q", act.CloseCurrentTab({ confirm = false })),
+ kb("SHIFT|CTRL", "LeftArrow", act.ActivateTabRelative(-1)),
+ kb("SHIFT|CTRL", "RightArrow", act.ActivateTabRelative(1)),
- -- panes
- kb("SHIFT|ALT", "w", act.SplitVertical { domain = "CurrentPaneDomain" }),
- kb("SHIFT|ALT", "d", act.SplitHorizontal { domain = "CurrentPaneDomain" }),
- kb("SHIFT|ALT", "q", act.CloseCurrentPane { confirm = false }),
- kb("SHIFT|ALT", "UpArrow", act.ActivatePaneDirection "Up"),
- kb("SHIFT|ALT", "DownArrow", act.ActivatePaneDirection "Down"),
- kb("SHIFT|ALT", "LeftArrow", act.ActivatePaneDirection "Left"),
- kb("SHIFT|ALT", "RightArrow", act.ActivatePaneDirection "Right"),
+ -- panes
+ kb("SHIFT|ALT", "w", act.SplitVertical({ domain = "CurrentPaneDomain" })),
+ kb("SHIFT|ALT", "d", act.SplitHorizontal({ domain = "CurrentPaneDomain" })),
+ kb("SHIFT|ALT", "q", act.CloseCurrentPane({ confirm = false })),
+ kb("SHIFT|ALT", "UpArrow", act.ActivatePaneDirection("Up")),
+ kb("SHIFT|ALT", "DownArrow", act.ActivatePaneDirection("Down")),
+ kb("SHIFT|ALT", "LeftArrow", act.ActivatePaneDirection("Left")),
+ kb("SHIFT|ALT", "RightArrow", act.ActivatePaneDirection("Right")),
+
+ kb("", "PageUp", act.ScrollByLine(-1)),
+ kb("SHIFT", "PageUp", act.ScrollByLine(-5)),
+ kb("", "PageDown", act.ScrollByLine(1)),
+ kb("SHIFT", "PageDown", act.ScrollByLine(5)),
- -- general
- kb("CTRL", "c", act_callback(function(w, p)
- local has_selection = w:get_selection_text_for_pane(p) ~= ""
+ -- general
+ kb(
+ "CTRL",
+ "c",
+ act_callback(function(w, p)
+ local has_selection = w:get_selection_text_for_pane(p) ~= ""
- if has_selection then
- w:perform_action(act.CopyTo "Clipboard", p)
- else
- w:perform_action(act.SendKey { mods = "CTRL", key = "c" }, p)
- end
- end)),
- kb("CTRL", "v", act.PasteFrom "Clipboard"),
- kb("CTRL", "f", act.Search "CurrentSelectionOrEmptyString")
- }
+ if has_selection then
+ w:perform_action(act.CopyTo("Clipboard"), p)
+ else
+ w:perform_action(act.SendKey({ mods = "CTRL", key = "c" }), p)
+ end
+ end)
+ ),
+ kb("CTRL", "v", act.PasteFrom("Clipboard")),
+ kb("CTRL", "f", act.Search("CurrentSelectionOrEmptyString")),
+ kb("SHIFT|CTRL", "l", act.ShowDebugOverlay),
+ },
}
diff --git a/.config/wezterm/stylua.toml b/.config/wezterm/stylua.toml
new file mode 100644
index 0000000..5cc0b69
--- /dev/null
+++ b/.config/wezterm/stylua.toml
@@ -0,0 +1,5 @@
+call_parentheses = "None"
+indent_type = "Spaces"
+
+[sort_requires]
+enabled = true
diff --git a/.config/wezterm/wezterm.lua b/.config/wezterm/wezterm.lua
index 0cd46b6..86014f6 100644
--- a/.config/wezterm/wezterm.lua
+++ b/.config/wezterm/wezterm.lua
@@ -1,61 +1,122 @@
-local wezterm = require "wezterm"
+local wezterm = require("wezterm")
function R(name)
- local m = require(name)
- return m
+ local m = require(name)
+ return m
+end
+
+-- C = {
+-- bg_dark = "#161b22",
+-- bg = "#1b2026",
+--
+-- fg_dark = "#77828c",
+-- fg = "#d9dfe4",
+--
+-- black = "#1f242b",
+-- red = "#df625d",
+-- yellow = "#fdc267",
+-- green = "#91d89a",
+-- blue = "#8ec6ff",
+-- pink = "#f2b9c1",
+-- cyan = "#77e2e3",
+-- white = "#d9dfe4",
+--
+-- bright = {
+-- red = "#f1726b",
+-- black = "#242930",
+-- yellow = "#ffd79d",
+-- green = "#a1e9aa",
+-- blue = "#add6ff",
+-- pink = "#ffcbd2",
+-- cyan = "#88f3f3",
+-- white = "#d9dfe4",
+-- },
+-- }
+
+function palette(colors)
+ return setmetatable(colors, {
+ __call = function(self)
+ return self[1]
+ end,
+ })
end
C = {
- fg = "#dfe2e7",
- bg = "#222831",
- fg_dark = "#8893a5",
- bg_dark = "#1e232b",
-
- black = "#252c36",
- red = "#de615c",
- green = "#91d89a",
- yellow = "#ffc469",
- blue = "#8fc7ff",
- pink = "#f2b9c1",
- cyan = "#9cfdff",
- white = "#dfe2e7",
-
- bright = {
- black = "#2c3440",
- red = "#e8908d",
- green = "#b2e4b8",
- yellow = "#ffd696",
- blue = "#b1d8ff",
- pink = "#f6ced4",
- cyan = "#bafeff",
- white = "#e9ebee",
- }
-}
+ fg = palette {
+ low = "#999fa7",
+ "#b8bec7",
+ high = "#d8dfe7",
+ },
+
+ bg = palette {
+ lowest = "#070c11",
+ low = "#0c1116",
+ "#12161c",
+ high = "#171c22",
+ highest = "#1d2228",
+ },
+
+ border = palette {
+ "#323b46",
+ variant = "#262f39",
+ },
+
+ red = palette {
+ "#ff928a",
+ bright = "#ffb2a9",
+ },
+ orange = palette {
+ "#ff9f6f",
+ bright = "#ffc08e",
+ },
+ yellow = palette {
+ "#ecb256",
+ bright = "#ffd278",
+ },
+ green = palette {
+ "#8bd294",
+ bright = "#abf3b3",
+ },
+ cyan = palette {
+ "#6dd3c2",
+ bright = "#8ef4e2",
+ },
+ blue = palette {
+ "#8bc3fc",
+ bright = "#abe4ff",
+ },
+ purple = palette {
+ "#c4b1f6",
+ bright = "#e4d1ff",
+ },
+ pink = palette {
+ "#e5acb4",
+ bright = "#ffccd4"
+ },
+ transparent = "#00000000",
+}
function M(...)
- local mt = {}
- for _,t in ipairs {...} do
- for k,v in pairs(t) do
- mt[k] = v
- end
- end
- return mt
+ local mt = {}
+ for _, t in ipairs({ ... }) do
+ for k, v in pairs(t) do
+ mt[k] = v
+ end
+ end
+ return mt
end
wezterm.on("bell", function()
- wezterm.background_child_process {
- "notify-send",
- "bell"
- }
+ wezterm.background_child_process({
+ "notify-send",
+ "bell",
+ })
end)
-return M(
- {
- window_close_confirmation = "NeverPrompt",
- check_for_updates = false
- },
- R "appearance",
- R "keys"
-)
+return M({
+ window_close_confirmation = "NeverPrompt",
+ check_for_updates = false,
+ warn_about_missing_glyphs = false,
+}, R("appearance"), R("keys"))
-- return {}