forked from Shinonome/caelestia-cli
Merge pull request #6 from caelestia-dots/python-rework
feat: rewrite in python
This commit is contained in:
+2
-1
@@ -1 +1,2 @@
|
|||||||
/data/schemes/dynamic/
|
__pycache__/
|
||||||
|
/dist/
|
||||||
|
|||||||
@@ -1,34 +1,17 @@
|
|||||||
# caelestia-scripts
|
# caelestia-cli
|
||||||
|
|
||||||
A collection of scripts for my caelestia dotfiles.
|
The main control script for the Caelestia dotfiles.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Clone this repo.
|
### Package manager
|
||||||
|
|
||||||
Run `install/scripts.fish`.
|
TODO
|
||||||
`~/.local/bin` must be in your path.
|
|
||||||
|
### Manual installation
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```
|
TODO
|
||||||
> caelestia help
|
|
||||||
Usage: caelestia COMMAND [ ...args ]
|
|
||||||
|
|
||||||
COMMAND := help | install | shell | toggle | workspace-action | scheme | screenshot | record | clipboard | clipboard-delete | emoji-picker | wallpaper | pip
|
|
||||||
|
|
||||||
help: show this help message
|
|
||||||
install: install a module
|
|
||||||
shell: start the shell or message it
|
|
||||||
toggle: toggle a special workspace
|
|
||||||
workspace-action: execute a Hyprland workspace dispatcher in the current group
|
|
||||||
scheme: change the current colour scheme
|
|
||||||
variant: change the current scheme variant
|
|
||||||
screenshot: take a screenshot
|
|
||||||
record: take a screen recording
|
|
||||||
clipboard: open clipboard history
|
|
||||||
clipboard-delete: delete an item from clipboard history
|
|
||||||
emoji-picker: open the emoji picker
|
|
||||||
wallpaper: change the wallpaper
|
|
||||||
pip: move the focused window into picture in picture mode or start the pip daemon
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
set -l chosen_item (cliphist list | fuzzel --dmenu --prompt='del > ' --placeholder='Delete from clipboard')
|
|
||||||
test -n "$chosen_item" && echo "$chosen_item" | cliphist delete
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
set -l chosen_item (cliphist list | fuzzel --dmenu --placeholder='Type to search clipboard')
|
|
||||||
test -n "$chosen_item" && echo "$chosen_item" | cliphist decode | wl-copy
|
|
||||||
+30
-60
@@ -1,38 +1,35 @@
|
|||||||
set -l seen '__fish_seen_subcommand_from'
|
set -l seen '__fish_seen_subcommand_from'
|
||||||
set -l has_opt '__fish_contains_opt'
|
set -l has_opt '__fish_contains_opt'
|
||||||
set -l commands help install shell toggle workspace-action scheme variant screenshot record clipboard clipboard-delete emoji-picker wallpaper pip
|
|
||||||
|
set -l commands shell toggle workspace-action scheme screenshot record clipboard emoji-picker wallpaper pip
|
||||||
set -l not_seen "not $seen $commands"
|
set -l not_seen "not $seen $commands"
|
||||||
|
|
||||||
# Disable file completions
|
# Disable file completions
|
||||||
complete -c caelestia -f
|
complete -c caelestia -f
|
||||||
|
|
||||||
|
# Add help for any command
|
||||||
|
complete -c caelestia -s 'h' -l 'help' -d 'Show help'
|
||||||
|
|
||||||
# Subcommands
|
# Subcommands
|
||||||
complete -c caelestia -n $not_seen -a 'help' -d 'Show help'
|
|
||||||
complete -c caelestia -n $not_seen -a 'install' -d 'Install a module'
|
|
||||||
complete -c caelestia -n $not_seen -a 'shell' -d 'Start the shell or message it'
|
complete -c caelestia -n $not_seen -a 'shell' -d 'Start the shell or message it'
|
||||||
complete -c caelestia -n $not_seen -a 'toggle' -d 'Toggle a special workspace'
|
complete -c caelestia -n $not_seen -a 'toggle' -d 'Toggle a special workspace'
|
||||||
complete -c caelestia -n $not_seen -a 'workspace-action' -d 'Exec a dispatcher in the current group'
|
complete -c caelestia -n $not_seen -a 'workspace-action' -d 'Exec a dispatcher in the current group'
|
||||||
complete -c caelestia -n $not_seen -a 'scheme' -d 'Switch the current colour scheme'
|
complete -c caelestia -n $not_seen -a 'scheme' -d 'Manage the colour scheme'
|
||||||
complete -c caelestia -n $not_seen -a 'variant' -d 'Switch the current scheme variant'
|
|
||||||
complete -c caelestia -n $not_seen -a 'screenshot' -d 'Take a screenshot'
|
complete -c caelestia -n $not_seen -a 'screenshot' -d 'Take a screenshot'
|
||||||
complete -c caelestia -n $not_seen -a 'record' -d 'Take a screen recording'
|
complete -c caelestia -n $not_seen -a 'record' -d 'Start a screen recording'
|
||||||
complete -c caelestia -n $not_seen -a 'clipboard' -d 'Open clipboard history'
|
complete -c caelestia -n $not_seen -a 'clipboard' -d 'Open clipboard history'
|
||||||
complete -c caelestia -n $not_seen -a 'clipboard-delete' -d 'Delete from clipboard history'
|
complete -c caelestia -n $not_seen -a 'emoji-picker' -d 'Toggle the emoji picker'
|
||||||
complete -c caelestia -n $not_seen -a 'emoji-picker' -d 'Open the emoji picker'
|
complete -c caelestia -n $not_seen -a 'wallpaper' -d 'Manage the wallpaper'
|
||||||
complete -c caelestia -n $not_seen -a 'wallpaper' -d 'Change the wallpaper'
|
|
||||||
complete -c caelestia -n $not_seen -a 'pip' -d 'Picture in picture utilities'
|
complete -c caelestia -n $not_seen -a 'pip' -d 'Picture in picture utilities'
|
||||||
|
|
||||||
# Install
|
|
||||||
set -l commands all btop discord firefox fish foot fuzzel hypr safeeyes scripts shell slurp spicetify gtk qt vscode
|
|
||||||
complete -c caelestia -n "$seen install && not $seen $commands" -a "$commands"
|
|
||||||
|
|
||||||
# Shell
|
# Shell
|
||||||
set -l commands help mpris drawers wallpaper notifs
|
set -l commands mpris drawers wallpaper notifs
|
||||||
set -l not_seen "$seen shell && not $seen $commands"
|
set -l not_seen "$seen shell && not $seen $commands"
|
||||||
complete -c caelestia -n $not_seen -a 'help' -d 'Show IPC commands'
|
complete -c caelestia -n $not_seen -s 's' -l 'show' -d 'Print all IPC commands'
|
||||||
|
complete -c caelestia -n $not_seen -s 'l' -l 'log' -d 'Print the shell log'
|
||||||
complete -c caelestia -n $not_seen -a 'mpris' -d 'Mpris control'
|
complete -c caelestia -n $not_seen -a 'mpris' -d 'Mpris control'
|
||||||
complete -c caelestia -n $not_seen -a 'drawers' -d 'Toggle drawers'
|
complete -c caelestia -n $not_seen -a 'drawers' -d 'Toggle drawers'
|
||||||
complete -c caelestia -n $not_seen -a 'wallpaper' -d 'Wallpaper control'
|
complete -c caelestia -n $not_seen -a 'wallpaper' -d 'Wallpaper control (for internal use)'
|
||||||
complete -c caelestia -n $not_seen -a 'notifs' -d 'Notification control'
|
complete -c caelestia -n $not_seen -a 'notifs' -d 'Notification control'
|
||||||
|
|
||||||
set -l commands getActive play pause playPause stop next previous list
|
set -l commands getActive play pause playPause stop next previous list
|
||||||
@@ -81,53 +78,26 @@ set -l commands workspace workspacegroup movetoworkspace movetoworkspacegroup
|
|||||||
complete -c caelestia -n "$seen workspace-action && not $seen $commands" -a "$commands" -d 'action'
|
complete -c caelestia -n "$seen workspace-action && not $seen $commands" -a "$commands" -d 'action'
|
||||||
|
|
||||||
# Scheme
|
# Scheme
|
||||||
set -q XDG_DATA_HOME && set -l data_dir $XDG_DATA_HOME || set -l data_dir $HOME/.local/share
|
complete -c caelestia -n "$seen scheme" -s 'r' -l 'random' -d 'Switch to a random scheme'
|
||||||
set -l scheme_dir $data_dir/caelestia/scripts/data/schemes
|
complete -c caelestia -n "$seen scheme" -s 'n' -l 'name' -d 'Set scheme name'
|
||||||
set -l schemes (basename -a (find $scheme_dir/ -mindepth 1 -maxdepth 1 -type d))
|
complete -c caelestia -n "$seen scheme" -s 'f' -l 'flavour' -d 'Set scheme flavour'
|
||||||
set -l commands 'print' $schemes
|
complete -c caelestia -n "$seen scheme" -s 'm' -l 'mode' -d 'Set scheme mode' -a 'light dark'
|
||||||
complete -c caelestia -n "$seen scheme && not $seen $commands" -a 'print' -d 'Generate and print a colour scheme for an image'
|
complete -c caelestia -n "$seen scheme" -s 'v' -l 'variant' -d 'Set scheme variant' -a 'vibrant tonalspot expressive fidelity fruitsalad rainbow neutral content monochrome'
|
||||||
complete -c caelestia -n "$seen scheme && not $seen $commands" -a "$schemes" -d 'scheme'
|
|
||||||
for scheme in $schemes
|
|
||||||
set -l flavours (basename -a (find $scheme_dir/$scheme/ -mindepth 1 -maxdepth 1 -type d) 2> /dev/null)
|
|
||||||
set -l modes (basename -s .txt (find $scheme_dir/$scheme/ -mindepth 1 -maxdepth 1 -type f) 2> /dev/null)
|
|
||||||
if test -n "$modes"
|
|
||||||
complete -c caelestia -n "$seen scheme && $seen $scheme && not $seen $modes" -a "$modes" -d 'mode'
|
|
||||||
else
|
|
||||||
complete -c caelestia -n "$seen scheme && $seen $scheme && not $seen $flavours" -a "$flavours" -d 'flavour'
|
|
||||||
for flavour in $flavours
|
|
||||||
set -l modes (basename -s .txt (find $scheme_dir/$scheme/$flavour/ -mindepth 1 -maxdepth 1 -type f))
|
|
||||||
complete -c caelestia -n "$seen scheme && $seen $scheme && $seen $flavour && not $seen $modes" -a "$modes" -d 'mode'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Variant
|
|
||||||
set -l commands vibrant tonalspot expressive fidelity fruitsalad rainbow neutral content monochrome
|
|
||||||
complete -c caelestia -n "$seen variant && not $seen $commands" -a "$commands" -d 'variant'
|
|
||||||
|
|
||||||
# Record
|
# Record
|
||||||
set -l not_seen "$seen record && not $has_opt -s h help"
|
complete -c caelestia -n "$seen record" -s 'r' -l 'region' -d 'Capture region'
|
||||||
complete -c caelestia -n "$not_seen && not $has_opt -s s sound && not $has_opt -s r region && not $has_opt -s c compression && not $has_opt -s H hwaccel" \
|
complete -c caelestia -n "$seen record" -s 's' -l 'sound' -d 'Capture sound'
|
||||||
-s 'h' -l 'help' -d 'Show help'
|
|
||||||
complete -c caelestia -n "$not_seen && not $has_opt -s s sound" -s 's' -l 'sound' -d 'Capture sound'
|
# Clipboard
|
||||||
complete -c caelestia -n "$not_seen && not $has_opt -s r region" -s 'r' -l 'region' -d 'Capture region'
|
complete -c caelestia -n "$seen clipboard" -s 'd' -l 'delete' -d 'Delete from cliboard history'
|
||||||
complete -c caelestia -n "$not_seen && not $has_opt -s c compression" -s 'c' -l 'compression' -d 'Compression level of file' -r
|
|
||||||
complete -c caelestia -n "$not_seen && not $has_opt -s H hwaccel" -s 'H' -l 'hwaccel' -d 'Use hardware acceleration'
|
|
||||||
|
|
||||||
# Wallpaper
|
# Wallpaper
|
||||||
set -l not_seen "$seen wallpaper && not $has_opt -s h help && not $has_opt -s f file && not $has_opt -s d directory"
|
complete -c caelestia -n "$seen wallpaper" -s 'p' -l 'print' -d 'Print the scheme for a wallpaper' -rF
|
||||||
complete -c caelestia -n $not_seen -s 'h' -l 'help' -d 'Show help'
|
complete -c caelestia -n "$seen wallpaper" -s 'r' -l 'random' -d 'Switch to a random wallpaper' -rF
|
||||||
complete -c caelestia -n $not_seen -s 'f' -l 'file' -d 'The file to switch to' -r
|
complete -c caelestia -n "$seen wallpaper" -s 'f' -l 'file' -d 'The file to switch to' -rF
|
||||||
complete -c caelestia -n $not_seen -s 'd' -l 'directory' -d 'The directory to select from' -r
|
complete -c caelestia -n "$seen wallpaper" -s 'n' -l 'no-filter' -d 'Do not filter by size'
|
||||||
|
complete -c caelestia -n "$seen wallpaper" -s 't' -l 'threshold' -d 'The threshold to filter by' -r
|
||||||
complete -c caelestia -n "$seen wallpaper && $has_opt -s f file" -F
|
complete -c caelestia -n "$seen wallpaper" -s 'N' -l 'no-smart' -d 'Disable smart mode switching'
|
||||||
complete -c caelestia -n "$seen wallpaper && $has_opt -s d directory" -F
|
|
||||||
|
|
||||||
set -l not_seen "$seen wallpaper && $has_opt -s d directory && not $has_opt -s F no-filter && not $has_opt -s t threshold"
|
|
||||||
complete -c caelestia -n $not_seen -s 'F' -l 'no-filter' -d 'Do not filter by size'
|
|
||||||
complete -c caelestia -n $not_seen -s 't' -l 'threshold' -d 'The threshold to filter by' -r
|
|
||||||
|
|
||||||
# Pip
|
# Pip
|
||||||
set -l not_seen "$seen pip && not $has_opt -s h help && not $has_opt -s d daemon"
|
complete -c caelestia -n "$seen pip" -s 'd' -l 'daemon' -d 'Start in daemon mode'
|
||||||
complete -c caelestia -n $not_seen -s 'h' -l 'help' -d 'Show help'
|
|
||||||
complete -c caelestia -n $not_seen -s 'd' -l 'daemon' -d 'Start in daemon mode'
|
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
{
|
|
||||||
"toggles": {
|
|
||||||
"communication": {
|
|
||||||
"apps": [
|
|
||||||
{
|
|
||||||
"selector": ".class == \"discord\"",
|
|
||||||
"spawn": "discord",
|
|
||||||
"action": "spawn move"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selector": ".class == \"whatsapp\"",
|
|
||||||
"spawn": "firefox --name whatsapp -P whatsapp 'https://web.whatsapp.com'",
|
|
||||||
"action": "move",
|
|
||||||
"extraCond": "grep -q 'Name=whatsapp' ~/.mozilla/firefox/profiles.ini"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"music": {
|
|
||||||
"apps": [
|
|
||||||
{
|
|
||||||
"selector": ".class == \"Spotify\" or .initialTitle == \"Spotify\" or .initialTitle == \"Spotify Free\"",
|
|
||||||
"spawn": "spicetify watch -s",
|
|
||||||
"action": "spawn move"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selector": ".class == \"feishin\"",
|
|
||||||
"spawn": "feishin",
|
|
||||||
"action": "move"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"sysmon": {
|
|
||||||
"apps": [
|
|
||||||
{
|
|
||||||
"selector": ".class == \"btop\" and .title == \"btop\" and .workspace.name == \"special:sysmon\"",
|
|
||||||
"spawn": "foot -a 'btop' -T 'btop' -- btop",
|
|
||||||
"action": "spawn"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"todo": {
|
|
||||||
"apps": [
|
|
||||||
{
|
|
||||||
"selector": ".class == \"Todoist\"",
|
|
||||||
"spawn": "todoist",
|
|
||||||
"action": "spawn move"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
set -l chosen_item (cat (dirname (status filename))/data/emojis.txt | fuzzel --dmenu --placeholder='Type to search emojis')
|
|
||||||
test -n "$chosen_item" && echo "$chosen_item" | cut -d ' ' -f 1 | tr -d '\n' | wl-copy
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
install-deps git btop
|
|
||||||
|
|
||||||
set -l dist $CONFIG/btop
|
|
||||||
|
|
||||||
# Update/Clone repo
|
|
||||||
update-repo btop $dist
|
|
||||||
sed -i 's|$SRC|'$dist'|g' $dist/btop.conf
|
|
||||||
|
|
||||||
# Install systemd service
|
|
||||||
setup-systemd-monitor btop $dist
|
|
||||||
|
|
||||||
log 'Done.'
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
install-deps git discord equicord-installer-bin
|
|
||||||
sudo Equilotl -install -location /opt/discord
|
|
||||||
sudo Equilotl -install-openasar -location /opt/discord
|
|
||||||
|
|
||||||
set -l dist $C_DATA/discord
|
|
||||||
|
|
||||||
# Update/Clone repo
|
|
||||||
update-repo discord $dist
|
|
||||||
|
|
||||||
# Install systemd service
|
|
||||||
setup-systemd-monitor discord $dist
|
|
||||||
|
|
||||||
# Link themes to client configs
|
|
||||||
set -l clients Vencord Equicord discord vesktop equibop legcord $argv
|
|
||||||
for client in $clients
|
|
||||||
if test -d $CONFIG/$client
|
|
||||||
log "Linking themes for $client"
|
|
||||||
install-link $dist/themes $CONFIG/$client/themes
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
log 'Done.'
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
install-deps git inotify-tools
|
|
||||||
|
|
||||||
set -l dist $C_DATA/firefox
|
|
||||||
|
|
||||||
# Update/Clone repo
|
|
||||||
update-repo firefox $dist
|
|
||||||
|
|
||||||
# Install native app manifest
|
|
||||||
for dev in mozilla zen
|
|
||||||
if test -d $HOME/.$dev
|
|
||||||
mkdir -p $HOME/.$dev/native-messaging-hosts
|
|
||||||
cp $dist/native_app/manifest.json $HOME/.$dev/native-messaging-hosts/caelestiafox.json
|
|
||||||
sed -i "s|\$SRC|$dist|g" $HOME/.$dev/native-messaging-hosts/caelestiafox.json
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Install zen css
|
|
||||||
if test -d $HOME/.zen
|
|
||||||
for profile in $HOME/.zen/*/chrome
|
|
||||||
for file in userChrome userContent
|
|
||||||
if test -f $profile/$file.css
|
|
||||||
set -l imp "@import url('$dist/zen/$file.css');"
|
|
||||||
grep -qFx $imp $profile/$file.css || printf '%s\n%s' $imp "$(cat $profile/$file.css)" > $profile/$file.css
|
|
||||||
else
|
|
||||||
echo "@import url('$dist/zen/$file.css');" > $profile/$file.css
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
log 'Done.'
|
|
||||||
log 'Please install the extension manually from https://addons.mozilla.org/en-US/firefox/addon/caelestiafox'
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
install-deps git starship fastfetch
|
|
||||||
|
|
||||||
set -l dist $C_DATA/fish
|
|
||||||
|
|
||||||
# Update/Clone repo
|
|
||||||
update-repo fish $dist
|
|
||||||
|
|
||||||
# Install fish config
|
|
||||||
install-link $dist/config.fish $CONFIG/fish/config.fish
|
|
||||||
|
|
||||||
# Install fish greeting
|
|
||||||
install-link $dist/fish_greeting.fish $CONFIG/fish/functions/fish_greeting.fish
|
|
||||||
|
|
||||||
# Install starship config
|
|
||||||
install-link $dist/starship.toml $CONFIG/starship.toml
|
|
||||||
|
|
||||||
# Install fastfetch config
|
|
||||||
install-link $dist/fastfetch.jsonc $CONFIG/fastfetch/config.jsonc
|
|
||||||
|
|
||||||
log 'Done.'
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
install-deps git foot inotify-tools
|
|
||||||
|
|
||||||
set -l dist $CONFIG/foot
|
|
||||||
|
|
||||||
update-repo foot $dist
|
|
||||||
sed -i 's|$SRC|'$dist'|g' $dist/foot.ini
|
|
||||||
|
|
||||||
install-link $dist/foot.fish ~/.local/bin/foot
|
|
||||||
|
|
||||||
log 'Done.'
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
install-deps git fuzzel-git
|
|
||||||
|
|
||||||
set -l dist $CONFIG/fuzzel
|
|
||||||
|
|
||||||
# Clone repo
|
|
||||||
update-repo fuzzel $dist
|
|
||||||
|
|
||||||
# Install systemd service
|
|
||||||
setup-systemd-monitor fuzzel $dist
|
|
||||||
|
|
||||||
log 'Done.'
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
install-deps git adw-gtk-theme
|
|
||||||
install-optional-deps 'papirus-icon-theme (icon theme)'
|
|
||||||
|
|
||||||
set -l dist $C_DATA/gtk
|
|
||||||
|
|
||||||
# Update/Clone repo
|
|
||||||
update-repo gtk $dist
|
|
||||||
|
|
||||||
# Install systemd service
|
|
||||||
setup-systemd-monitor gtk $dist
|
|
||||||
|
|
||||||
# Set theme
|
|
||||||
gsettings set org.gnome.desktop.interface gtk-theme \'adw-gtk3-dark\'
|
|
||||||
if pacman -Q papirus-icon-theme &> /dev/null && test "$(gsettings get org.gnome.desktop.interface icon-theme | cut -d - -f 1 | string sub -s 2)" != Papirus
|
|
||||||
read -l -p "input 'Set icon theme to Papirus? [Y/n] ' -n" confirm
|
|
||||||
test "$confirm" = 'n' -o "$confirm" = 'N' || gsettings set org.gnome.desktop.interface icon-theme \'Papirus-Dark\'
|
|
||||||
end
|
|
||||||
|
|
||||||
log 'Done.'
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
install-deps git uwsm hyprland-git hyprpaper-git hyprlock-git hypridle-git polkit-gnome gnome-keyring wl-clipboard wireplumber app2unit-git
|
|
||||||
install-optional-deps 'gammastep (night light)' 'wlogout (secondary session menu)' 'grimblast-git (screenshot freeze)' 'hyprpicker-git (colour picker)' 'foot (terminal emulator)' 'firefox (web browser)' 'vscodium-bin (IDE)' 'thunar (file manager)' 'nemo (secondary file manager)' 'fuzzel (secondary app launcher)' 'ydotool (alternate paste)' 'trash-cli (auto trash)'
|
|
||||||
|
|
||||||
set -l hypr $CONFIG/hypr
|
|
||||||
|
|
||||||
# Cause hyprland autogenerates a config file when it is removed
|
|
||||||
set -l remote https://github.com/caelestia-dots/hypr.git
|
|
||||||
if test -d $hypr
|
|
||||||
cd $hypr || exit
|
|
||||||
if test "$(git config --get remote.origin.url)" != $remote
|
|
||||||
cd .. || exit
|
|
||||||
confirm-overwrite $hypr dummy
|
|
||||||
git clone $remote /tmp/caelestia-hypr
|
|
||||||
rm -rf $hypr && mv /tmp/caelestia-hypr $hypr
|
|
||||||
else
|
|
||||||
git pull
|
|
||||||
end
|
|
||||||
else
|
|
||||||
git clone $remote $dir
|
|
||||||
end
|
|
||||||
|
|
||||||
# Install uwsm envs
|
|
||||||
install-link $hypr/uwsm $CONFIG/uwsm
|
|
||||||
|
|
||||||
# Enable ydotool if installed
|
|
||||||
pacman -Q ydotool &> /dev/null && systemctl --user enable --now ydotool.service
|
|
||||||
|
|
||||||
log 'Done.'
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
install-deps git darkly-bin
|
|
||||||
install-optional-deps 'papirus-icon-theme (icon theme)'
|
|
||||||
|
|
||||||
set -l dist $C_DATA/qt
|
|
||||||
|
|
||||||
# Update/Clone repo
|
|
||||||
update-repo qt $dist
|
|
||||||
|
|
||||||
# Install systemd service
|
|
||||||
setup-systemd-monitor qt $dist
|
|
||||||
|
|
||||||
# Change settings
|
|
||||||
confirm-copy $dist/qtct.conf $CONFIG/qt5ct/qt5ct.conf
|
|
||||||
confirm-copy $dist/qtct.conf $CONFIG/qt6ct/qt6ct.conf
|
|
||||||
|
|
||||||
log 'Done.'
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
install-deps git dart-sass aylurs-gtk-shell-git alsa-utils libappindicator-gtk3
|
|
||||||
|
|
||||||
# Update/Clone repo
|
|
||||||
update-repo safeeyes $C_DATA/safeeyes
|
|
||||||
|
|
||||||
if which systemctl &> /dev/null
|
|
||||||
log 'Installing systemd service...'
|
|
||||||
|
|
||||||
set -l systemd $CONFIG/systemd/user
|
|
||||||
mkdir -p $systemd
|
|
||||||
echo -n "
|
|
||||||
[Unit]
|
|
||||||
Description=Protect your eyes from eye strain using this simple and beautiful, yet extensible break reminder.
|
|
||||||
After=graphical-session.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=exec
|
|
||||||
ExecStart=/usr/bin/ags run -d $C_DATA/safeeyes
|
|
||||||
Restart=on-failure
|
|
||||||
Slice=app-graphical.slice
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=graphical-session.target
|
|
||||||
" > $systemd/caelestia-safeeyes.service
|
|
||||||
|
|
||||||
systemctl --user daemon-reload
|
|
||||||
systemctl --user enable --now caelestia-safeeyes.service
|
|
||||||
end
|
|
||||||
|
|
||||||
log 'Done.'
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
install-deps git hyprland-git hyprpaper-git imagemagick wl-clipboard fuzzel-git socat foot jq python xdg-user-dirs python-materialyoucolor-git app2unit-git grim wayfreeze-git wl-screenrec swappy
|
|
||||||
install-optional-deps 'discord (messaging app)' 'btop (system monitor)' 'zen-browser (web browser)'
|
|
||||||
|
|
||||||
set -l dist $C_DATA/scripts
|
|
||||||
|
|
||||||
# Update/Clone repo
|
|
||||||
update-repo scripts $dist
|
|
||||||
|
|
||||||
# Install to path
|
|
||||||
install-link $dist/main.fish ~/.local/bin/caelestia
|
|
||||||
|
|
||||||
# Install completions
|
|
||||||
test -e $CONFIG/fish/completions/caelestia.fish && rm $CONFIG/fish/completions/caelestia.fish
|
|
||||||
mkdir -p $CONFIG/fish/completions
|
|
||||||
cp $dist/completions/caelestia.fish $CONFIG/fish/completions/caelestia.fish
|
|
||||||
|
|
||||||
log 'Done.'
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
if ! pacman -Q lm_sensors > /dev/null
|
|
||||||
sudo pacman -S --noconfirm lm_sensors
|
|
||||||
sudo sensors-detect --auto
|
|
||||||
end
|
|
||||||
|
|
||||||
install-deps git quickshell curl jq ttf-material-symbols-variable-git ttf-jetbrains-mono-nerd ttf-ibm-plex app2unit-git fd fish python-aubio python-pyaudio python-numpy cava networkmanager bluez-utils ddcutil brightnessctl imagemagick
|
|
||||||
install-optional-deps 'uwsm (for systems using uwsm)'
|
|
||||||
|
|
||||||
set -l shell $C_DATA/shell
|
|
||||||
|
|
||||||
# Update/Clone repo
|
|
||||||
update-repo shell $shell
|
|
||||||
|
|
||||||
if which systemctl &> /dev/null
|
|
||||||
log 'Installing systemd service...'
|
|
||||||
|
|
||||||
set -l systemd $CONFIG/systemd/user
|
|
||||||
mkdir -p $systemd
|
|
||||||
echo -n "
|
|
||||||
[Unit]
|
|
||||||
Description=A very segsy desktop shell.
|
|
||||||
After=graphical-session.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=exec
|
|
||||||
ExecStart=$shell/run.fish
|
|
||||||
Restart=on-failure
|
|
||||||
Slice=app-graphical.slice
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=graphical-session.target
|
|
||||||
" > $systemd/caelestia-shell.service
|
|
||||||
|
|
||||||
systemctl --user daemon-reload
|
|
||||||
systemctl --user enable --now caelestia-shell.service
|
|
||||||
end
|
|
||||||
|
|
||||||
log 'Done.'
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
install-deps git slurp
|
|
||||||
|
|
||||||
set -l dist $C_DATA/slurp
|
|
||||||
|
|
||||||
# Clone repo
|
|
||||||
update-repo slurp $dist
|
|
||||||
|
|
||||||
# Install systemd service
|
|
||||||
setup-systemd-monitor slurp $dist
|
|
||||||
|
|
||||||
# Install to path
|
|
||||||
install-link $dist/slurp ~/.local/bin/slurp
|
|
||||||
|
|
||||||
log 'Done.'
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
install-deps git spicetify-cli spicetify-marketplace-bin
|
|
||||||
|
|
||||||
set -l dist $C_DATA/spicetify
|
|
||||||
|
|
||||||
# Clone repo
|
|
||||||
update-repo spicetify $dist
|
|
||||||
|
|
||||||
# Install systemd service
|
|
||||||
setup-systemd-monitor spicetify $dist
|
|
||||||
|
|
||||||
# Install theme files
|
|
||||||
mkdir -p $CONFIG/spicetify/Themes/caelestia
|
|
||||||
cp $dist/color.ini $CONFIG/spicetify/Themes/caelestia/color.ini
|
|
||||||
cp $dist/user.css $CONFIG/spicetify/Themes/caelestia/user.css
|
|
||||||
|
|
||||||
# Set spicetify theme
|
|
||||||
spicetify config current_theme caelestia color_scheme caelestia
|
|
||||||
|
|
||||||
# Setup marketplace
|
|
||||||
spicetify config custom_apps marketplace
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
. (dirname (status filename))/../util.fish
|
|
||||||
|
|
||||||
function confirm-overwrite -a path
|
|
||||||
if test -e $path -o -L $path
|
|
||||||
read -l -p "input '$(realpath $path 2> /dev/null || echo $path) already exists. Overwrite? [y/N] ' -n" confirm
|
|
||||||
if test "$confirm" = 'y' -o "$confirm" = 'Y'
|
|
||||||
log 'Continuing.'
|
|
||||||
test -z "$argv[2]" && rm -rf $path # If a second arg is provided, don't delete
|
|
||||||
else
|
|
||||||
log 'Exiting.'
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function install-deps
|
|
||||||
# All dependencies already installed
|
|
||||||
pacman -Q $argv &> /dev/null && return
|
|
||||||
|
|
||||||
for dep in $argv
|
|
||||||
# Skip if already installed
|
|
||||||
if ! pacman -Q $dep &> /dev/null
|
|
||||||
# If pacman can install it, use it, otherwise use an AUR helper
|
|
||||||
if pacman -Si $dep &> /dev/null
|
|
||||||
log "Installing dependency '$dep'"
|
|
||||||
sudo pacman -S --noconfirm $dep
|
|
||||||
else
|
|
||||||
# Get AUR helper or install if none
|
|
||||||
which yay &> /dev/null && set -l helper yay || set -l helper paru
|
|
||||||
if ! which $helper &> /dev/null
|
|
||||||
warn 'No AUR helper found'
|
|
||||||
read -l -p "input 'Install yay? [Y/n] ' -n" confirm
|
|
||||||
if test "$confirm" = 'n' -o "$confirm" = 'N'
|
|
||||||
warn "Manually install yay or paru and try again."
|
|
||||||
warn "Alternatively, install the dependencies '$argv' manually and try again."
|
|
||||||
exit
|
|
||||||
else
|
|
||||||
sudo pacman -S --needed git base-devel
|
|
||||||
git clone https://aur.archlinux.org/yay.git
|
|
||||||
cd yay
|
|
||||||
makepkg -si
|
|
||||||
cd ..
|
|
||||||
rm -rf yay
|
|
||||||
|
|
||||||
# First use, see https://github.com/Jguer/yay?tab=readme-ov-file#first-use
|
|
||||||
yay -Y --gendb
|
|
||||||
yay -Y --devel --save
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
log "Installing dependency '$dep'"
|
|
||||||
$helper -S --noconfirm $dep
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function install-optional-deps
|
|
||||||
for dep in $argv
|
|
||||||
set -l dep_name (string split -f 1 ' ' $dep)
|
|
||||||
if ! pacman -Q $dep_name &> /dev/null
|
|
||||||
read -l -p "input 'Install $dep? [Y/n] ' -n" confirm
|
|
||||||
test "$confirm" != 'n' -a "$confirm" != 'N' && install-deps $dep_name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function update-repo -a module dir
|
|
||||||
set -l remote https://github.com/caelestia-dots/$module.git
|
|
||||||
if test -d $dir
|
|
||||||
cd $dir || exit
|
|
||||||
|
|
||||||
# Delete and clone if it's a different git repo
|
|
||||||
if test "$(git config --get remote.origin.url)" != $remote
|
|
||||||
cd .. || exit
|
|
||||||
confirm-overwrite $dir
|
|
||||||
git clone $remote $dir
|
|
||||||
else
|
|
||||||
# Check for uncommitted changes
|
|
||||||
if test -n "$(git status --porcelain)"
|
|
||||||
read -l -p "input 'You have uncommitted changes in $dir. Stash, reset or exit? [S/r/e] ' -n" confirm
|
|
||||||
|
|
||||||
if test "$confirm" = 'e' -o "$confirm" = 'E'
|
|
||||||
log 'Exiting...'
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
if test "$confirm" = 'r' -o "$confirm" = 'R'
|
|
||||||
log 'Resetting to HEAD...'
|
|
||||||
git reset --hard
|
|
||||||
else
|
|
||||||
log 'Stashing changes...'
|
|
||||||
git stash
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
git pull
|
|
||||||
end
|
|
||||||
else
|
|
||||||
git clone $remote $dir
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function setup-systemd-monitor -a module dir
|
|
||||||
set -l systemd $CONFIG/systemd/user
|
|
||||||
if which systemctl &> /dev/null
|
|
||||||
log 'Installing systemd service...'
|
|
||||||
|
|
||||||
mkdir -p $systemd
|
|
||||||
echo "[Unit]
|
|
||||||
Description=Sync $module and caelestia schemes
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=$dir/monitor/update.fish" > $systemd/$module-monitor-scheme.service
|
|
||||||
echo "[Unit]
|
|
||||||
Description=Sync $module and caelestia schemes (monitor)
|
|
||||||
|
|
||||||
[Path]
|
|
||||||
PathModified=%S/caelestia/scheme/current.txt
|
|
||||||
Unit=$module-monitor-scheme.service
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=default.target" > $systemd/$module-monitor-scheme.path
|
|
||||||
|
|
||||||
systemctl --user daemon-reload
|
|
||||||
systemctl --user enable --now $module-monitor-scheme.path
|
|
||||||
systemctl --user start $module-monitor-scheme.service
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function install-link -a from to
|
|
||||||
if ! test -L $to -a "$(realpath $to 2> /dev/null)" = $from
|
|
||||||
mkdir -p (dirname $to)
|
|
||||||
confirm-overwrite $to
|
|
||||||
ln -s $from $to
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function confirm-copy -a from to
|
|
||||||
test -L $to -a "$(realpath $to 2> /dev/null)" = (realpath $from) && return # Return if symlink
|
|
||||||
cmp $from $to &> /dev/null && return # Return if files are the same
|
|
||||||
if test -e $to
|
|
||||||
read -l -p "input '$(realpath $to) already exists. Overwrite? [y/N] ' -n" confirm
|
|
||||||
test "$confirm" = 'y' -o "$confirm" = 'Y' && log 'Continuing.' || return
|
|
||||||
end
|
|
||||||
cp $from $to
|
|
||||||
end
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
install-deps git
|
|
||||||
|
|
||||||
set -l dist $C_DATA/vscode
|
|
||||||
|
|
||||||
# Update/Clone repo
|
|
||||||
update-repo vscode $dist
|
|
||||||
|
|
||||||
# Install settings
|
|
||||||
for prog in 'Code' 'Code - OSS' 'VSCodium'
|
|
||||||
set -l conf $CONFIG/$prog
|
|
||||||
if test -d $conf
|
|
||||||
confirm-copy $dist/settings.json $conf/User/settings.json
|
|
||||||
confirm-copy $dist/keybindings.json $conf/User/keybindings.json
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Install extension
|
|
||||||
for prog in code code-insiders codium
|
|
||||||
if which $prog &> /dev/null
|
|
||||||
log "Installing extensions for '$prog'"
|
|
||||||
if ! contains 'catppuccin.catppuccin-vsc-icons' ($prog --list-extensions)
|
|
||||||
read -l -p "input 'Install catppuccin icons (for light/dark integration)? [Y/n] ' -n" confirm
|
|
||||||
test "$confirm" = 'n' -o "$confirm" = 'N' || $prog --install-extension catppuccin.catppuccin-vsc-icons
|
|
||||||
end
|
|
||||||
$prog --install-extension $dist/caelestia-vscode-integration/caelestia-vscode-integration-*.vsix
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
log 'Done.'
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
set -l src (dirname (realpath (status filename)))
|
|
||||||
|
|
||||||
. $src/util.fish
|
|
||||||
|
|
||||||
if test "$argv[1]" = shell
|
|
||||||
# Start shell if no args
|
|
||||||
if test -z "$argv[2..]"
|
|
||||||
if qs list --all | grep "Config path: $C_DATA/shell/shell.qml" &> /dev/null
|
|
||||||
warn 'Shell already running'
|
|
||||||
else
|
|
||||||
$C_DATA/shell/run.fish
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if test "$argv[2]" = help
|
|
||||||
qs -p $C_DATA/shell ipc show
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
if qs list --all | grep "Config path: $C_DATA/shell/shell.qml" &> /dev/null
|
|
||||||
qs -p $C_DATA/shell ipc call $argv[2..]
|
|
||||||
else
|
|
||||||
warn 'Shell unavailable'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
if test "$argv[1]" = toggle
|
|
||||||
set -l valid_toggles communication music sysmon specialws todo
|
|
||||||
if contains -- "$argv[2]" $valid_toggles
|
|
||||||
if test $argv[2] = specialws
|
|
||||||
$src/toggles/specialws.fish
|
|
||||||
else
|
|
||||||
. $src/toggles/util.fish
|
|
||||||
toggle-workspace $argv[2]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
error "Invalid toggle: $argv[2]"
|
|
||||||
end
|
|
||||||
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
if test "$argv[1]" = workspace-action
|
|
||||||
$src/workspace-action.sh $argv[2..]
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
if test "$argv[1]" = scheme
|
|
||||||
if test "$argv[2]" = print
|
|
||||||
$src/scheme/gen-print-scheme.fish $argv[3..]
|
|
||||||
else
|
|
||||||
$src/scheme/main.fish $argv[2..]
|
|
||||||
end
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
if test "$argv[1]" = variant
|
|
||||||
set -l variants vibrant tonalspot expressive fidelity fruitsalad rainbow neutral content monochrome
|
|
||||||
if contains -- "$argv[2]" $variants
|
|
||||||
echo -n $argv[2] > $C_STATE/scheme/current-variant.txt
|
|
||||||
$src/scheme/gen-scheme.fish
|
|
||||||
else
|
|
||||||
error "Invalid variant: $argv[2]"
|
|
||||||
end
|
|
||||||
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
if test "$argv[1]" = install
|
|
||||||
set -l valid_modules scripts btop discord firefox fish foot fuzzel hypr safeeyes shell slurp spicetify gtk qt vscode
|
|
||||||
if test "$argv[2]" = all
|
|
||||||
for module in $valid_modules
|
|
||||||
$src/install/$module.fish $argv[3..]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
contains -- "$argv[2]" $valid_modules && $src/install/$argv[2].fish $argv[3..] || error "Invalid module: $argv[2]"
|
|
||||||
end
|
|
||||||
test -f $C_STATE/scheme/current.txt || $src/scheme/main.fish onedark # Init scheme after install or update
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
set -l valid_subcommands screenshot record clipboard clipboard-delete emoji-picker wallpaper pip
|
|
||||||
|
|
||||||
if contains -- "$argv[1]" $valid_subcommands
|
|
||||||
$src/$argv[1].fish $argv[2..]
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
test "$argv[1]" != help && error "Unknown command: $argv[1]"
|
|
||||||
|
|
||||||
echo 'Usage: caelestia COMMAND [ ...args ]'
|
|
||||||
echo
|
|
||||||
echo 'COMMAND := help | install | shell | toggle | workspace-action | scheme | screenshot | record | clipboard | clipboard-delete | emoji-picker | wallpaper | pip'
|
|
||||||
echo
|
|
||||||
echo ' help: show this help message'
|
|
||||||
echo ' install: install a module'
|
|
||||||
echo ' shell: start the shell or message it'
|
|
||||||
echo ' toggle: toggle a special workspace'
|
|
||||||
echo ' workspace-action: execute a Hyprland workspace dispatcher in the current group'
|
|
||||||
echo ' scheme: change the current colour scheme'
|
|
||||||
echo ' variant: change the current scheme variant'
|
|
||||||
echo ' screenshot: take a screenshot'
|
|
||||||
echo ' record: take a screen recording'
|
|
||||||
echo ' clipboard: open clipboard history'
|
|
||||||
echo ' clipboard-delete: delete an item from clipboard history'
|
|
||||||
echo ' emoji-picker: open the emoji picker'
|
|
||||||
echo ' wallpaper: change the wallpaper'
|
|
||||||
echo ' pip: move the focused window into picture in picture mode or start the pip daemon'
|
|
||||||
|
|
||||||
# Set exit status
|
|
||||||
test "$argv[1]" = help
|
|
||||||
exit
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
argparse -n 'caelestia-pip' -X 0 \
|
|
||||||
'h/help' \
|
|
||||||
'd/daemon' \
|
|
||||||
-- $argv
|
|
||||||
or exit
|
|
||||||
|
|
||||||
if set -q _flag_h
|
|
||||||
echo 'Usage:'
|
|
||||||
echo ' caelestia pip ( -h | --help )'
|
|
||||||
echo ' caelestia pip [ -d | --daemon ]'
|
|
||||||
echo
|
|
||||||
echo 'Options:'
|
|
||||||
echo ' -h, --help Print this help message and exit'
|
|
||||||
echo ' -d, --daemon Run this script in daemon mode'
|
|
||||||
echo
|
|
||||||
echo 'Normal mode (no args):'
|
|
||||||
echo ' Move and resize the active window to picture in picture default geometry.'
|
|
||||||
echo
|
|
||||||
echo 'Daemon mode:'
|
|
||||||
echo ' Set all picture in picture window initial geometry to default.'
|
|
||||||
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
function handle-window -a address workspace
|
|
||||||
set -l monitor_id (hyprctl workspaces -j | jq '.[] | select(.name == "'$workspace'").monitorID')
|
|
||||||
set -l monitor_size (hyprctl monitors -j | jq -r '.[] | select(.id == '$monitor_id') | "\(.width)\n\(.height)"')
|
|
||||||
set -l window_size (hyprctl clients -j | jq '.[] | select(.address == "'$address'").size[]')
|
|
||||||
set -l scale_factor (math $monitor_size[2] / 4 / $window_size[2])
|
|
||||||
set -l scaled_window_size (math -s 0 $window_size[1] x $scale_factor) (math -s 0 $window_size[2] x $scale_factor)
|
|
||||||
|
|
||||||
hyprctl dispatch "resizewindowpixel exact $scaled_window_size,address:$address" > /dev/null
|
|
||||||
hyprctl dispatch "movewindowpixel exact $(math -s 0 $monitor_size[1] x 0.98 - $scaled_window_size[1]) $(math -s 0 $monitor_size[2] x 0.97 - $scaled_window_size[2]),address:$address" > /dev/null
|
|
||||||
log "Handled window at address $address"
|
|
||||||
end
|
|
||||||
|
|
||||||
if set -q _flag_d
|
|
||||||
log 'Daemon started'
|
|
||||||
socat -U - UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | while read line
|
|
||||||
switch $line
|
|
||||||
case 'openwindow*'
|
|
||||||
set -l window (string sub -s 13 $line | string split ',')
|
|
||||||
if string match -qr '^(Picture(-| )in(-| )[Pp]icture)$' $window[4]
|
|
||||||
handle-window 0x$window[1] $window[2]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
set -l active_window (hyprctl activewindow -j | jq -r '"\(.address)\n\(.workspace.name)\n\(.floating)"')
|
|
||||||
if test $active_window[3] = true
|
|
||||||
handle-window $active_window
|
|
||||||
else
|
|
||||||
warn 'Focused window is not floating, ignoring'
|
|
||||||
end
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["hatchling", "hatch-vcs"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "caelestia"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
dynamic = ["version"]
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
caelestia = "caelestia:main"
|
||||||
|
|
||||||
|
[tool.hatch.version]
|
||||||
|
source = "vcs"
|
||||||
-99
@@ -1,99 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
function get-audio-source
|
|
||||||
pactl list short sources | grep '\.monitor.*RUNNING' | cut -f 2 | head -1
|
|
||||||
end
|
|
||||||
|
|
||||||
function get-region
|
|
||||||
slurp || exit 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function get-active-monitor
|
|
||||||
hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name'
|
|
||||||
end
|
|
||||||
|
|
||||||
argparse -n 'caelestia-record' -X 0 \
|
|
||||||
'h/help' \
|
|
||||||
's/sound' \
|
|
||||||
'r/region=?' \
|
|
||||||
'n/no-hwaccel' \
|
|
||||||
-- $argv
|
|
||||||
or exit
|
|
||||||
|
|
||||||
if set -q _flag_h
|
|
||||||
echo 'Usage:'
|
|
||||||
echo ' caelestia record ( -h | --help )'
|
|
||||||
echo ' caelestia record [ -s | --sound ] [ -r | --region ] [ -c | --compression ] [ -H | --hwaccel ]'
|
|
||||||
echo
|
|
||||||
echo 'Options:'
|
|
||||||
echo ' -h, --help Print this help message and exit'
|
|
||||||
echo ' -s, --sound Enable audio capturing'
|
|
||||||
echo ' -r, --region [ <region> ] The region to capture, current monitor if option not given, default region slurp'
|
|
||||||
echo ' -N, --no-hwaccel Do not use the GPU encoder'
|
|
||||||
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
set -l storage_dir (xdg-user-dir VIDEOS)/Recordings
|
|
||||||
set -l state_dir $C_STATE/record
|
|
||||||
|
|
||||||
mkdir -p $storage_dir
|
|
||||||
mkdir -p $state_dir
|
|
||||||
|
|
||||||
set -l file_ext 'mp4'
|
|
||||||
set -l recording_path "$state_dir/recording.$file_ext"
|
|
||||||
set -l notif_id_path "$state_dir/notifid.txt"
|
|
||||||
|
|
||||||
if pgrep wl-screenrec > /dev/null
|
|
||||||
pkill wl-screenrec
|
|
||||||
|
|
||||||
# Move to recordings folder
|
|
||||||
set -l new_recording_path "$storage_dir/recording_$(date '+%Y%m%d_%H-%M-%S').$file_ext"
|
|
||||||
mv $recording_path $new_recording_path
|
|
||||||
|
|
||||||
# Close start notification
|
|
||||||
if test -f $notif_id_path
|
|
||||||
gdbus call --session \
|
|
||||||
--dest org.freedesktop.Notifications \
|
|
||||||
--object-path /org/freedesktop/Notifications \
|
|
||||||
--method org.freedesktop.Notifications.CloseNotification \
|
|
||||||
(cat $notif_id_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Notification with actions
|
|
||||||
set -l action (notify-send 'Recording stopped' "Stopped recording $new_recording_path" -i 'video-x-generic' -a 'caelestia-record' \
|
|
||||||
--action='watch=Watch' --action='open=Open' --action='save=Save As' --action='delete=Delete')
|
|
||||||
|
|
||||||
switch $action
|
|
||||||
case 'watch'
|
|
||||||
app2unit -O $new_recording_path
|
|
||||||
case 'open'
|
|
||||||
dbus-send --session --dest=org.freedesktop.FileManager1 --type=method_call /org/freedesktop/FileManager1 org.freedesktop.FileManager1.ShowItems array:string:"file://$new_recording_path" string:'' \
|
|
||||||
|| app2unit -O (dirname $new_recording_path)
|
|
||||||
case 'save'
|
|
||||||
set -l save_file (app2unit -- zenity --file-selection --save --title='Save As')
|
|
||||||
test -n "$save_file" && mv $new_recording_path $save_file || warn 'No file selected'
|
|
||||||
case 'delete'
|
|
||||||
rm $new_recording_path
|
|
||||||
end
|
|
||||||
else
|
|
||||||
# Set region if flag given otherwise active monitor
|
|
||||||
if set -q _flag_r
|
|
||||||
# Use given region if value otherwise slurp
|
|
||||||
set region -g (test -n "$_flag_r" && echo $_flag_r || get-region)
|
|
||||||
else
|
|
||||||
set region -o (get-active-monitor)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Sound if enabled
|
|
||||||
set -q _flag_s && set -l audio --audio --audio-device (get-audio-source)
|
|
||||||
|
|
||||||
# No hardware accel
|
|
||||||
set -q _flag_n && set -l hwaccel --no-hw
|
|
||||||
|
|
||||||
wl-screenrec $region $audio $hwaccel --codec hevc -f $recording_path & disown
|
|
||||||
|
|
||||||
notify-send 'Recording started' 'Recording...' -i 'video-x-generic' -a 'caelestia-record' -p > $notif_id_path
|
|
||||||
end
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Utility script for rebuilding and running caelestia
|
||||||
|
|
||||||
|
cd $(dirname $0) || exit
|
||||||
|
|
||||||
|
sudo rm -r dist /usr/bin/caelestia /usr/lib/python3.*/site-packages/caelestia* 2> /dev/null
|
||||||
|
python -m build --wheel --no-isolation > /dev/null
|
||||||
|
sudo python -m installer --destdir=/ dist/*.whl > /dev/null
|
||||||
|
|
||||||
|
/usr/bin/caelestia "$@"
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import sys
|
|
||||||
from colorsys import hls_to_rgb, rgb_to_hls
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from materialyoucolor.blend import Blend
|
|
||||||
from materialyoucolor.dynamiccolor.material_dynamic_colors import (
|
|
||||||
DynamicScheme,
|
|
||||||
MaterialDynamicColors,
|
|
||||||
)
|
|
||||||
from materialyoucolor.hct import Hct
|
|
||||||
from materialyoucolor.scheme.scheme_content import SchemeContent
|
|
||||||
from materialyoucolor.scheme.scheme_expressive import SchemeExpressive
|
|
||||||
from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity
|
|
||||||
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad
|
|
||||||
from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome
|
|
||||||
from materialyoucolor.scheme.scheme_neutral import SchemeNeutral
|
|
||||||
from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow
|
|
||||||
from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot
|
|
||||||
from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant
|
|
||||||
|
|
||||||
light_colours = [
|
|
||||||
"dc8a78",
|
|
||||||
"dd7878",
|
|
||||||
"ea76cb",
|
|
||||||
"8839ef",
|
|
||||||
"d20f39",
|
|
||||||
"e64553",
|
|
||||||
"fe640b",
|
|
||||||
"df8e1d",
|
|
||||||
"40a02b",
|
|
||||||
"179299",
|
|
||||||
"04a5e5",
|
|
||||||
"209fb5",
|
|
||||||
"1e66f5",
|
|
||||||
"7287fd",
|
|
||||||
]
|
|
||||||
|
|
||||||
dark_colours = [
|
|
||||||
"f5e0dc",
|
|
||||||
"f2cdcd",
|
|
||||||
"f5c2e7",
|
|
||||||
"cba6f7",
|
|
||||||
"f38ba8",
|
|
||||||
"eba0ac",
|
|
||||||
"fab387",
|
|
||||||
"f9e2af",
|
|
||||||
"a6e3a1",
|
|
||||||
"94e2d5",
|
|
||||||
"89dceb",
|
|
||||||
"74c7ec",
|
|
||||||
"89b4fa",
|
|
||||||
"b4befe",
|
|
||||||
]
|
|
||||||
|
|
||||||
colour_names = [
|
|
||||||
"rosewater",
|
|
||||||
"flamingo",
|
|
||||||
"pink",
|
|
||||||
"mauve",
|
|
||||||
"red",
|
|
||||||
"maroon",
|
|
||||||
"peach",
|
|
||||||
"yellow",
|
|
||||||
"green",
|
|
||||||
"teal",
|
|
||||||
"sky",
|
|
||||||
"sapphire",
|
|
||||||
"blue",
|
|
||||||
"lavender",
|
|
||||||
"success",
|
|
||||||
"error",
|
|
||||||
]
|
|
||||||
|
|
||||||
HLS = tuple[float, float, float]
|
|
||||||
|
|
||||||
|
|
||||||
def hex_to_rgb(hex: str) -> tuple[float, float, float]:
|
|
||||||
"""Convert a hex string to an RGB tuple in the range [0, 1]."""
|
|
||||||
return tuple(int(hex[i : i + 2], 16) / 255 for i in (0, 2, 4))
|
|
||||||
|
|
||||||
|
|
||||||
def rgb_to_hex(rgb: tuple[float, float, float]) -> str:
|
|
||||||
"""Convert an RGB tuple in the range [0, 1] to a hex string."""
|
|
||||||
return "".join(f"{round(i * 255):02X}" for i in rgb)
|
|
||||||
|
|
||||||
|
|
||||||
def hex_to_hls(hex: str) -> tuple[float, float, float]:
|
|
||||||
return rgb_to_hls(*hex_to_rgb(hex))
|
|
||||||
|
|
||||||
|
|
||||||
def hls_to_hex(h: str, l: str, s: str) -> str:
|
|
||||||
return rgb_to_hex(hls_to_rgb(h, l, s))
|
|
||||||
|
|
||||||
|
|
||||||
def hex_to_argb(hex: str) -> int:
|
|
||||||
return int(f"0xFF{hex}", 16)
|
|
||||||
|
|
||||||
|
|
||||||
def argb_to_hls(argb: int) -> HLS:
|
|
||||||
return hex_to_hls(f"{argb:08X}"[2:])
|
|
||||||
|
|
||||||
|
|
||||||
def grayscale(hls: HLS, light: bool) -> HLS:
|
|
||||||
h, l, s = hls
|
|
||||||
return h, 0.5 - l / 2 if light else l / 2 + 0.5, 0
|
|
||||||
|
|
||||||
|
|
||||||
def mix(a: HLS, b: HLS, w: float) -> HLS:
|
|
||||||
r1, g1, b1 = hls_to_rgb(*a)
|
|
||||||
r2, g2, b2 = hls_to_rgb(*b)
|
|
||||||
return rgb_to_hls(
|
|
||||||
r1 * (1 - w) + r2 * w, g1 * (1 - w) + g2 * w, b1 * (1 - w) + b2 * w
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def harmonize(a: str, b: int) -> HLS:
|
|
||||||
return argb_to_hls(Blend.harmonize(hex_to_argb(a), b))
|
|
||||||
|
|
||||||
|
|
||||||
def darken(colour: HLS, amount: float) -> HLS:
|
|
||||||
h, l, s = colour
|
|
||||||
return h, max(0, l - amount), s
|
|
||||||
|
|
||||||
|
|
||||||
def distance(colour: HLS, base: str) -> float:
|
|
||||||
h1, l1, s1 = colour
|
|
||||||
h2, l2, s2 = hex_to_hls(base)
|
|
||||||
return abs(h1 - h2) * 0.4 + abs(l1 - l2) * 0.3 + abs(s1 - s2) * 0.3
|
|
||||||
|
|
||||||
|
|
||||||
def smart_sort(colours: list[HLS], base: list[str]) -> dict[str, HLS]:
|
|
||||||
sorted_colours = [None] * len(colours)
|
|
||||||
distances = {}
|
|
||||||
|
|
||||||
for colour in colours:
|
|
||||||
dist = [(i, distance(colour, b)) for i, b in enumerate(base)]
|
|
||||||
dist.sort(key=lambda x: x[1])
|
|
||||||
distances[colour] = dist
|
|
||||||
|
|
||||||
for colour in colours:
|
|
||||||
while len(distances[colour]) > 0:
|
|
||||||
i, dist = distances[colour][0]
|
|
||||||
|
|
||||||
if sorted_colours[i] is None:
|
|
||||||
sorted_colours[i] = colour, dist
|
|
||||||
break
|
|
||||||
elif sorted_colours[i][1] > dist:
|
|
||||||
old = sorted_colours[i][0]
|
|
||||||
sorted_colours[i] = colour, dist
|
|
||||||
colour = old
|
|
||||||
|
|
||||||
distances[colour].pop(0)
|
|
||||||
|
|
||||||
return {colour_names[i]: c[0] for i, c in enumerate(sorted_colours)}
|
|
||||||
|
|
||||||
|
|
||||||
def get_scheme(scheme: str) -> DynamicScheme:
|
|
||||||
if scheme == "content":
|
|
||||||
return SchemeContent
|
|
||||||
if scheme == "expressive":
|
|
||||||
return SchemeExpressive
|
|
||||||
if scheme == "fidelity":
|
|
||||||
return SchemeFidelity
|
|
||||||
if scheme == "fruitsalad":
|
|
||||||
return SchemeFruitSalad
|
|
||||||
if scheme == "monochrome":
|
|
||||||
return SchemeMonochrome
|
|
||||||
if scheme == "neutral":
|
|
||||||
return SchemeNeutral
|
|
||||||
if scheme == "rainbow":
|
|
||||||
return SchemeRainbow
|
|
||||||
if scheme == "tonalspot":
|
|
||||||
return SchemeTonalSpot
|
|
||||||
return SchemeVibrant
|
|
||||||
|
|
||||||
|
|
||||||
def get_alt(i: int) -> str:
|
|
||||||
names = ["default", "alt1", "alt2"]
|
|
||||||
return names[i]
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
light = sys.argv[1] == "light"
|
|
||||||
scheme = sys.argv[2]
|
|
||||||
primaries = sys.argv[3].split(" ")
|
|
||||||
colours_in = sys.argv[4].split(" ")
|
|
||||||
out_path = sys.argv[5]
|
|
||||||
|
|
||||||
base = light_colours if light else dark_colours
|
|
||||||
|
|
||||||
# Convert to HLS
|
|
||||||
base_colours = [hex_to_hls(c) for c in colours_in]
|
|
||||||
|
|
||||||
# Sort colours and turn into dict
|
|
||||||
base_colours = smart_sort(base_colours, base)
|
|
||||||
|
|
||||||
# Adjust colours
|
|
||||||
MatScheme = get_scheme(scheme)
|
|
||||||
for name, hls in base_colours.items():
|
|
||||||
if scheme == "monochrome":
|
|
||||||
base_colours[name] = grayscale(hls, light)
|
|
||||||
else:
|
|
||||||
argb = hex_to_argb(hls_to_hex(*hls))
|
|
||||||
mat_scheme = MatScheme(Hct.from_int(argb), not light, 0)
|
|
||||||
|
|
||||||
colour = MaterialDynamicColors.primary.get_hct(mat_scheme)
|
|
||||||
|
|
||||||
# Boost neutral scheme colours
|
|
||||||
if scheme == "neutral":
|
|
||||||
colour.chroma += 10
|
|
||||||
|
|
||||||
base_colours[name] = hex_to_hls(
|
|
||||||
"{:02X}{:02X}{:02X}".format(*colour.to_rgba()[:3])
|
|
||||||
)
|
|
||||||
|
|
||||||
# Layers and accents
|
|
||||||
for i, primary in enumerate(primaries):
|
|
||||||
material = {}
|
|
||||||
|
|
||||||
primary_argb = hex_to_argb(primary)
|
|
||||||
primary_scheme = MatScheme(Hct.from_int(primary_argb), not light, 0)
|
|
||||||
for colour in vars(MaterialDynamicColors).keys():
|
|
||||||
colour_name = getattr(MaterialDynamicColors, colour)
|
|
||||||
if hasattr(colour_name, "get_hct"):
|
|
||||||
rgb = colour_name.get_hct(primary_scheme).to_rgba()[:3]
|
|
||||||
material[colour] = hex_to_hls("{:02X}{:02X}{:02X}".format(*rgb))
|
|
||||||
|
|
||||||
# TODO: eventually migrate to material for layers
|
|
||||||
colours = {
|
|
||||||
**material,
|
|
||||||
"text": material["onBackground"],
|
|
||||||
"subtext1": material["onSurfaceVariant"],
|
|
||||||
"subtext0": material["outline"],
|
|
||||||
"overlay2": mix(material["surface"], material["outline"], 0.86),
|
|
||||||
"overlay1": mix(material["surface"], material["outline"], 0.71),
|
|
||||||
"overlay0": mix(material["surface"], material["outline"], 0.57),
|
|
||||||
"surface2": mix(material["surface"], material["outline"], 0.43),
|
|
||||||
"surface1": mix(material["surface"], material["outline"], 0.29),
|
|
||||||
"surface0": mix(material["surface"], material["outline"], 0.14),
|
|
||||||
"base": material["surface"],
|
|
||||||
"mantle": darken(material["surface"], 0.03),
|
|
||||||
"crust": darken(material["surface"], 0.05),
|
|
||||||
"success": harmonize(base[8], primary_argb),
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, hls in base_colours.items():
|
|
||||||
colours[name] = harmonize(hls_to_hex(*hls), primary_argb)
|
|
||||||
|
|
||||||
out_file = Path(f"{out_path}/{scheme}/{get_alt(i)}/{sys.argv[1]}.txt")
|
|
||||||
out_file.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
colour_arr = [
|
|
||||||
f"{name} {hls_to_hex(*colour)}" for name, colour in colours.items()
|
|
||||||
]
|
|
||||||
out_file.write_text("\n".join(colour_arr))
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
set -l src (dirname (status filename))
|
|
||||||
|
|
||||||
. $src/../util.fish
|
|
||||||
|
|
||||||
test -f "$argv[1]" && set -l img (realpath "$argv[1]") || set -l img $C_STATE/wallpaper/thumbnail.jpg
|
|
||||||
|
|
||||||
# Thumbnail image if not already thumbnail
|
|
||||||
if test $img != $C_STATE/wallpaper/thumbnail.jpg
|
|
||||||
set -l thumb_path $C_CACHE/thumbnails/(sha1sum $img | cut -d ' ' -f 1).jpg
|
|
||||||
if ! test -f $thumb_path
|
|
||||||
magick -define jpeg:size=256x256 $img -thumbnail 128x128\> $thumb_path
|
|
||||||
end
|
|
||||||
set img $thumb_path
|
|
||||||
end
|
|
||||||
|
|
||||||
set -l variants vibrant tonalspot expressive fidelity fruitsalad rainbow neutral content monochrome
|
|
||||||
contains -- "$argv[2]" $variants && set -l variant $argv[2] || set -l variant (cat $C_STATE/scheme/current-variant.txt 2> /dev/null)
|
|
||||||
contains -- "$variant" $variants || set -l variant tonalspot
|
|
||||||
|
|
||||||
set -l hash (sha1sum $img | cut -d ' ' -f 1)
|
|
||||||
|
|
||||||
# Cache scheme
|
|
||||||
if ! test -d $C_CACHE/schemes/$hash/$variant
|
|
||||||
set -l colours ($src/score.py $img)
|
|
||||||
$src/autoadjust.py dark $variant $colours $C_CACHE/schemes/$hash
|
|
||||||
$src/autoadjust.py light $variant $colours $C_CACHE/schemes/$hash
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get mode from image
|
|
||||||
set -l lightness (magick $img -format '%[fx:int(mean*100)]' info:)
|
|
||||||
test $lightness -ge 60 && set -l mode light || set -l mode dark
|
|
||||||
|
|
||||||
# Print scheme
|
|
||||||
cat $C_CACHE/schemes/$hash/$variant/default/$mode.txt
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
set -l src (dirname (status filename))
|
|
||||||
|
|
||||||
. $src/../util.fish
|
|
||||||
|
|
||||||
test -f "$argv[1]" && set -l img (realpath "$argv[1]") || set -l img $C_STATE/wallpaper/thumbnail.jpg
|
|
||||||
|
|
||||||
set -l variants vibrant tonalspot expressive fidelity fruitsalad rainbow neutral content monochrome
|
|
||||||
contains -- "$argv[2]" $variants && set -l variant $argv[2] || set -l variant (cat $C_STATE/scheme/current-variant.txt 2> /dev/null)
|
|
||||||
contains -- "$variant" $variants || set -l variant tonalspot
|
|
||||||
|
|
||||||
set -l hash (sha1sum $img | cut -d ' ' -f 1)
|
|
||||||
|
|
||||||
# Cache scheme
|
|
||||||
if ! test -d $C_CACHE/schemes/$hash/$variant
|
|
||||||
set -l colours ($src/score.py $img)
|
|
||||||
$src/autoadjust.py dark $variant $colours $C_CACHE/schemes/$hash
|
|
||||||
$src/autoadjust.py light $variant $colours $C_CACHE/schemes/$hash
|
|
||||||
end
|
|
||||||
|
|
||||||
# Copy scheme from cache
|
|
||||||
rm -rf $src/../data/schemes/dynamic
|
|
||||||
cp -r $C_CACHE/schemes/$hash/$variant $src/../data/schemes/dynamic
|
|
||||||
|
|
||||||
# Update if current
|
|
||||||
set -l variant (string match -gr 'dynamic-(.*)' (cat $C_STATE/scheme/current-name.txt 2> /dev/null))
|
|
||||||
if test -n "$variant"
|
|
||||||
# If variant doesn't exist, use default
|
|
||||||
test -d $src/../data/schemes/dynamic/$variant || set -l variant default
|
|
||||||
# Apply scheme
|
|
||||||
$src/main.fish dynamic $variant $MODE > /dev/null
|
|
||||||
end
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
# Usage:
|
|
||||||
# caelestia scheme <scheme> <flavour> [mode]
|
|
||||||
# caelestia scheme <scheme> [flavour]
|
|
||||||
# caelestia scheme [scheme]
|
|
||||||
|
|
||||||
function set-scheme -a path name mode
|
|
||||||
mkdir -p $C_STATE/scheme
|
|
||||||
|
|
||||||
# Update scheme colours
|
|
||||||
cp $path $C_STATE/scheme/current.txt
|
|
||||||
|
|
||||||
# Update scheme name
|
|
||||||
echo -n $name > $C_STATE/scheme/current-name.txt
|
|
||||||
|
|
||||||
# Update scheme mode
|
|
||||||
echo -n $mode > $C_STATE/scheme/current-mode.txt
|
|
||||||
|
|
||||||
log "Changed scheme to $name ($mode)"
|
|
||||||
end
|
|
||||||
|
|
||||||
set -l src (dirname (status filename))/..
|
|
||||||
set -l schemes $src/data/schemes
|
|
||||||
|
|
||||||
. $src/util.fish
|
|
||||||
|
|
||||||
set -l scheme $argv[1]
|
|
||||||
set -l flavour $argv[2]
|
|
||||||
set -l mode $argv[3]
|
|
||||||
|
|
||||||
set -l valid_schemes (basename -a $schemes/*)
|
|
||||||
|
|
||||||
test -z "$scheme" && set -l scheme (random choice $valid_schemes)
|
|
||||||
|
|
||||||
if contains -- "$scheme" $valid_schemes
|
|
||||||
set -l flavours (basename -a (find $schemes/$scheme/ -mindepth 1 -maxdepth 1 -type d) 2> /dev/null)
|
|
||||||
set -l modes (basename -s .txt (find $schemes/$scheme/ -mindepth 1 -maxdepth 1 -type f) 2> /dev/null)
|
|
||||||
|
|
||||||
if test -n "$modes"
|
|
||||||
# Scheme only has one flavour, so second arg is mode
|
|
||||||
set -l mode $flavour
|
|
||||||
if test -z "$mode"
|
|
||||||
# Try to use current mode if not provided and current mode exists for flavour, otherwise random mode
|
|
||||||
set mode (cat $C_STATE/scheme/current-mode.txt 2> /dev/null)
|
|
||||||
contains -- "$mode" $modes || set mode (random choice $modes)
|
|
||||||
end
|
|
||||||
|
|
||||||
if contains -- "$mode" $modes
|
|
||||||
# Provided valid mode
|
|
||||||
set-scheme $schemes/$scheme/$mode.txt $scheme $mode
|
|
||||||
else
|
|
||||||
error "Invalid mode for $scheme: $mode"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
# Scheme has multiple flavours, so second arg is flavour
|
|
||||||
test -z "$flavour" && set -l flavour (random choice $flavours)
|
|
||||||
|
|
||||||
if contains -- "$flavour" $flavours
|
|
||||||
# Provided valid flavour
|
|
||||||
set -l modes (basename -s .txt $schemes/$scheme/$flavour/*.txt)
|
|
||||||
if test -z "$mode"
|
|
||||||
# Try to use current mode if not provided and current mode exists for flavour, otherwise random mode
|
|
||||||
set mode (cat $C_STATE/scheme/current-mode.txt 2> /dev/null)
|
|
||||||
contains -- "$mode" $modes || set mode (random choice $modes)
|
|
||||||
end
|
|
||||||
|
|
||||||
if contains -- "$mode" $modes
|
|
||||||
# Provided valid mode
|
|
||||||
set-scheme $schemes/$scheme/$flavour/$mode.txt $scheme-$flavour $mode
|
|
||||||
else
|
|
||||||
error "Invalid mode for $scheme $flavour: $mode"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
# Invalid flavour
|
|
||||||
error "Invalid flavour for $scheme: $flavour"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
error "Invalid scheme: $scheme"
|
|
||||||
end
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
mkdir -p "$C_CACHE/screenshots"
|
|
||||||
set -l tmp_file "$C_CACHE/screenshots/$(date +'%Y%m%d%H%M%S')"
|
|
||||||
|
|
||||||
if test "$argv[1]" = 'region'
|
|
||||||
if test "$argv[2]" = 'freeze'
|
|
||||||
wayfreeze --hide-cursor & set PID $last_pid
|
|
||||||
sleep .1
|
|
||||||
end
|
|
||||||
|
|
||||||
set -l ws (hyprctl -j activeworkspace | jq -r '.id')
|
|
||||||
set -l region (hyprctl -j clients | jq -r --argjson activeWsId $ws '.[] | select(.workspace.id == $activeWsId) | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"' | slurp)
|
|
||||||
if test -n "$region"
|
|
||||||
grim -l 0 -g $region - | swappy -f - &
|
|
||||||
end
|
|
||||||
|
|
||||||
set -q PID && kill $PID
|
|
||||||
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
grim $argv $tmp_file; and wl-copy < $tmp_file; or exit 1
|
|
||||||
|
|
||||||
set -l action (notify-send -i 'image-x-generic-symbolic' -h "STRING:image-path:$tmp_file" \
|
|
||||||
-a 'caelestia-screenshot' --action='open=Open' --action='save=Save' \
|
|
||||||
'Screenshot taken' "Screenshot stored in $tmp_file and copied to clipboard")
|
|
||||||
switch $action
|
|
||||||
case 'open'
|
|
||||||
app2unit -- swappy -f $tmp_file & disown
|
|
||||||
case 'save'
|
|
||||||
set -l save_file (app2unit -- zenity --file-selection --save --title='Save As')
|
|
||||||
test -z $save_file && exit 0
|
|
||||||
if test -f $save_file
|
|
||||||
app2unit -- yad --image='abrt' --title='Warning!' --text-align='center' --buttons-layout='center' --borders=20 \
|
|
||||||
--text='<span size="x-large">Are you sure you want to overwrite this file?</span>' || exit 0
|
|
||||||
end
|
|
||||||
cp $tmp_file $save_file
|
|
||||||
end
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
from caelestia.parser import parse_args
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser, args = parse_args()
|
||||||
|
if "cls" in args:
|
||||||
|
args.cls(args).run()
|
||||||
|
else:
|
||||||
|
parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
primary_paletteKeyColor 5E8046
|
||||||
|
secondary_paletteKeyColor 6E7B62
|
||||||
|
tertiary_paletteKeyColor 517F7E
|
||||||
|
neutral_paletteKeyColor 75786F
|
||||||
|
neutral_variant_paletteKeyColor 74796D
|
||||||
|
background 11140E
|
||||||
|
onBackground E1E4D9
|
||||||
|
surface 11140E
|
||||||
|
surfaceDim 11140E
|
||||||
|
surfaceBright 373A33
|
||||||
|
surfaceContainerLowest 0C0F09
|
||||||
|
surfaceContainerLow 191D16
|
||||||
|
surfaceContainer 1D211A
|
||||||
|
surfaceContainerHigh 282B24
|
||||||
|
surfaceContainerHighest 33362F
|
||||||
|
onSurface E1E4D9
|
||||||
|
surfaceVariant 44483E
|
||||||
|
onSurfaceVariant C4C8BB
|
||||||
|
inverseSurface E1E4D9
|
||||||
|
inverseOnSurface 2E312A
|
||||||
|
outline 8E9286
|
||||||
|
outlineVariant 44483E
|
||||||
|
shadow 000000
|
||||||
|
scrim 000000
|
||||||
|
surfaceTint ACD28F
|
||||||
|
primary ACD28F
|
||||||
|
onPrimary 1A3705
|
||||||
|
primaryContainer 304F1A
|
||||||
|
onPrimaryContainer C7EEA9
|
||||||
|
inversePrimary 476730
|
||||||
|
secondary BDCBAF
|
||||||
|
onSecondary 283420
|
||||||
|
secondaryContainer 414D37
|
||||||
|
onSecondaryContainer D9E7CA
|
||||||
|
tertiary A0CFCE
|
||||||
|
onTertiary 003737
|
||||||
|
tertiaryContainer 6B9998
|
||||||
|
onTertiaryContainer 000000
|
||||||
|
error FFB4AB
|
||||||
|
onError 690005
|
||||||
|
errorContainer 93000A
|
||||||
|
onErrorContainer FFDAD6
|
||||||
|
primaryFixed C7EEA9
|
||||||
|
primaryFixedDim ACD28F
|
||||||
|
onPrimaryFixed 0A2000
|
||||||
|
onPrimaryFixedVariant 304F1A
|
||||||
|
secondaryFixed D9E7CA
|
||||||
|
secondaryFixedDim BDCBAF
|
||||||
|
onSecondaryFixed 141E0C
|
||||||
|
onSecondaryFixedVariant 3F4A35
|
||||||
|
tertiaryFixed BBECEA
|
||||||
|
tertiaryFixedDim A0CFCE
|
||||||
|
onTertiaryFixed 002020
|
||||||
|
onTertiaryFixedVariant 1E4E4D
|
||||||
|
text E1E4D9
|
||||||
|
subtext1 C4C8BB
|
||||||
|
subtext0 8E9286
|
||||||
|
overlay2 7D8075
|
||||||
|
overlay1 6A6D63
|
||||||
|
overlay0 585C52
|
||||||
|
surface2 474A42
|
||||||
|
surface1 353931
|
||||||
|
surface0 22261F
|
||||||
|
base 11140E
|
||||||
|
mantle 090B08
|
||||||
|
crust 040503
|
||||||
|
success ADE29A
|
||||||
|
rosewater ACD28F
|
||||||
|
flamingo 9BD4A0
|
||||||
|
pink 8AD0EF
|
||||||
|
mauve 91CEF5
|
||||||
|
red 86D6BE
|
||||||
|
maroon 81D4DA
|
||||||
|
peach 90D6AE
|
||||||
|
yellow A7D293
|
||||||
|
green A3D398
|
||||||
|
teal 82D5C7
|
||||||
|
sky 80D5D3
|
||||||
|
sapphire 86D2E8
|
||||||
|
blue 9CCBFA
|
||||||
|
lavender 81D3E2
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
primary_paletteKeyColor 5E8046
|
||||||
|
secondary_paletteKeyColor 6E7B62
|
||||||
|
tertiary_paletteKeyColor 517F7E
|
||||||
|
neutral_paletteKeyColor 75786F
|
||||||
|
neutral_variant_paletteKeyColor 74796D
|
||||||
|
background F9FAF0
|
||||||
|
onBackground 191D16
|
||||||
|
surface F9FAF0
|
||||||
|
surfaceDim D9DBD1
|
||||||
|
surfaceBright F9FAF0
|
||||||
|
surfaceContainerLowest FFFFFF
|
||||||
|
surfaceContainerLow F3F5EA
|
||||||
|
surfaceContainer EDEFE4
|
||||||
|
surfaceContainerHigh E7E9DF
|
||||||
|
surfaceContainerHighest E1E4D9
|
||||||
|
onSurface 191D16
|
||||||
|
surfaceVariant E0E4D6
|
||||||
|
onSurfaceVariant 44483E
|
||||||
|
inverseSurface 2E312A
|
||||||
|
inverseOnSurface F0F2E7
|
||||||
|
outline 71766B
|
||||||
|
outlineVariant C4C8BB
|
||||||
|
shadow 000000
|
||||||
|
scrim 000000
|
||||||
|
surfaceTint 476730
|
||||||
|
primary 476730
|
||||||
|
onPrimary FFFFFF
|
||||||
|
primaryContainer C7EEA9
|
||||||
|
onPrimaryContainer 304F1A
|
||||||
|
inversePrimary ACD28F
|
||||||
|
secondary 56624B
|
||||||
|
onSecondary FFFFFF
|
||||||
|
secondaryContainer D7E4C7
|
||||||
|
onSecondaryContainer 3F4A35
|
||||||
|
tertiary 4F7C7C
|
||||||
|
onTertiary FFFFFF
|
||||||
|
tertiaryContainer 4F7C7C
|
||||||
|
onTertiaryContainer FFFFFF
|
||||||
|
error BA1A1A
|
||||||
|
onError FFFFFF
|
||||||
|
errorContainer FFDAD6
|
||||||
|
onErrorContainer 93000A
|
||||||
|
primaryFixed C7EEA9
|
||||||
|
primaryFixedDim ACD28F
|
||||||
|
onPrimaryFixed 0A2000
|
||||||
|
onPrimaryFixedVariant 304F1A
|
||||||
|
secondaryFixed D9E7CA
|
||||||
|
secondaryFixedDim BDCBAF
|
||||||
|
onSecondaryFixed 141E0C
|
||||||
|
onSecondaryFixedVariant 3F4A35
|
||||||
|
tertiaryFixed BBECEA
|
||||||
|
tertiaryFixedDim A0CFCE
|
||||||
|
onTertiaryFixed 002020
|
||||||
|
onTertiaryFixedVariant 1E4E4D
|
||||||
|
text 191D16
|
||||||
|
subtext1 44483E
|
||||||
|
subtext0 71766B
|
||||||
|
overlay2 84887E
|
||||||
|
overlay1 989C92
|
||||||
|
overlay0 ABAFA4
|
||||||
|
surface2 BFC1B7
|
||||||
|
surface1 D2D4C9
|
||||||
|
surface0 E6E8DD
|
||||||
|
base F9FAF0
|
||||||
|
mantle F4F6E5
|
||||||
|
crust F1F4DD
|
||||||
|
success 4A9F23
|
||||||
|
rosewater 3D6837
|
||||||
|
flamingo 34693F
|
||||||
|
pink 006968
|
||||||
|
mauve 00696F
|
||||||
|
red 156A59
|
||||||
|
maroon 006876
|
||||||
|
peach 256B4A
|
||||||
|
yellow 426733
|
||||||
|
green 476730
|
||||||
|
teal 00677B
|
||||||
|
sky 2E628B
|
||||||
|
sapphire 206486
|
||||||
|
blue 0F6681
|
||||||
|
lavender 0D6A5F
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
primary_paletteKeyColor 5E76AB
|
||||||
|
secondary_paletteKeyColor 70778B
|
||||||
|
tertiary_paletteKeyColor 8B6D8C
|
||||||
|
neutral_paletteKeyColor 76777D
|
||||||
|
neutral_variant_paletteKeyColor 757780
|
||||||
|
background 121318
|
||||||
|
onBackground E2E2E9
|
||||||
|
surface 121318
|
||||||
|
surfaceDim 121318
|
||||||
|
surfaceBright 37393E
|
||||||
|
surfaceContainerLowest 0C0E13
|
||||||
|
surfaceContainerLow 1A1B20
|
||||||
|
surfaceContainer 1E1F25
|
||||||
|
surfaceContainerHigh 282A2F
|
||||||
|
surfaceContainerHighest 33353A
|
||||||
|
onSurface E2E2E9
|
||||||
|
surfaceVariant 44474F
|
||||||
|
onSurfaceVariant C5C6D0
|
||||||
|
inverseSurface E2E2E9
|
||||||
|
inverseOnSurface 2F3036
|
||||||
|
outline 8E9099
|
||||||
|
outlineVariant 44474F
|
||||||
|
shadow 000000
|
||||||
|
scrim 000000
|
||||||
|
surfaceTint AEC6FF
|
||||||
|
primary AEC6FF
|
||||||
|
onPrimary 122F60
|
||||||
|
primaryContainer 2C4678
|
||||||
|
onPrimaryContainer D8E2FF
|
||||||
|
inversePrimary 455E91
|
||||||
|
secondary BFC6DC
|
||||||
|
onSecondary 293041
|
||||||
|
secondaryContainer 3F4759
|
||||||
|
onSecondaryContainer DBE2F9
|
||||||
|
tertiary DFBBDE
|
||||||
|
onTertiary 402843
|
||||||
|
tertiaryContainer A786A7
|
||||||
|
onTertiaryContainer 000000
|
||||||
|
error FFB4AB
|
||||||
|
onError 690005
|
||||||
|
errorContainer 93000A
|
||||||
|
onErrorContainer FFDAD6
|
||||||
|
primaryFixed D8E2FF
|
||||||
|
primaryFixedDim AEC6FF
|
||||||
|
onPrimaryFixed 001A43
|
||||||
|
onPrimaryFixedVariant 2C4678
|
||||||
|
secondaryFixed DBE2F9
|
||||||
|
secondaryFixedDim BFC6DC
|
||||||
|
onSecondaryFixed 141B2C
|
||||||
|
onSecondaryFixedVariant 3F4759
|
||||||
|
tertiaryFixed FCD7FB
|
||||||
|
tertiaryFixedDim DFBBDE
|
||||||
|
onTertiaryFixed 2A132D
|
||||||
|
onTertiaryFixedVariant 583E5A
|
||||||
|
text E2E2E9
|
||||||
|
subtext1 C5C6D0
|
||||||
|
subtext0 8E9099
|
||||||
|
overlay2 7D7E87
|
||||||
|
overlay1 6A6C74
|
||||||
|
overlay0 595A62
|
||||||
|
surface2 47494F
|
||||||
|
surface1 36373D
|
||||||
|
surface0 23242A
|
||||||
|
base 121318
|
||||||
|
mantle 0B0C0F
|
||||||
|
crust 070709
|
||||||
|
success 93E5B6
|
||||||
|
rosewater 9BD4A1
|
||||||
|
flamingo 84D5C3
|
||||||
|
pink A1CAFE
|
||||||
|
mauve A5C8FF
|
||||||
|
red 80D3DE
|
||||||
|
maroon 8ECFF2
|
||||||
|
peach 80D5D0
|
||||||
|
yellow 93D5A9
|
||||||
|
green 8DD5B3
|
||||||
|
teal 84D2E5
|
||||||
|
sky 89D0ED
|
||||||
|
sapphire 9CCBFB
|
||||||
|
blue ACC6FF
|
||||||
|
lavender 94CDF7
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
primary_paletteKeyColor 5E76AB
|
||||||
|
secondary_paletteKeyColor 70778B
|
||||||
|
tertiary_paletteKeyColor 8B6D8C
|
||||||
|
neutral_paletteKeyColor 76777D
|
||||||
|
neutral_variant_paletteKeyColor 757780
|
||||||
|
background FAF9FF
|
||||||
|
onBackground 1A1B20
|
||||||
|
surface FAF9FF
|
||||||
|
surfaceDim DAD9E0
|
||||||
|
surfaceBright FAF9FF
|
||||||
|
surfaceContainerLowest FFFFFF
|
||||||
|
surfaceContainerLow F3F3FA
|
||||||
|
surfaceContainer EEEDF4
|
||||||
|
surfaceContainerHigh E8E7EF
|
||||||
|
surfaceContainerHighest E2E2E9
|
||||||
|
onSurface 1A1B20
|
||||||
|
surfaceVariant E1E2EC
|
||||||
|
onSurfaceVariant 44474F
|
||||||
|
inverseSurface 2F3036
|
||||||
|
inverseOnSurface F1F0F7
|
||||||
|
outline 72747D
|
||||||
|
outlineVariant C5C6D0
|
||||||
|
shadow 000000
|
||||||
|
scrim 000000
|
||||||
|
surfaceTint 455E91
|
||||||
|
primary 455E91
|
||||||
|
onPrimary FFFFFF
|
||||||
|
primaryContainer D8E2FF
|
||||||
|
onPrimaryContainer 2C4678
|
||||||
|
inversePrimary AEC6FF
|
||||||
|
secondary 575E71
|
||||||
|
onSecondary FFFFFF
|
||||||
|
secondaryContainer DBE2F9
|
||||||
|
onSecondaryContainer 3F4759
|
||||||
|
tertiary 896B8A
|
||||||
|
onTertiary FFFFFF
|
||||||
|
tertiaryContainer 896B8A
|
||||||
|
onTertiaryContainer FFFFFF
|
||||||
|
error BA1A1A
|
||||||
|
onError FFFFFF
|
||||||
|
errorContainer FFDAD6
|
||||||
|
onErrorContainer 93000A
|
||||||
|
primaryFixed D8E2FF
|
||||||
|
primaryFixedDim AEC6FF
|
||||||
|
onPrimaryFixed 001A43
|
||||||
|
onPrimaryFixedVariant 2C4678
|
||||||
|
secondaryFixed DBE2F9
|
||||||
|
secondaryFixedDim BFC6DC
|
||||||
|
onSecondaryFixed 141B2C
|
||||||
|
onSecondaryFixedVariant 3F4759
|
||||||
|
tertiaryFixed FCD7FB
|
||||||
|
tertiaryFixedDim DFBBDE
|
||||||
|
onTertiaryFixed 2A132D
|
||||||
|
onTertiaryFixedVariant 583E5A
|
||||||
|
text 1A1B20
|
||||||
|
subtext1 44474F
|
||||||
|
subtext0 72747D
|
||||||
|
overlay2 85878F
|
||||||
|
overlay1 999BA3
|
||||||
|
overlay0 ACADB5
|
||||||
|
surface2 C0C0C7
|
||||||
|
surface1 D3D2D9
|
||||||
|
surface0 E7E6ED
|
||||||
|
base FAF9FF
|
||||||
|
mantle EDEAFF
|
||||||
|
crust E5E0FF
|
||||||
|
success 00A25A
|
||||||
|
rosewater 1F6A4E
|
||||||
|
flamingo 056A5C
|
||||||
|
pink 15667E
|
||||||
|
mauve 1B6685
|
||||||
|
red 006972
|
||||||
|
maroon 266389
|
||||||
|
peach 006A67
|
||||||
|
yellow 2B6A46
|
||||||
|
green 35693F
|
||||||
|
teal 30628C
|
||||||
|
sky 435E91
|
||||||
|
sapphire 3D5F8F
|
||||||
|
blue 37608E
|
||||||
|
lavender 0A6777
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
primary_paletteKeyColor 2E8195
|
||||||
|
secondary_paletteKeyColor 647B82
|
||||||
|
tertiary_paletteKeyColor 707598
|
||||||
|
neutral_paletteKeyColor 72787A
|
||||||
|
neutral_variant_paletteKeyColor 70797C
|
||||||
|
background 0F1416
|
||||||
|
onBackground DEE3E6
|
||||||
|
surface 0F1416
|
||||||
|
surfaceDim 0F1416
|
||||||
|
surfaceBright 343A3C
|
||||||
|
surfaceContainerLowest 090F11
|
||||||
|
surfaceContainerLow 171C1E
|
||||||
|
surfaceContainer 1B2022
|
||||||
|
surfaceContainerHigh 252B2D
|
||||||
|
surfaceContainerHighest 303638
|
||||||
|
onSurface DEE3E6
|
||||||
|
surfaceVariant 3F484B
|
||||||
|
onSurfaceVariant BFC8CB
|
||||||
|
inverseSurface DEE3E6
|
||||||
|
inverseOnSurface 2C3133
|
||||||
|
outline 899295
|
||||||
|
outlineVariant 3F484B
|
||||||
|
shadow 000000
|
||||||
|
scrim 000000
|
||||||
|
surfaceTint 85D2E7
|
||||||
|
primary 85D2E7
|
||||||
|
onPrimary 003641
|
||||||
|
primaryContainer 004E5D
|
||||||
|
onPrimaryContainer AEECFF
|
||||||
|
inversePrimary 00687B
|
||||||
|
secondary B2CBD3
|
||||||
|
onSecondary 1D343A
|
||||||
|
secondaryContainer 364D53
|
||||||
|
onSecondaryContainer CEE7EF
|
||||||
|
tertiary BFC4EB
|
||||||
|
onTertiary 292E4D
|
||||||
|
tertiaryContainer 898FB3
|
||||||
|
onTertiaryContainer 000000
|
||||||
|
error FFB4AB
|
||||||
|
onError 690005
|
||||||
|
errorContainer 93000A
|
||||||
|
onErrorContainer FFDAD6
|
||||||
|
primaryFixed AEECFF
|
||||||
|
primaryFixedDim 85D2E7
|
||||||
|
onPrimaryFixed 001F26
|
||||||
|
onPrimaryFixedVariant 004E5D
|
||||||
|
secondaryFixed CEE7EF
|
||||||
|
secondaryFixedDim B2CBD3
|
||||||
|
onSecondaryFixed 061F25
|
||||||
|
onSecondaryFixedVariant 344A51
|
||||||
|
tertiaryFixed DEE1FF
|
||||||
|
tertiaryFixedDim BFC4EB
|
||||||
|
onTertiaryFixed 141937
|
||||||
|
onTertiaryFixedVariant 3F4565
|
||||||
|
text DEE3E6
|
||||||
|
subtext1 BFC8CB
|
||||||
|
subtext0 899295
|
||||||
|
overlay2 788083
|
||||||
|
overlay1 666D70
|
||||||
|
overlay0 555C5E
|
||||||
|
surface2 434A4D
|
||||||
|
surface1 32393B
|
||||||
|
surface0 202628
|
||||||
|
base 0F1416
|
||||||
|
mantle 090C0D
|
||||||
|
crust 050607
|
||||||
|
success 93E5B6
|
||||||
|
rosewater 9BD4A1
|
||||||
|
flamingo 84D5C3
|
||||||
|
pink 8CD0F1
|
||||||
|
mauve 91CEF5
|
||||||
|
red 80D4DC
|
||||||
|
maroon 85D2E7
|
||||||
|
peach 80D5D0
|
||||||
|
yellow 93D5A9
|
||||||
|
green 8DD5B3
|
||||||
|
teal 81D3E0
|
||||||
|
sky 83D2E4
|
||||||
|
sapphire 8AD1EE
|
||||||
|
blue 9CCBFA
|
||||||
|
lavender 86D1EB
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
primary_paletteKeyColor 2E8195
|
||||||
|
secondary_paletteKeyColor 647B82
|
||||||
|
tertiary_paletteKeyColor 707598
|
||||||
|
neutral_paletteKeyColor 72787A
|
||||||
|
neutral_variant_paletteKeyColor 70797C
|
||||||
|
background F5FAFC
|
||||||
|
onBackground 171C1E
|
||||||
|
surface F5FAFC
|
||||||
|
surfaceDim D5DBDD
|
||||||
|
surfaceBright F5FAFC
|
||||||
|
surfaceContainerLowest FFFFFF
|
||||||
|
surfaceContainerLow EFF4F7
|
||||||
|
surfaceContainer E9EFF1
|
||||||
|
surfaceContainerHigh E4E9EB
|
||||||
|
surfaceContainerHighest DEE3E6
|
||||||
|
onSurface 171C1E
|
||||||
|
surfaceVariant DBE4E7
|
||||||
|
onSurfaceVariant 3F484B
|
||||||
|
inverseSurface 2C3133
|
||||||
|
inverseOnSurface ECF2F4
|
||||||
|
outline 6D7679
|
||||||
|
outlineVariant BFC8CB
|
||||||
|
shadow 000000
|
||||||
|
scrim 000000
|
||||||
|
surfaceTint 00687B
|
||||||
|
primary 00687B
|
||||||
|
onPrimary FFFFFF
|
||||||
|
primaryContainer AEECFF
|
||||||
|
onPrimaryContainer 004E5D
|
||||||
|
inversePrimary 85D2E7
|
||||||
|
secondary 4B6269
|
||||||
|
onSecondary FFFFFF
|
||||||
|
secondaryContainer CEE7EF
|
||||||
|
onSecondaryContainer 344A51
|
||||||
|
tertiary 6D7395
|
||||||
|
onTertiary FFFFFF
|
||||||
|
tertiaryContainer 6D7395
|
||||||
|
onTertiaryContainer FFFFFF
|
||||||
|
error BA1A1A
|
||||||
|
onError FFFFFF
|
||||||
|
errorContainer FFDAD6
|
||||||
|
onErrorContainer 93000A
|
||||||
|
primaryFixed AEECFF
|
||||||
|
primaryFixedDim 85D2E7
|
||||||
|
onPrimaryFixed 001F26
|
||||||
|
onPrimaryFixedVariant 004E5D
|
||||||
|
secondaryFixed CEE7EF
|
||||||
|
secondaryFixedDim B2CBD3
|
||||||
|
onSecondaryFixed 061F25
|
||||||
|
onSecondaryFixedVariant 344A51
|
||||||
|
tertiaryFixed DEE1FF
|
||||||
|
tertiaryFixedDim BFC4EB
|
||||||
|
onTertiaryFixed 141937
|
||||||
|
onTertiaryFixedVariant 3F4565
|
||||||
|
text 171C1E
|
||||||
|
subtext1 3F484B
|
||||||
|
subtext0 6D7679
|
||||||
|
overlay2 80888B
|
||||||
|
overlay1 949C9F
|
||||||
|
overlay0 A7AFB1
|
||||||
|
surface2 BBC1C4
|
||||||
|
surface1 CED4D6
|
||||||
|
surface0 E2E8EA
|
||||||
|
base F5FAFC
|
||||||
|
mantle E9F4F8
|
||||||
|
crust E1F0F6
|
||||||
|
success 00A25A
|
||||||
|
rosewater 1F6A4E
|
||||||
|
flamingo 056A5C
|
||||||
|
pink 046877
|
||||||
|
mauve 00687B
|
||||||
|
red 006970
|
||||||
|
maroon 02677E
|
||||||
|
peach 006A67
|
||||||
|
yellow 2B6A46
|
||||||
|
green 35693F
|
||||||
|
teal 0D6680
|
||||||
|
sky 2E628B
|
||||||
|
sapphire 206486
|
||||||
|
blue 156583
|
||||||
|
lavender 036873
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# Main background, empty for terminal default, need to be empty if you want transparent background
|
||||||
|
theme[main_bg]={{ $surface }}
|
||||||
|
|
||||||
|
# Main text color
|
||||||
|
theme[main_fg]={{ $onSurface }}
|
||||||
|
|
||||||
|
# Title color for boxes
|
||||||
|
theme[title]={{ $onSurface }}
|
||||||
|
|
||||||
|
# Highlight color for keyboard shortcuts
|
||||||
|
theme[hi_fg]={{ $primary }}
|
||||||
|
|
||||||
|
# Background color of selected item in processes box
|
||||||
|
theme[selected_bg]={{ $surfaceContainer }}
|
||||||
|
|
||||||
|
# Foreground color of selected item in processes box
|
||||||
|
theme[selected_fg]={{ $primary }}
|
||||||
|
|
||||||
|
# Color of inactive/disabled text
|
||||||
|
theme[inactive_fg]={{ $outline }}
|
||||||
|
|
||||||
|
# Color of text appearing on top of graphs, i.e uptime and current network graph scaling
|
||||||
|
theme[graph_text]={{ $tertiary }}
|
||||||
|
|
||||||
|
# Background color of the percentage meters
|
||||||
|
theme[meter_bg]={{ $outline }}
|
||||||
|
|
||||||
|
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
|
||||||
|
theme[proc_misc]={{ $tertiary }}
|
||||||
|
|
||||||
|
# CPU, Memory, Network, Proc box outline colors
|
||||||
|
theme[cpu_box]={{ $mauve }}
|
||||||
|
theme[mem_box]={{ $green }}
|
||||||
|
theme[net_box]={{ $maroon }}
|
||||||
|
theme[proc_box]={{ $blue }}
|
||||||
|
|
||||||
|
# Box divider line and small boxes line color
|
||||||
|
theme[div_line]={{ $outlineVariant }}
|
||||||
|
|
||||||
|
# Temperature graph color (Green -> Yellow -> Red)
|
||||||
|
theme[temp_start]={{ $green }}
|
||||||
|
theme[temp_mid]={{ $yellow }}
|
||||||
|
theme[temp_end]={{ $red }}
|
||||||
|
|
||||||
|
# CPU graph colors (Teal -> Sapphire -> Lavender)
|
||||||
|
theme[cpu_start]={{ $teal }}
|
||||||
|
theme[cpu_mid]={{ $sapphire }}
|
||||||
|
theme[cpu_end]={{ $lavender }}
|
||||||
|
|
||||||
|
# Mem/Disk free meter (Mauve -> Lavender -> Blue)
|
||||||
|
theme[free_start]={{ $mauve }}
|
||||||
|
theme[free_mid]={{ $lavender }}
|
||||||
|
theme[free_end]={{ $blue }}
|
||||||
|
|
||||||
|
# Mem/Disk cached meter (Sapphire -> Blue -> Lavender)
|
||||||
|
theme[cached_start]={{ $sapphire }}
|
||||||
|
theme[cached_mid]={{ $blue }}
|
||||||
|
theme[cached_end]={{ $lavender }}
|
||||||
|
|
||||||
|
# Mem/Disk available meter (Peach -> Maroon -> Red)
|
||||||
|
theme[available_start]={{ $peach }}
|
||||||
|
theme[available_mid]={{ $maroon }}
|
||||||
|
theme[available_end]={{ $red }}
|
||||||
|
|
||||||
|
# Mem/Disk used meter (Green -> Teal -> Sky)
|
||||||
|
theme[used_start]={{ $green }}
|
||||||
|
theme[used_mid]={{ $teal }}
|
||||||
|
theme[used_end]={{ $sky }}
|
||||||
|
|
||||||
|
# Download graph colors (Peach -> Maroon -> Red)
|
||||||
|
theme[download_start]={{ $peach }}
|
||||||
|
theme[download_mid]={{ $maroon }}
|
||||||
|
theme[download_end]={{ $red }}
|
||||||
|
|
||||||
|
# Upload graph colors (Green -> Teal -> Sky)
|
||||||
|
theme[upload_start]={{ $green }}
|
||||||
|
theme[upload_mid]={{ $teal }}
|
||||||
|
theme[upload_end]={{ $sky }}
|
||||||
|
|
||||||
|
# Process box color gradient for threads, mem and cpu usage (Sapphire -> Lavender -> Mauve)
|
||||||
|
theme[process_start]={{ $sapphire }}
|
||||||
|
theme[process_mid]={{ $lavender }}
|
||||||
|
theme[process_end]={{ $mauve }}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
/**
|
||||||
|
* @name Midnight (Caelestia)
|
||||||
|
* @description A dark, rounded discord theme. Caelestia scheme colours.
|
||||||
|
* @author refact0r, esme, anubis
|
||||||
|
* @version 1.6.2
|
||||||
|
* @invite nz87hXyvcy
|
||||||
|
* @website https://github.com/refact0r/midnight-discord
|
||||||
|
* @authorId 508863359777505290
|
||||||
|
* @authorLink https://www.refact0r.dev
|
||||||
|
*/
|
||||||
|
|
||||||
|
@use "sass:color";
|
||||||
|
@use "colours" as c;
|
||||||
|
|
||||||
|
@import url("https://refact0r.github.io/midnight-discord/build/midnight.css");
|
||||||
|
|
||||||
|
body {
|
||||||
|
/* font, change to '' for default discord font */
|
||||||
|
--font: "figtree";
|
||||||
|
|
||||||
|
/* sizes */
|
||||||
|
--gap: 12px; /* spacing between panels */
|
||||||
|
--divider-thickness: 4px; /* thickness of unread messages divider and highlighted message borders */
|
||||||
|
--border-thickness: 1px; /* thickness of borders around main panels. DOES NOT AFFECT OTHER BORDERS */
|
||||||
|
|
||||||
|
/* animation/transition options */
|
||||||
|
--animations: on; /* turn off to disable all midnight animations/transitions */
|
||||||
|
--list-item-transition: 0.2s ease; /* transition for list items */
|
||||||
|
--dms-icon-svg-transition: 0.4s ease; /* transition for the dms icon */
|
||||||
|
|
||||||
|
/* top bar options */
|
||||||
|
--top-bar-height: var(
|
||||||
|
--gap
|
||||||
|
); /* height of the titlebar/top bar (discord default is 36px, 24px recommended if moving/hiding top bar buttons) */
|
||||||
|
--top-bar-button-position: hide; /* off: default position, hide: hide inbox/support buttons completely, serverlist: move inbox button to server list, titlebar: move inbox button to titlebar (will hide title) */
|
||||||
|
--top-bar-title-position: hide; /* off: default centered position, hide: hide title completely, left: left align title (like old discord) */
|
||||||
|
--subtle-top-bar-title: off; /* off: default, on: hide the icon and use subtle text color (like old discord) */
|
||||||
|
|
||||||
|
/* window controls */
|
||||||
|
--custom-window-controls: on; /* turn off to use discord default window controls */
|
||||||
|
--window-control-size: 14px; /* size of custom window controls */
|
||||||
|
|
||||||
|
/* dms button icon options */
|
||||||
|
--custom-dms-icon: custom; /* off: use default discord icon, hide: remove icon entirely, custom: use custom icon */
|
||||||
|
--dms-icon-svg-url: url("https://upload.wikimedia.org/wikipedia/commons/c/c4/Font_Awesome_5_solid_moon.svg"); /* icon svg url. MUST BE A SVG. */
|
||||||
|
--dms-icon-svg-size: 90%; /* size of the svg (css mask-size) */
|
||||||
|
--dms-icon-color-before: var(--icon-secondary); /* normal icon color */
|
||||||
|
--dms-icon-color-after: var(--white); /* icon color when button is hovered/selected */
|
||||||
|
|
||||||
|
/* dms button background options */
|
||||||
|
--custom-dms-background: off; /* off to disable, image to use a background image (must set url variable below), color to use a custom color/gradient */
|
||||||
|
--dms-background-image-url: url(""); /* url of the background image */
|
||||||
|
--dms-background-image-size: cover; /* size of the background image (css background-size) */
|
||||||
|
--dms-background-color: linear-gradient(
|
||||||
|
70deg,
|
||||||
|
var(--blue-2),
|
||||||
|
var(--purple-2),
|
||||||
|
var(--red-2)
|
||||||
|
); /* fixed color/gradient (css background) */
|
||||||
|
|
||||||
|
/* background image options */
|
||||||
|
--background-image: off; /* turn on to use a background image */
|
||||||
|
--background-image-url: url(""); /* url of the background image */
|
||||||
|
|
||||||
|
/* transparency/blur options */
|
||||||
|
/* NOTE: TO USE TRANSPARENCY/BLUR, YOU MUST HAVE TRANSPARENT BG COLORS. FOR EXAMPLE: --bg-4: hsla(220, 15%, 10%, 0.7); */
|
||||||
|
--transparency-tweaks: off; /* turn on to remove some elements for better transparency */
|
||||||
|
--remove-bg-layer: off; /* turn on to remove the base --bg-3 layer for use with window transparency (WILL OVERRIDE BACKGROUND IMAGE) */
|
||||||
|
--panel-blur: off; /* turn on to blur the background of panels */
|
||||||
|
--blur-amount: 12px; /* amount of blur */
|
||||||
|
--bg-floating: #{c.$surface}; /* you can set this to a more opaque color if floating panels look too transparent */
|
||||||
|
|
||||||
|
/* chatbar options */
|
||||||
|
--custom-chatbar: aligned; /* off: default chatbar, aligned: chatbar aligned with the user panel, separated: chatbar separated from chat */
|
||||||
|
--chatbar-height: 47px; /* height of the chatbar (52px by default, 47px recommended for aligned, 56px recommended for separated) */
|
||||||
|
--chatbar-padding: 8px; /* padding of the chatbar. only applies in aligned mode. */
|
||||||
|
|
||||||
|
/* other options */
|
||||||
|
--small-user-panel: off; /* turn on to make the user panel smaller like in old discord */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* color options */
|
||||||
|
:root {
|
||||||
|
--colors: on; /* turn off to use discord default colors */
|
||||||
|
|
||||||
|
/* text colors */
|
||||||
|
--text-0: #{c.$onPrimary}; /* text on colored elements */
|
||||||
|
--text-1: #{color.scale(c.$onSurface, $lightness: 10%)}; /* bright text on colored elements */
|
||||||
|
--text-2: #{color.scale(c.$onSurface, $lightness: 5%)}; /* headings and important text */
|
||||||
|
--text-3: #{c.$onSurface}; /* normal text */
|
||||||
|
--text-4: #{c.$outline}; /* icon buttons and channels */
|
||||||
|
--text-5: #{c.$outline}; /* muted channels/chats and timestamps */
|
||||||
|
|
||||||
|
/* background and dark colors */
|
||||||
|
--bg-1: #{c.$surfaceContainerHighest}; /* dark buttons when clicked */
|
||||||
|
--bg-2: #{c.$surfaceContainerHigh}; /* dark buttons */
|
||||||
|
--bg-3: #{c.$surface}; /* spacing, secondary elements */
|
||||||
|
--bg-4: #{c.$surfaceContainer}; /* main background color */
|
||||||
|
--hover: #{color.change(c.$onSurface, $alpha: 0.08)}; /* channels and buttons when hovered */
|
||||||
|
--active: #{color.change(c.$onSurface, $alpha: 0.1)}; /* channels and buttons when clicked or selected */
|
||||||
|
--active-2: #{color.change(c.$onSurface, $alpha: 0.2)}; /* extra state for transparent buttons */
|
||||||
|
--message-hover: #{color.change(c.$onSurface, $alpha: 0.08)}; /* messages when hovered */
|
||||||
|
|
||||||
|
/* accent colors */
|
||||||
|
--accent-1: var(--blue-1); /* links and other accent text */
|
||||||
|
--accent-2: var(--blue-2); /* small accent elements */
|
||||||
|
--accent-3: var(--blue-3); /* accent buttons */
|
||||||
|
--accent-4: var(--blue-4); /* accent buttons when hovered */
|
||||||
|
--accent-5: var(--blue-5); /* accent buttons when clicked */
|
||||||
|
--accent-new: #{c.$error}; /* stuff that's normally red like mute/deafen buttons */
|
||||||
|
--mention: linear-gradient(
|
||||||
|
to right,
|
||||||
|
color-mix(in hsl, var(--blue-2), transparent 90%) 40%,
|
||||||
|
transparent
|
||||||
|
); /* background of messages that mention you */
|
||||||
|
--mention-hover: linear-gradient(
|
||||||
|
to right,
|
||||||
|
color-mix(in hsl, var(--blue-2), transparent 95%) 40%,
|
||||||
|
transparent
|
||||||
|
); /* background of messages that mention you when hovered */
|
||||||
|
--reply: linear-gradient(
|
||||||
|
to right,
|
||||||
|
color-mix(in hsl, var(--text-3), transparent 90%) 40%,
|
||||||
|
transparent
|
||||||
|
); /* background of messages that reply to you */
|
||||||
|
--reply-hover: linear-gradient(
|
||||||
|
to right,
|
||||||
|
color-mix(in hsl, var(--text-3), transparent 95%) 40%,
|
||||||
|
transparent
|
||||||
|
); /* background of messages that reply to you when hovered */
|
||||||
|
|
||||||
|
/* status indicator colors */
|
||||||
|
--online: var(--green-2); /* change to #43a25a for default */
|
||||||
|
--dnd: var(--red-2); /* change to #d83a42 for default */
|
||||||
|
--idle: var(--yellow-2); /* change to #ca9654 for default */
|
||||||
|
--streaming: var(--purple-2); /* change to #593695 for default */
|
||||||
|
--offline: var(--text-4); /* change to #83838b for default offline color */
|
||||||
|
|
||||||
|
/* border colors */
|
||||||
|
--border-light: #{color.change(c.$outline, $alpha: 0)}; /* light border color */
|
||||||
|
--border: #{color.change(c.$outline, $alpha: 0)}; /* normal border color */
|
||||||
|
--button-border: #{color.change(c.$outline, $alpha: 0)}; /* neutral border color of buttons */
|
||||||
|
|
||||||
|
/* base colors */
|
||||||
|
--red-1: #{c.$error};
|
||||||
|
--red-2: #{color.scale(c.$error, $lightness: -5%)};
|
||||||
|
--red-3: #{color.scale(c.$error, $lightness: -10%)};
|
||||||
|
--red-4: #{color.scale(c.$error, $lightness: -15%)};
|
||||||
|
--red-5: #{color.scale(c.$error, $lightness: -20%)};
|
||||||
|
|
||||||
|
--green-1: #{c.$green};
|
||||||
|
--green-2: #{color.scale(c.$green, $lightness: -5%)};
|
||||||
|
--green-3: #{color.scale(c.$green, $lightness: -10%)};
|
||||||
|
--green-4: #{color.scale(c.$green, $lightness: -15%)};
|
||||||
|
--green-5: #{color.scale(c.$green, $lightness: -20%)};
|
||||||
|
|
||||||
|
--blue-1: #{c.$primary};
|
||||||
|
--blue-2: #{color.scale(c.$primary, $lightness: -5%)};
|
||||||
|
--blue-3: #{color.scale(c.$primary, $lightness: -10%)};
|
||||||
|
--blue-4: #{color.scale(c.$primary, $lightness: -15%)};
|
||||||
|
--blue-5: #{color.scale(c.$primary, $lightness: -20%)};
|
||||||
|
|
||||||
|
--yellow-1: #{c.$yellow};
|
||||||
|
--yellow-2: #{color.scale(c.$yellow, $lightness: -5%)};
|
||||||
|
--yellow-3: #{color.scale(c.$yellow, $lightness: -10%)};
|
||||||
|
--yellow-4: #{color.scale(c.$yellow, $lightness: -15%)};
|
||||||
|
--yellow-5: #{color.scale(c.$yellow, $lightness: -20%)};
|
||||||
|
|
||||||
|
--purple-1: #{c.$mauve};
|
||||||
|
--purple-2: #{color.scale(c.$mauve, $lightness: -5%)};
|
||||||
|
--purple-3: #{color.scale(c.$mauve, $lightness: -10%)};
|
||||||
|
--purple-4: #{color.scale(c.$mauve, $lightness: -15%)};
|
||||||
|
--purple-5: #{color.scale(c.$mauve, $lightness: -20%)};
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
font=JetBrains Mono NF:size=17
|
||||||
|
terminal=foot -e
|
||||||
|
prompt="> "
|
||||||
|
layer=overlay
|
||||||
|
lines=15
|
||||||
|
width=60
|
||||||
|
dpi-aware=no
|
||||||
|
inner-pad=10
|
||||||
|
horizontal-pad=40
|
||||||
|
vertical-pad=15
|
||||||
|
match-counter=yes
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
background=282c34dd
|
||||||
|
text=abb2bfdd
|
||||||
|
prompt=d19a66ff
|
||||||
|
placeholder=666e7cff
|
||||||
|
input=abb2bfff
|
||||||
|
match=be5046ff
|
||||||
|
selection=d19a6687
|
||||||
|
selection-text=abb2bfff
|
||||||
|
selection-match=be5046ff
|
||||||
|
counter=666e7cff
|
||||||
|
border=d19a6677
|
||||||
|
|
||||||
|
[border]
|
||||||
|
radius=10
|
||||||
|
width=2
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
background={{ $surface }}dd
|
||||||
|
text={{ $onSurface }}dd
|
||||||
|
prompt={{ $primary }}ff
|
||||||
|
placeholder={{ $outline }}ff
|
||||||
|
input={{ $onSurface }}ff
|
||||||
|
match={{ $tertiary }}ff
|
||||||
|
selection={{ $primary }}87
|
||||||
|
selection-text={{ $onSurface }}ff
|
||||||
|
selection-match={{ $tertiary }}ff
|
||||||
|
counter={{ $outline }}ff
|
||||||
|
border={{ $primary }}77
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
[caelestia]
|
||||||
|
text = {{ $onSurface }} ; Main text colour
|
||||||
|
subtext = {{ $onSurfaceVariant }} ; Subtext colour
|
||||||
|
main = {{ $surfaceContainer }} ; Panel backgrounds
|
||||||
|
highlight = {{ $primary }} ; Doesn't seem to do anything
|
||||||
|
misc = {{ $primary }} ; Doesn't seem to do anything
|
||||||
|
notification = {{ $outline }} ; Notifications probably
|
||||||
|
notification-error = {{ $error }} ; Error notifications probably
|
||||||
|
shadow = {{ $shadow }} ; Shadow for covers, context menu, also affects playlist/artist banners
|
||||||
|
card = {{ $surfaceContainerHigh }} ; Context menu and tooltips
|
||||||
|
player = {{ $secondaryContainer }} ; Background for top result in search
|
||||||
|
sidebar = {{ $surface }} ; Background
|
||||||
|
main-elevated = {{ $surfaceContainerHigh }} ; Higher layers than main, e.g. search bar
|
||||||
|
highlight-elevated = {{ $surfaceContainerHighest }} ; Home button and search bar accelerator
|
||||||
|
selected-row = {{ $onSurface }} ; Selections, hover, other coloured text and slider background
|
||||||
|
button = {{ $primary }} ; Slider and text buttons
|
||||||
|
button-active = {{ $primary }} ; Background buttons
|
||||||
|
button-disabled = {{ $outline }} ; Disabled buttons
|
||||||
|
tab-active = {{ $surfaceContainerHigh }} ; Profile fallbacks in search
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
[caelestia]
|
||||||
|
text = {{ $onSurface }} ; Main text colour
|
||||||
|
subtext = {{ $onSurfaceVariant }} ; Subtext colour
|
||||||
|
main = {{ $surface }} ; Panel backgrounds
|
||||||
|
highlight = {{ $primary }} ; Doesn't seem to do anything
|
||||||
|
misc = {{ $primary }} ; Doesn't seem to do anything
|
||||||
|
notification = {{ $outline }} ; Notifications probably
|
||||||
|
notification-error = {{ $error }} ; Error notifications probably
|
||||||
|
shadow = {{ $shadow }} ; Shadow for covers, context menu, also affects playlist/artist banners
|
||||||
|
card = {{ $surfaceContainer }} ; Context menu and tooltips
|
||||||
|
player = {{ $secondaryContainer }} ; Background for top result in search
|
||||||
|
sidebar = {{ $surfaceContainer }} ; Background
|
||||||
|
main-elevated = {{ $surfaceContainerHigh }} ; Higher layers than main, e.g. search bar
|
||||||
|
highlight-elevated = {{ $surfaceContainerHighest }} ; Home button and search bar accelerator
|
||||||
|
selected-row = {{ $onSurface }} ; Selections, hover, other coloured text and slider background
|
||||||
|
button = {{ $primary }} ; Slider and text buttons
|
||||||
|
button-active = {{ $primary }} ; Background buttons
|
||||||
|
button-disabled = {{ $outline }} ; Disabled buttons
|
||||||
|
tab-active = {{ $surfaceContainer }} ; Profile fallbacks in search
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import argparse
|
||||||
|
|
||||||
|
from caelestia.subcommands import clipboard, emoji, pip, record, scheme, screenshot, shell, toggle, wallpaper, wsaction
|
||||||
|
from caelestia.utils.paths import wallpapers_dir
|
||||||
|
from caelestia.utils.scheme import get_scheme_names, scheme_variants
|
||||||
|
from caelestia.utils.wallpaper import get_wallpaper
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> (argparse.ArgumentParser, argparse.Namespace):
|
||||||
|
parser = argparse.ArgumentParser(prog="caelestia", description="Main control script for the Caelestia dotfiles")
|
||||||
|
|
||||||
|
# Add subcommand parsers
|
||||||
|
command_parser = parser.add_subparsers(
|
||||||
|
title="subcommands", description="valid subcommands", metavar="COMMAND", help="the subcommand to run"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create parser for shell opts
|
||||||
|
shell_parser = command_parser.add_parser("shell", help="start or message the shell")
|
||||||
|
shell_parser.set_defaults(cls=shell.Command)
|
||||||
|
shell_parser.add_argument("message", nargs="*", help="a message to send to the shell")
|
||||||
|
shell_parser.add_argument("-s", "--show", action="store_true", help="print all shell IPC commands")
|
||||||
|
shell_parser.add_argument(
|
||||||
|
"-l",
|
||||||
|
"--log",
|
||||||
|
nargs="?",
|
||||||
|
const="quickshell.dbus.properties.warning=false;quickshell.dbus.dbusmenu.warning=false;quickshell.service.notifications.warning=false;quickshell.service.sni.host.warning=false",
|
||||||
|
metavar="RULES",
|
||||||
|
help="print the shell log",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create parser for toggle opts
|
||||||
|
toggle_parser = command_parser.add_parser("toggle", help="toggle a special workspace")
|
||||||
|
toggle_parser.set_defaults(cls=toggle.Command)
|
||||||
|
toggle_parser.add_argument(
|
||||||
|
"workspace", choices=["communication", "music", "sysmon", "specialws", "todo"], help="the workspace to toggle"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create parser for workspace-action opts
|
||||||
|
ws_action_parser = command_parser.add_parser(
|
||||||
|
"workspace-action", help="execute a Hyprland workspace dispatcher in the current group"
|
||||||
|
)
|
||||||
|
ws_action_parser.set_defaults(cls=wsaction.Command)
|
||||||
|
ws_action_parser.add_argument(
|
||||||
|
"-g", "--group", action="store_true", help="whether to execute the dispatcher on a group"
|
||||||
|
)
|
||||||
|
ws_action_parser.add_argument(
|
||||||
|
"dispatcher", choices=["workspace", "movetoworkspace"], help="the dispatcher to execute"
|
||||||
|
)
|
||||||
|
ws_action_parser.add_argument("workspace", type=int, help="the workspace to pass to the dispatcher")
|
||||||
|
|
||||||
|
# Create parser for scheme opts
|
||||||
|
scheme_parser = command_parser.add_parser("scheme", help="manage the colour scheme")
|
||||||
|
scheme_parser.set_defaults(cls=scheme.Command)
|
||||||
|
scheme_parser.add_argument("-r", "--random", action="store_true", help="switch to a random scheme")
|
||||||
|
scheme_parser.add_argument("-n", "--name", choices=get_scheme_names(), help="the name of the scheme to switch to")
|
||||||
|
scheme_parser.add_argument("-f", "--flavour", help="the flavour to switch to")
|
||||||
|
scheme_parser.add_argument("-m", "--mode", choices=["dark", "light"], help="the mode to switch to")
|
||||||
|
scheme_parser.add_argument("-v", "--variant", choices=scheme_variants, help="the variant to switch to")
|
||||||
|
|
||||||
|
# Create parser for screenshot opts
|
||||||
|
screenshot_parser = command_parser.add_parser("screenshot", help="take a screenshot")
|
||||||
|
screenshot_parser.set_defaults(cls=screenshot.Command)
|
||||||
|
screenshot_parser.add_argument("-r", "--region", nargs="?", const="slurp", help="take a screenshot of a region")
|
||||||
|
screenshot_parser.add_argument(
|
||||||
|
"-f", "--freeze", action="store_true", help="freeze the screen while selecting a region"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create parser for record opts
|
||||||
|
record_parser = command_parser.add_parser("record", help="start a screen recording")
|
||||||
|
record_parser.set_defaults(cls=record.Command)
|
||||||
|
record_parser.add_argument("-r", "--region", nargs="?", const="slurp", help="record a region")
|
||||||
|
record_parser.add_argument("-s", "--sound", action="store_true", help="record audio")
|
||||||
|
|
||||||
|
# Create parser for clipboard opts
|
||||||
|
clipboard_parser = command_parser.add_parser("clipboard", help="open clipboard history")
|
||||||
|
clipboard_parser.set_defaults(cls=clipboard.Command)
|
||||||
|
clipboard_parser.add_argument("-d", "--delete", action="store_true", help="delete from clipboard history")
|
||||||
|
|
||||||
|
# Create parser for emoji-picker opts
|
||||||
|
emoji_parser = command_parser.add_parser("emoji-picker", help="toggle the emoji picker")
|
||||||
|
emoji_parser.set_defaults(cls=emoji.Command)
|
||||||
|
|
||||||
|
# Create parser for wallpaper opts
|
||||||
|
wallpaper_parser = command_parser.add_parser("wallpaper", help="manage the wallpaper")
|
||||||
|
wallpaper_parser.set_defaults(cls=wallpaper.Command)
|
||||||
|
wallpaper_parser.add_argument(
|
||||||
|
"-p", "--print", nargs="?", const=get_wallpaper(), metavar="PATH", help="print the scheme for a wallpaper"
|
||||||
|
)
|
||||||
|
wallpaper_parser.add_argument(
|
||||||
|
"-r", "--random", nargs="?", const=wallpapers_dir, metavar="DIR", help="switch to a random wallpaper"
|
||||||
|
)
|
||||||
|
wallpaper_parser.add_argument("-f", "--file", help="the path to the wallpaper to switch to")
|
||||||
|
wallpaper_parser.add_argument("-n", "--no-filter", action="store_true", help="do not filter by size")
|
||||||
|
wallpaper_parser.add_argument(
|
||||||
|
"-t",
|
||||||
|
"--threshold",
|
||||||
|
default=0.8,
|
||||||
|
help="the minimum percentage of the largest monitor size the image must be greater than to be selected",
|
||||||
|
)
|
||||||
|
wallpaper_parser.add_argument(
|
||||||
|
"-N",
|
||||||
|
"--no-smart",
|
||||||
|
action="store_true",
|
||||||
|
help="do not automatically change the scheme mode based on wallpaper colour",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create parser for pip opts
|
||||||
|
pip_parser = command_parser.add_parser("pip", help="picture in picture utilities")
|
||||||
|
pip_parser.set_defaults(cls=pip.Command)
|
||||||
|
pip_parser.add_argument("-d", "--daemon", action="store_true", help="start the daemon")
|
||||||
|
|
||||||
|
return parser, parser.parse_args()
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import subprocess
|
||||||
|
from argparse import Namespace
|
||||||
|
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
args: Namespace
|
||||||
|
|
||||||
|
def __init__(self, args: Namespace) -> None:
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
clip = subprocess.check_output(["cliphist", "list"])
|
||||||
|
|
||||||
|
if self.args.delete:
|
||||||
|
args = ["--prompt=del > ", "--placeholder=Delete from clipboard"]
|
||||||
|
else:
|
||||||
|
args = ["--placeholder=Type to search clipboard"]
|
||||||
|
|
||||||
|
chosen = subprocess.check_output(["fuzzel", "--dmenu", *args], input=clip)
|
||||||
|
|
||||||
|
if self.args.delete:
|
||||||
|
subprocess.run(["cliphist", "delete"], input=chosen)
|
||||||
|
else:
|
||||||
|
decoded = subprocess.check_output(["cliphist", "decode"], input=chosen)
|
||||||
|
subprocess.run(["wl-copy"], input=decoded)
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import subprocess
|
||||||
|
from argparse import Namespace
|
||||||
|
|
||||||
|
from caelestia.utils.paths import cli_data_dir
|
||||||
|
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
args: Namespace
|
||||||
|
|
||||||
|
def __init__(self, args: Namespace) -> None:
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
emojis = (cli_data_dir / "emojis.txt").read_text()
|
||||||
|
chosen = subprocess.check_output(
|
||||||
|
["fuzzel", "--dmenu", "--placeholder=Type to search emojis"], input=emojis, text=True
|
||||||
|
)
|
||||||
|
subprocess.run(["wl-copy"], input=chosen.split()[0], text=True)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import re
|
||||||
|
import socket
|
||||||
|
from argparse import Namespace
|
||||||
|
|
||||||
|
from caelestia.utils import hypr
|
||||||
|
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
args: Namespace
|
||||||
|
|
||||||
|
def __init__(self, args: Namespace) -> None:
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
if self.args.daemon:
|
||||||
|
self.daemon()
|
||||||
|
else:
|
||||||
|
win = hypr.message("activewindow")
|
||||||
|
if win["floating"]:
|
||||||
|
self.handle_window(win["address"], win["workspace"]["name"])
|
||||||
|
|
||||||
|
def handle_window(self, address: str, ws: str) -> None:
|
||||||
|
mon_id = next(w for w in hypr.message("workspaces") if w["name"] == ws)["monitorID"]
|
||||||
|
mon = next(m for m in hypr.message("monitors") if m["id"] == mon_id)
|
||||||
|
width, height = next(c for c in hypr.message("clients") if c["address"] == address)["size"]
|
||||||
|
|
||||||
|
scale_factor = mon["height"] / 4 / height
|
||||||
|
scaled_win_size = f"{int(width * scale_factor)} {int(height * scale_factor)}"
|
||||||
|
off = min(mon["width"], mon["height"]) * 0.03
|
||||||
|
move_to = f"{int(mon['width'] - off - width * scale_factor)} {int(mon['height'] - off - height * scale_factor)}"
|
||||||
|
|
||||||
|
hypr.dispatch("resizewindowpixel", "exact", f"{scaled_win_size},address:{address}")
|
||||||
|
hypr.dispatch("movewindowpixel", "exact", f"{move_to},address:{address}")
|
||||||
|
|
||||||
|
def daemon(self) -> None:
|
||||||
|
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
|
||||||
|
sock.connect(hypr.socket2_path)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
data = sock.recv(4096).decode()
|
||||||
|
if data.startswith("openwindow>>"):
|
||||||
|
address, ws, cls, title = data[12:].split(",")
|
||||||
|
if re.match(r"^[Pp]icture(-| )in(-| )[Pp]icture$", title):
|
||||||
|
self.handle_window(f"0x{address}", ws)
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
from argparse import Namespace
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from caelestia.utils.paths import recording_notif_path, recording_path, recordings_dir
|
||||||
|
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
args: Namespace
|
||||||
|
|
||||||
|
def __init__(self, args: Namespace) -> None:
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
proc = subprocess.run(["pidof", "wl-screenrec"])
|
||||||
|
if proc.returncode == 0:
|
||||||
|
self.stop()
|
||||||
|
else:
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
def start(self) -> None:
|
||||||
|
args = []
|
||||||
|
|
||||||
|
if self.args.region:
|
||||||
|
if self.args.region == "slurp":
|
||||||
|
region = subprocess.check_output(["slurp"], text=True)
|
||||||
|
else:
|
||||||
|
region = self.args.region
|
||||||
|
args += ["-g", region.strip()]
|
||||||
|
|
||||||
|
if self.args.sound:
|
||||||
|
sources = subprocess.check_output(["pactl", "list", "short", "sources"], text=True).splitlines()
|
||||||
|
for source in sources:
|
||||||
|
if "RUNNING" in source:
|
||||||
|
args += ["--audio", "--audio-device", source.split()[1]]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise ValueError("No audio source found")
|
||||||
|
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
["wl-screenrec", *args, "--codec", "hevc", "-f", recording_path],
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
start_new_session=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Send notif if proc hasn't ended after a small delay
|
||||||
|
time.sleep(0.1)
|
||||||
|
if proc.poll() is None:
|
||||||
|
notif = subprocess.check_output(
|
||||||
|
["notify-send", "-p", "-a", "caelestia-cli", "Recording started", "Recording..."], text=True
|
||||||
|
).strip()
|
||||||
|
recording_notif_path.write_text(notif)
|
||||||
|
else:
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
"notify-send",
|
||||||
|
"-a",
|
||||||
|
"caelestia-cli",
|
||||||
|
"Recording failed",
|
||||||
|
f"Recording failed to start: {proc.communicate()[1]}",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def stop(self) -> None:
|
||||||
|
subprocess.run(["pkill", "wl-screenrec"])
|
||||||
|
|
||||||
|
# Move to recordings folder
|
||||||
|
new_path = recordings_dir / f"recording_{datetime.now().strftime('%Y%m%d_%H-%M-%S')}.mp4"
|
||||||
|
recording_path.rename(new_path)
|
||||||
|
|
||||||
|
# Close start notification
|
||||||
|
try:
|
||||||
|
notif = recording_notif_path.read_text()
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
"gdbus",
|
||||||
|
"call",
|
||||||
|
"--session",
|
||||||
|
"--dest=org.freedesktop.Notifications",
|
||||||
|
"--object-path=/org/freedesktop/Notifications",
|
||||||
|
"--method=org.freedesktop.Notifications.CloseNotification",
|
||||||
|
notif,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
action = subprocess.check_output(
|
||||||
|
[
|
||||||
|
"notify-send",
|
||||||
|
"-a",
|
||||||
|
"caelestia-cli",
|
||||||
|
"--action=watch=Watch",
|
||||||
|
"--action=open=Open",
|
||||||
|
"--action=delete=Delete",
|
||||||
|
"Recording stopped",
|
||||||
|
f"Recording saved in {new_path}",
|
||||||
|
],
|
||||||
|
text=True,
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
if action == "watch":
|
||||||
|
subprocess.Popen(["app2unit", "-O", new_path], start_new_session=True)
|
||||||
|
elif action == "open":
|
||||||
|
p = subprocess.run(
|
||||||
|
[
|
||||||
|
"dbus-send",
|
||||||
|
"--session",
|
||||||
|
"--dest=org.freedesktop.FileManager1",
|
||||||
|
"--type=method_call",
|
||||||
|
"/org/freedesktop/FileManager1",
|
||||||
|
"org.freedesktop.FileManager1.ShowItems",
|
||||||
|
f"array:string:file://{new_path}",
|
||||||
|
"string:",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if p.returncode != 0:
|
||||||
|
subprocess.Popen(["app2unit", "-O", new_path.parent], start_new_session=True)
|
||||||
|
elif action == "delete":
|
||||||
|
new_path.unlink()
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
from argparse import Namespace
|
||||||
|
|
||||||
|
from caelestia.utils.scheme import get_scheme
|
||||||
|
from caelestia.utils.theme import apply_colours
|
||||||
|
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
args: Namespace
|
||||||
|
|
||||||
|
def __init__(self, args: Namespace) -> None:
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
scheme = get_scheme()
|
||||||
|
|
||||||
|
if self.args.random:
|
||||||
|
scheme.set_random()
|
||||||
|
apply_colours(scheme.colours, scheme.mode)
|
||||||
|
elif self.args.name or self.args.flavour or self.args.mode or self.args.variant:
|
||||||
|
if self.args.name:
|
||||||
|
scheme.name = self.args.name
|
||||||
|
if self.args.flavour:
|
||||||
|
scheme.flavour = self.args.flavour
|
||||||
|
if self.args.mode:
|
||||||
|
scheme.mode = self.args.mode
|
||||||
|
if self.args.variant:
|
||||||
|
scheme.variant = self.args.variant
|
||||||
|
apply_colours(scheme.colours, scheme.mode)
|
||||||
|
else:
|
||||||
|
print(scheme)
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import subprocess
|
||||||
|
from argparse import Namespace
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from caelestia.utils import hypr
|
||||||
|
from caelestia.utils.paths import screenshots_cache_dir, screenshots_dir
|
||||||
|
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
args: Namespace
|
||||||
|
|
||||||
|
def __init__(self, args: Namespace) -> None:
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
if self.args.region:
|
||||||
|
self.region()
|
||||||
|
else:
|
||||||
|
self.fullscreen()
|
||||||
|
|
||||||
|
def region(self) -> None:
|
||||||
|
freeze_proc = None
|
||||||
|
|
||||||
|
if self.args.freeze:
|
||||||
|
freeze_proc = subprocess.Popen(["wayfreeze", "--hide-cursor"])
|
||||||
|
|
||||||
|
if self.args.region == "slurp":
|
||||||
|
ws = hypr.message("activeworkspace")["id"]
|
||||||
|
geoms = [
|
||||||
|
f"{','.join(map(str, c['at']))} {'x'.join(map(str, c['size']))}"
|
||||||
|
for c in hypr.message("clients")
|
||||||
|
if c["workspace"]["id"] == ws
|
||||||
|
]
|
||||||
|
region = subprocess.check_output(["slurp"], input="\n".join(geoms), text=True)
|
||||||
|
else:
|
||||||
|
region = self.args.region
|
||||||
|
|
||||||
|
sc_data = subprocess.check_output(["grim", "-l", "0", "-g", region.strip(), "-"])
|
||||||
|
swappy = subprocess.Popen(["swappy", "-f", "-"], stdin=subprocess.PIPE, start_new_session=True)
|
||||||
|
swappy.stdin.write(sc_data)
|
||||||
|
swappy.stdin.close()
|
||||||
|
|
||||||
|
if freeze_proc:
|
||||||
|
freeze_proc.kill()
|
||||||
|
|
||||||
|
def fullscreen(self) -> None:
|
||||||
|
sc_data = subprocess.check_output(["grim", "-"])
|
||||||
|
|
||||||
|
subprocess.run(["wl-copy"], input=sc_data)
|
||||||
|
|
||||||
|
dest = screenshots_cache_dir / datetime.now().strftime("%Y%m%d%H%M%S")
|
||||||
|
screenshots_cache_dir.mkdir(exist_ok=True, parents=True)
|
||||||
|
dest.write_bytes(sc_data)
|
||||||
|
|
||||||
|
action = subprocess.check_output(
|
||||||
|
[
|
||||||
|
"notify-send",
|
||||||
|
"-i",
|
||||||
|
"image-x-generic-symbolic",
|
||||||
|
"-h",
|
||||||
|
f"STRING:image-path:{dest}",
|
||||||
|
"-a",
|
||||||
|
"caelestia-cli",
|
||||||
|
"--action=open=Open",
|
||||||
|
"--action=save=Save",
|
||||||
|
"Screenshot taken",
|
||||||
|
f"Screenshot stored in {dest} and copied to clipboard",
|
||||||
|
],
|
||||||
|
text=True,
|
||||||
|
).strip()
|
||||||
|
|
||||||
|
if action == "open":
|
||||||
|
subprocess.Popen(["swappy", "-f", dest], start_new_session=True)
|
||||||
|
elif action == "save":
|
||||||
|
new_dest = (screenshots_dir / dest.name).with_suffix(".png")
|
||||||
|
new_dest.parent.mkdir(exist_ok=True, parents=True)
|
||||||
|
dest.rename(new_dest)
|
||||||
|
subprocess.run(["notify-send", "Screenshot saved", f"Saved to {new_dest}"])
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import subprocess
|
||||||
|
from argparse import Namespace
|
||||||
|
|
||||||
|
from caelestia.utils import paths
|
||||||
|
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
args: Namespace
|
||||||
|
|
||||||
|
def __init__(self, args: Namespace) -> None:
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
if self.args.show:
|
||||||
|
# Print the ipc
|
||||||
|
self.print_ipc()
|
||||||
|
elif self.args.log:
|
||||||
|
# Print the log
|
||||||
|
self.print_log()
|
||||||
|
elif self.args.message:
|
||||||
|
# Send a message
|
||||||
|
self.message(*self.args.message)
|
||||||
|
else:
|
||||||
|
# Start the shell
|
||||||
|
self.shell()
|
||||||
|
|
||||||
|
def shell(self, *args: list[str]) -> str:
|
||||||
|
return subprocess.check_output(["qs", "-p", paths.c_data_dir / "shell", *args], text=True)
|
||||||
|
|
||||||
|
def print_ipc(self) -> None:
|
||||||
|
print(self.shell("ipc", "show"), end="")
|
||||||
|
|
||||||
|
def print_log(self) -> None:
|
||||||
|
log = self.shell("log")
|
||||||
|
# FIXME: remove when logging rules are added/warning is removed
|
||||||
|
for line in log.splitlines():
|
||||||
|
if "QProcess: Destroyed while process" not in line:
|
||||||
|
print(line)
|
||||||
|
|
||||||
|
def message(self, *args: list[str]) -> None:
|
||||||
|
print(self.shell("ipc", "call", *args), end="")
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import subprocess
|
||||||
|
from argparse import Namespace
|
||||||
|
|
||||||
|
from caelestia.utils import hypr
|
||||||
|
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
args: Namespace
|
||||||
|
clients: list[dict[str, any]] = None
|
||||||
|
|
||||||
|
def __init__(self, args: Namespace) -> None:
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
getattr(self, self.args.workspace)()
|
||||||
|
|
||||||
|
def get_clients(self) -> list[dict[str, any]]:
|
||||||
|
if self.clients is None:
|
||||||
|
self.clients = hypr.message("clients")
|
||||||
|
|
||||||
|
return self.clients
|
||||||
|
|
||||||
|
def move_client(self, selector: callable, workspace: str) -> None:
|
||||||
|
for client in self.get_clients():
|
||||||
|
if selector(client):
|
||||||
|
hypr.dispatch("movetoworkspacesilent", f"special:{workspace},address:{client['address']}")
|
||||||
|
|
||||||
|
def spawn_client(self, selector: callable, spawn: list[str]) -> bool:
|
||||||
|
exists = any(selector(client) for client in self.get_clients())
|
||||||
|
|
||||||
|
if not exists:
|
||||||
|
subprocess.Popen(["app2unit", "--", *spawn], start_new_session=True)
|
||||||
|
|
||||||
|
return not exists
|
||||||
|
|
||||||
|
def spawn_or_move(self, selector: callable, spawn: list[str], workspace: str) -> None:
|
||||||
|
if not self.spawn_client(selector, spawn):
|
||||||
|
self.move_client(selector, workspace)
|
||||||
|
|
||||||
|
def communication(self) -> None:
|
||||||
|
self.spawn_or_move(lambda c: c["class"] == "discord", ["discord"], "communication")
|
||||||
|
self.move_client(lambda c: c["class"] == "whatsapp", "communication")
|
||||||
|
hypr.dispatch("togglespecialworkspace", "communication")
|
||||||
|
|
||||||
|
def music(self) -> None:
|
||||||
|
self.spawn_or_move(
|
||||||
|
lambda c: c["class"] == "Spotify" or c["initialTitle"] == "Spotify" or c["initialTitle"] == "Spotify Free",
|
||||||
|
["spicetify", "watch", "-s"],
|
||||||
|
"music",
|
||||||
|
)
|
||||||
|
self.move_client(lambda c: c["class"] == "feishin", "music")
|
||||||
|
hypr.dispatch("togglespecialworkspace", "music")
|
||||||
|
|
||||||
|
def sysmon(self) -> None:
|
||||||
|
self.spawn_client(
|
||||||
|
lambda c: c["class"] == "btop" and c["title"] == "btop" and c["workspace"]["name"] == "special:sysmon",
|
||||||
|
["foot", "-a", "btop", "-T", "btop", "--", "btop"],
|
||||||
|
)
|
||||||
|
hypr.dispatch("togglespecialworkspace", "sysmon")
|
||||||
|
|
||||||
|
def todo(self) -> None:
|
||||||
|
self.spawn_or_move(lambda c: c["class"] == "Todoist", ["todoist"], "todo")
|
||||||
|
hypr.dispatch("togglespecialworkspace", "todo")
|
||||||
|
|
||||||
|
def specialws(self) -> None:
|
||||||
|
workspaces = hypr.message("workspaces")
|
||||||
|
on_special_ws = any(ws["name"] == "special:special" for ws in workspaces)
|
||||||
|
toggle_ws = "special"
|
||||||
|
|
||||||
|
if not on_special_ws:
|
||||||
|
active_ws = hypr.message("activewindow")["workspace"]["name"]
|
||||||
|
if active_ws.startswith("special:"):
|
||||||
|
toggle_ws = active_ws[8:]
|
||||||
|
|
||||||
|
hypr.dispatch("togglespecialworkspace", toggle_ws)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import json
|
||||||
|
from argparse import Namespace
|
||||||
|
|
||||||
|
from caelestia.utils.wallpaper import get_colours_for_wall, get_wallpaper, set_random, set_wallpaper
|
||||||
|
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
args: Namespace
|
||||||
|
|
||||||
|
def __init__(self, args: Namespace) -> None:
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
if self.args.print:
|
||||||
|
print(json.dumps(get_colours_for_wall(self.args.print, self.args.no_smart)))
|
||||||
|
elif self.args.file:
|
||||||
|
set_wallpaper(self.args.file, self.args.no_smart)
|
||||||
|
elif self.args.random:
|
||||||
|
set_random(self.args)
|
||||||
|
else:
|
||||||
|
print(get_wallpaper() or "No wallpaper set")
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
from argparse import Namespace
|
||||||
|
|
||||||
|
from caelestia.utils import hypr
|
||||||
|
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
args: Namespace
|
||||||
|
|
||||||
|
def __init__(self, args: Namespace) -> None:
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
active_ws = hypr.message("activeworkspace")["id"]
|
||||||
|
|
||||||
|
if self.args.group:
|
||||||
|
hypr.dispatch(self.args.dispatcher, (self.args.workspace - 1) * 10 + active_ws % 10)
|
||||||
|
else:
|
||||||
|
hypr.dispatch(self.args.dispatcher, int((active_ws - 1) / 10) * 10 + self.args.workspace)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import json as j
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
|
||||||
|
socket_base = f"{os.getenv('XDG_RUNTIME_DIR')}/hypr/{os.getenv('HYPRLAND_INSTANCE_SIGNATURE')}"
|
||||||
|
socket_path = f"{socket_base}/.socket.sock"
|
||||||
|
socket2_path = f"{socket_base}/.socket2.sock"
|
||||||
|
|
||||||
|
|
||||||
|
def message(msg: str, json: bool = True) -> str | dict[str, any]:
|
||||||
|
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
|
||||||
|
sock.connect(socket_path)
|
||||||
|
|
||||||
|
if json:
|
||||||
|
msg = f"j/{msg}"
|
||||||
|
sock.send(msg.encode())
|
||||||
|
|
||||||
|
resp = sock.recv(8192).decode()
|
||||||
|
while True:
|
||||||
|
new_resp = sock.recv(8192)
|
||||||
|
if not new_resp:
|
||||||
|
break
|
||||||
|
resp += new_resp.decode()
|
||||||
|
|
||||||
|
return j.loads(resp) if json else resp
|
||||||
|
|
||||||
|
|
||||||
|
def dispatch(dispatcher: str, *args: list[any]) -> bool:
|
||||||
|
return message(f"dispatch {dispatcher} {' '.join(map(str, args))}".rstrip(), json=False) == "ok"
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from materialyoucolor.hct import Hct
|
||||||
|
|
||||||
|
from caelestia.utils.material.generator import gen_scheme
|
||||||
|
from caelestia.utils.material.score import score
|
||||||
|
from caelestia.utils.paths import compute_hash, scheme_cache_dir, wallpaper_thumbnail_path
|
||||||
|
|
||||||
|
|
||||||
|
def get_score_for_image(image: str, cache_base: Path) -> tuple[list[Hct], list[Hct]]:
|
||||||
|
cache = cache_base / "score.json"
|
||||||
|
|
||||||
|
try:
|
||||||
|
with cache.open("r") as f:
|
||||||
|
return [[Hct.from_int(c) for c in li] for li in json.load(f)]
|
||||||
|
except (IOError, json.JSONDecodeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
s = score(image)
|
||||||
|
|
||||||
|
cache.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with cache.open("w") as f:
|
||||||
|
json.dump([[c.to_int() for c in li] for li in s], f)
|
||||||
|
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def get_colours_for_image(image: str = str(wallpaper_thumbnail_path), scheme=None) -> dict[str, str]:
|
||||||
|
if scheme is None:
|
||||||
|
from caelestia.utils.scheme import get_scheme
|
||||||
|
|
||||||
|
scheme = get_scheme()
|
||||||
|
|
||||||
|
cache_base = scheme_cache_dir / compute_hash(image)
|
||||||
|
cache = (cache_base / scheme.variant / scheme.flavour / scheme.mode).with_suffix(".json")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with cache.open("r") as f:
|
||||||
|
return json.load(f)
|
||||||
|
except (IOError, json.JSONDecodeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
primaries, colours = get_score_for_image(image, cache_base)
|
||||||
|
i = ["default", "alt1", "alt2"].index(scheme.flavour)
|
||||||
|
scheme = gen_scheme(scheme, primaries[i], colours)
|
||||||
|
|
||||||
|
cache.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with cache.open("w") as f:
|
||||||
|
json.dump(scheme, f)
|
||||||
|
|
||||||
|
return scheme
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
from materialyoucolor.blend import Blend
|
||||||
|
from materialyoucolor.dynamiccolor.material_dynamic_colors import (
|
||||||
|
DynamicScheme,
|
||||||
|
MaterialDynamicColors,
|
||||||
|
)
|
||||||
|
from materialyoucolor.hct import Hct
|
||||||
|
from materialyoucolor.hct.cam16 import Cam16
|
||||||
|
from materialyoucolor.scheme.scheme_content import SchemeContent
|
||||||
|
from materialyoucolor.scheme.scheme_expressive import SchemeExpressive
|
||||||
|
from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity
|
||||||
|
from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad
|
||||||
|
from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome
|
||||||
|
from materialyoucolor.scheme.scheme_neutral import SchemeNeutral
|
||||||
|
from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow
|
||||||
|
from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot
|
||||||
|
from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant
|
||||||
|
|
||||||
|
|
||||||
|
def hex_to_hct(hex: str) -> Hct:
|
||||||
|
return Hct.from_int(int(f"0xFF{hex}", 16))
|
||||||
|
|
||||||
|
|
||||||
|
light_colours = [
|
||||||
|
hex_to_hct("dc8a78"),
|
||||||
|
hex_to_hct("dd7878"),
|
||||||
|
hex_to_hct("ea76cb"),
|
||||||
|
hex_to_hct("8839ef"),
|
||||||
|
hex_to_hct("d20f39"),
|
||||||
|
hex_to_hct("e64553"),
|
||||||
|
hex_to_hct("fe640b"),
|
||||||
|
hex_to_hct("df8e1d"),
|
||||||
|
hex_to_hct("40a02b"),
|
||||||
|
hex_to_hct("179299"),
|
||||||
|
hex_to_hct("04a5e5"),
|
||||||
|
hex_to_hct("209fb5"),
|
||||||
|
hex_to_hct("1e66f5"),
|
||||||
|
hex_to_hct("7287fd"),
|
||||||
|
]
|
||||||
|
|
||||||
|
dark_colours = [
|
||||||
|
hex_to_hct("f5e0dc"),
|
||||||
|
hex_to_hct("f2cdcd"),
|
||||||
|
hex_to_hct("f5c2e7"),
|
||||||
|
hex_to_hct("cba6f7"),
|
||||||
|
hex_to_hct("f38ba8"),
|
||||||
|
hex_to_hct("eba0ac"),
|
||||||
|
hex_to_hct("fab387"),
|
||||||
|
hex_to_hct("f9e2af"),
|
||||||
|
hex_to_hct("a6e3a1"),
|
||||||
|
hex_to_hct("94e2d5"),
|
||||||
|
hex_to_hct("89dceb"),
|
||||||
|
hex_to_hct("74c7ec"),
|
||||||
|
hex_to_hct("89b4fa"),
|
||||||
|
hex_to_hct("b4befe"),
|
||||||
|
]
|
||||||
|
|
||||||
|
colour_names = [
|
||||||
|
"rosewater",
|
||||||
|
"flamingo",
|
||||||
|
"pink",
|
||||||
|
"mauve",
|
||||||
|
"red",
|
||||||
|
"maroon",
|
||||||
|
"peach",
|
||||||
|
"yellow",
|
||||||
|
"green",
|
||||||
|
"teal",
|
||||||
|
"sky",
|
||||||
|
"sapphire",
|
||||||
|
"blue",
|
||||||
|
"lavender",
|
||||||
|
"success",
|
||||||
|
"error",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def grayscale(colour: Hct, light: bool) -> Hct:
|
||||||
|
colour = darken(colour, 0.35) if light else lighten(colour, 0.65)
|
||||||
|
colour.chroma = 0
|
||||||
|
return colour
|
||||||
|
|
||||||
|
|
||||||
|
def mix(a: Hct, b: Hct, w: float) -> Hct:
|
||||||
|
return Hct.from_int(Blend.cam16_ucs(a.to_int(), b.to_int(), w))
|
||||||
|
|
||||||
|
|
||||||
|
def harmonize(a: Hct, b: Hct) -> Hct:
|
||||||
|
return Hct.from_int(Blend.harmonize(a.to_int(), b.to_int()))
|
||||||
|
|
||||||
|
|
||||||
|
def lighten(colour: Hct, amount: float) -> Hct:
|
||||||
|
diff = (100 - colour.tone) * amount
|
||||||
|
return Hct.from_hct(colour.hue, colour.chroma + diff / 5, colour.tone + diff)
|
||||||
|
|
||||||
|
|
||||||
|
def darken(colour: Hct, amount: float) -> Hct:
|
||||||
|
diff = colour.tone * amount
|
||||||
|
return Hct.from_hct(colour.hue, colour.chroma + diff / 5, colour.tone - diff)
|
||||||
|
|
||||||
|
|
||||||
|
def distance(colour: Cam16, base: Cam16) -> float:
|
||||||
|
return colour.distance(base)
|
||||||
|
|
||||||
|
|
||||||
|
def smart_sort(colours: list[Hct], base: list[Hct]) -> dict[str, Hct]:
|
||||||
|
sorted_colours = [None] * len(colours)
|
||||||
|
distances = {}
|
||||||
|
|
||||||
|
cams = [(c, Cam16.from_int(c.to_int())) for c in colours]
|
||||||
|
base_cams = [Cam16.from_int(c.to_int()) for c in base]
|
||||||
|
|
||||||
|
for colour, cam in cams:
|
||||||
|
dist = [(i, distance(cam, b)) for i, b in enumerate(base_cams)]
|
||||||
|
dist.sort(key=lambda x: x[1])
|
||||||
|
distances[colour] = dist
|
||||||
|
|
||||||
|
for colour in colours:
|
||||||
|
while len(distances[colour]) > 0:
|
||||||
|
i, dist = distances[colour][0]
|
||||||
|
|
||||||
|
if sorted_colours[i] is None:
|
||||||
|
sorted_colours[i] = colour, dist
|
||||||
|
break
|
||||||
|
elif sorted_colours[i][1] > dist:
|
||||||
|
old = sorted_colours[i][0]
|
||||||
|
sorted_colours[i] = colour, dist
|
||||||
|
colour = old
|
||||||
|
|
||||||
|
distances[colour].pop(0)
|
||||||
|
|
||||||
|
return {colour_names[i]: c[0] for i, c in enumerate(sorted_colours)}
|
||||||
|
|
||||||
|
|
||||||
|
def get_scheme(scheme: str) -> DynamicScheme:
|
||||||
|
if scheme == "content":
|
||||||
|
return SchemeContent
|
||||||
|
if scheme == "expressive":
|
||||||
|
return SchemeExpressive
|
||||||
|
if scheme == "fidelity":
|
||||||
|
return SchemeFidelity
|
||||||
|
if scheme == "fruitsalad":
|
||||||
|
return SchemeFruitSalad
|
||||||
|
if scheme == "monochrome":
|
||||||
|
return SchemeMonochrome
|
||||||
|
if scheme == "neutral":
|
||||||
|
return SchemeNeutral
|
||||||
|
if scheme == "rainbow":
|
||||||
|
return SchemeRainbow
|
||||||
|
if scheme == "tonalspot":
|
||||||
|
return SchemeTonalSpot
|
||||||
|
return SchemeVibrant
|
||||||
|
|
||||||
|
|
||||||
|
def gen_scheme(scheme, primary: Hct, colours: list[Hct]) -> dict[str, str]:
|
||||||
|
light = scheme.mode == "light"
|
||||||
|
base = light_colours if light else dark_colours
|
||||||
|
|
||||||
|
# Sort colours and turn into dict
|
||||||
|
colours = smart_sort(colours, base)
|
||||||
|
|
||||||
|
# Harmonize colours
|
||||||
|
for name, hct in colours.items():
|
||||||
|
if scheme.variant == "monochrome":
|
||||||
|
colours[name] = grayscale(hct, light)
|
||||||
|
else:
|
||||||
|
harmonized = harmonize(hct, primary)
|
||||||
|
colours[name] = darken(harmonized, 0.35) if light else lighten(harmonized, 0.65)
|
||||||
|
|
||||||
|
# Material colours
|
||||||
|
primary_scheme = get_scheme(scheme.variant)(primary, not light, 0)
|
||||||
|
for colour in vars(MaterialDynamicColors).keys():
|
||||||
|
colour_name = getattr(MaterialDynamicColors, colour)
|
||||||
|
if hasattr(colour_name, "get_hct"):
|
||||||
|
colours[colour] = colour_name.get_hct(primary_scheme)
|
||||||
|
|
||||||
|
# FIXME: deprecated stuff
|
||||||
|
colours["text"] = colours["onBackground"]
|
||||||
|
colours["subtext1"] = colours["onSurfaceVariant"]
|
||||||
|
colours["subtext0"] = colours["outline"]
|
||||||
|
colours["overlay2"] = mix(colours["surface"], colours["outline"], 0.86)
|
||||||
|
colours["overlay1"] = mix(colours["surface"], colours["outline"], 0.71)
|
||||||
|
colours["overlay0"] = mix(colours["surface"], colours["outline"], 0.57)
|
||||||
|
colours["surface2"] = mix(colours["surface"], colours["outline"], 0.43)
|
||||||
|
colours["surface1"] = mix(colours["surface"], colours["outline"], 0.29)
|
||||||
|
colours["surface0"] = mix(colours["surface"], colours["outline"], 0.14)
|
||||||
|
colours["base"] = colours["surface"]
|
||||||
|
colours["mantle"] = darken(colours["surface"], 0.03)
|
||||||
|
colours["crust"] = darken(colours["surface"], 0.05)
|
||||||
|
colours["success"] = harmonize(base[8], primary)
|
||||||
|
|
||||||
|
# For debugging
|
||||||
|
# print("\n".join(["{}: \x1b[48;2;{};{};{}m \x1b[0m".format(n, *c.to_rgba()[:3]) for n, c in colours.items()]))
|
||||||
|
|
||||||
|
return {k: hex(v.to_int())[4:] for k, v in colours.items()}
|
||||||
Executable → Regular
+16
-18
@@ -20,9 +20,8 @@ class Score:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def score(colors_to_population: dict) -> tuple[list[Hct], list[Hct]]:
|
def score(colors_to_population: dict, filter_enabled: bool = True) -> tuple[list[Hct], list[Hct]]:
|
||||||
desired = 14
|
desired = 14
|
||||||
filter_enabled = False
|
|
||||||
dislike_filter = True
|
dislike_filter = True
|
||||||
|
|
||||||
colors_hct = []
|
colors_hct = []
|
||||||
@@ -50,18 +49,11 @@ class Score:
|
|||||||
hue = int(sanitize_degrees_int(round(hct.hue)))
|
hue = int(sanitize_degrees_int(round(hct.hue)))
|
||||||
proportion = hue_excited_proportions[hue]
|
proportion = hue_excited_proportions[hue]
|
||||||
|
|
||||||
if filter_enabled and (
|
if filter_enabled and (hct.chroma < Score.CUTOFF_CHROMA or proportion <= Score.CUTOFF_EXCITED_PROPORTION):
|
||||||
hct.chroma < Score.CUTOFF_CHROMA
|
|
||||||
or proportion <= Score.CUTOFF_EXCITED_PROPORTION
|
|
||||||
):
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
proportion_score = proportion * 100.0 * Score.WEIGHT_PROPORTION
|
proportion_score = proportion * 100.0 * Score.WEIGHT_PROPORTION
|
||||||
chroma_weight = (
|
chroma_weight = Score.WEIGHT_CHROMA_BELOW if hct.chroma < Score.TARGET_CHROMA else Score.WEIGHT_CHROMA_ABOVE
|
||||||
Score.WEIGHT_CHROMA_BELOW
|
|
||||||
if hct.chroma < Score.TARGET_CHROMA
|
|
||||||
else Score.WEIGHT_CHROMA_ABOVE
|
|
||||||
)
|
|
||||||
chroma_score = (hct.chroma - Score.TARGET_CHROMA) * chroma_weight
|
chroma_score = (hct.chroma - Score.TARGET_CHROMA) * chroma_weight
|
||||||
score = proportion_score + chroma_score
|
score = proportion_score + chroma_score
|
||||||
scored_hct.append({"hct": hct, "score": score})
|
scored_hct.append({"hct": hct, "score": score})
|
||||||
@@ -75,8 +67,7 @@ class Score:
|
|||||||
for item in scored_hct:
|
for item in scored_hct:
|
||||||
hct = item["hct"]
|
hct = item["hct"]
|
||||||
duplicate_hue = any(
|
duplicate_hue = any(
|
||||||
difference_degrees(hct.hue, chosen_hct.hue) < difference_degrees_
|
difference_degrees(hct.hue, chosen_hct.hue) < difference_degrees_ for chosen_hct in chosen_colors
|
||||||
for chosen_hct in chosen_colors
|
|
||||||
)
|
)
|
||||||
if not duplicate_hue:
|
if not duplicate_hue:
|
||||||
chosen_colors.append(hct)
|
chosen_colors.append(hct)
|
||||||
@@ -102,8 +93,7 @@ class Score:
|
|||||||
for item in scored_hct:
|
for item in scored_hct:
|
||||||
hct = item["hct"]
|
hct = item["hct"]
|
||||||
duplicate_hue = any(
|
duplicate_hue = any(
|
||||||
difference_degrees(hct.hue, chosen_hct.hue) < difference_degrees_
|
difference_degrees(hct.hue, chosen_hct.hue) < difference_degrees_ for chosen_hct in chosen_primaries
|
||||||
for chosen_hct in chosen_primaries
|
|
||||||
)
|
)
|
||||||
if not duplicate_hue:
|
if not duplicate_hue:
|
||||||
chosen_primaries.append(hct)
|
chosen_primaries.append(hct)
|
||||||
@@ -119,16 +109,24 @@ class Score:
|
|||||||
for i, chosen_hct in enumerate(chosen_colors):
|
for i, chosen_hct in enumerate(chosen_colors):
|
||||||
chosen_colors[i] = DislikeAnalyzer.fix_if_disliked(chosen_hct)
|
chosen_colors[i] = DislikeAnalyzer.fix_if_disliked(chosen_hct)
|
||||||
|
|
||||||
|
# Ensure enough colours
|
||||||
|
if len(chosen_colors) < desired:
|
||||||
|
return Score.score(colors_to_population, False)
|
||||||
|
|
||||||
return chosen_primaries, chosen_colors
|
return chosen_primaries, chosen_colors
|
||||||
|
|
||||||
|
|
||||||
|
def score(image: str) -> tuple[list[Hct], list[Hct]]:
|
||||||
|
return Score.score(ImageQuantizeCelebi(image, 1, 128))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
img = sys.argv[1]
|
img = sys.argv[1]
|
||||||
mode = sys.argv[2] if len(sys.argv) > 2 else "hex"
|
mode = sys.argv[2] if len(sys.argv) > 2 else "hex"
|
||||||
|
|
||||||
colours = Score.score(ImageQuantizeCelebi(img, 1, 128))
|
colours = Score.score(ImageQuantizeCelebi(img, 1, 128))
|
||||||
for l in colours:
|
for t in colours:
|
||||||
if mode != "hex":
|
if mode != "hex":
|
||||||
print("".join(["\x1b[48;2;{};{};{}m \x1b[0m".format(*c.to_rgba()[:3]) for c in l]))
|
print("".join(["\x1b[48;2;{};{};{}m \x1b[0m".format(*c.to_rgba()[:3]) for c in t]))
|
||||||
if mode != "swatch":
|
if mode != "swatch":
|
||||||
print(" ".join(["{:02X}{:02X}{:02X}".format(*c.to_rgba()[:3]) for c in l]))
|
print(" ".join(["{:02X}{:02X}{:02X}".format(*c.to_rgba()[:3]) for c in t]))
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
config_dir = Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config"))
|
||||||
|
data_dir = Path(os.getenv("XDG_DATA_HOME", Path.home() / ".local/share"))
|
||||||
|
state_dir = Path(os.getenv("XDG_STATE_HOME", Path.home() / ".local/state"))
|
||||||
|
cache_dir = Path(os.getenv("XDG_CACHE_HOME", Path.home() / ".cache"))
|
||||||
|
|
||||||
|
c_config_dir = config_dir / "caelestia"
|
||||||
|
c_data_dir = data_dir / "caelestia"
|
||||||
|
c_state_dir = state_dir / "caelestia"
|
||||||
|
c_cache_dir = cache_dir / "caelestia"
|
||||||
|
|
||||||
|
cli_data_dir = Path(__file__).parent.parent / "data"
|
||||||
|
templates_dir = cli_data_dir / "templates"
|
||||||
|
|
||||||
|
scheme_path = c_state_dir / "scheme.json"
|
||||||
|
scheme_data_dir = cli_data_dir / "schemes"
|
||||||
|
scheme_cache_dir = c_cache_dir / "schemes"
|
||||||
|
|
||||||
|
wallpapers_dir = Path.home() / "Pictures/Wallpapers"
|
||||||
|
wallpaper_path_path = c_state_dir / "wallpaper/path.txt"
|
||||||
|
wallpaper_link_path = c_state_dir / "wallpaper/current"
|
||||||
|
wallpaper_thumbnail_path = c_state_dir / "wallpaper/thumbnail.jpg"
|
||||||
|
wallpapers_cache_dir = c_cache_dir / "wallpapers"
|
||||||
|
|
||||||
|
screenshots_dir = Path.home() / "Pictures/Screenshots"
|
||||||
|
screenshots_cache_dir = c_cache_dir / "screenshots"
|
||||||
|
|
||||||
|
recordings_dir = Path.home() / "Videos/Recordings"
|
||||||
|
recording_path = c_state_dir / "record/recording.mp4"
|
||||||
|
recording_notif_path = c_state_dir / "record/notifid.txt"
|
||||||
|
|
||||||
|
|
||||||
|
def compute_hash(path: Path | str) -> str:
|
||||||
|
sha = hashlib.sha256()
|
||||||
|
|
||||||
|
with open(path, "rb") as f:
|
||||||
|
while chunk := f.read(8192):
|
||||||
|
sha.update(chunk)
|
||||||
|
|
||||||
|
return sha.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def atomic_dump(path: Path, content: dict[str, any]) -> None:
|
||||||
|
with tempfile.NamedTemporaryFile("w") as f:
|
||||||
|
json.dump(content, f)
|
||||||
|
f.flush()
|
||||||
|
shutil.move(f.name, path)
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
import json
|
||||||
|
import random
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from caelestia.utils.material import get_colours_for_image
|
||||||
|
from caelestia.utils.paths import atomic_dump, scheme_data_dir, scheme_path
|
||||||
|
|
||||||
|
|
||||||
|
class Scheme:
|
||||||
|
_name: str
|
||||||
|
_flavour: str
|
||||||
|
_mode: str
|
||||||
|
_variant: str
|
||||||
|
_colours: dict[str, str]
|
||||||
|
|
||||||
|
def __init__(self, json: dict[str, any] | None) -> None:
|
||||||
|
if json is None:
|
||||||
|
self._name = "catppuccin"
|
||||||
|
self._flavour = "mocha"
|
||||||
|
self._mode = "dark"
|
||||||
|
self._variant = "tonalspot"
|
||||||
|
self._colours = read_colours_from_file(self.get_colours_path())
|
||||||
|
else:
|
||||||
|
self._name = json["name"]
|
||||||
|
self._flavour = json["flavour"]
|
||||||
|
self._mode = json["mode"]
|
||||||
|
self._variant = json["variant"]
|
||||||
|
self._colours = json["colours"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, name: str) -> None:
|
||||||
|
if name == self._name:
|
||||||
|
return
|
||||||
|
|
||||||
|
if name not in get_scheme_names():
|
||||||
|
raise ValueError(f"Invalid scheme name: {name}")
|
||||||
|
|
||||||
|
self._name = name
|
||||||
|
self._check_flavour()
|
||||||
|
self._check_mode()
|
||||||
|
self._update_colours()
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def flavour(self) -> str:
|
||||||
|
return self._flavour
|
||||||
|
|
||||||
|
@flavour.setter
|
||||||
|
def flavour(self, flavour: str) -> None:
|
||||||
|
if flavour == self._flavour:
|
||||||
|
return
|
||||||
|
|
||||||
|
if flavour not in get_scheme_flavours():
|
||||||
|
raise ValueError(f'Invalid scheme flavour: "{flavour}". Valid flavours: {get_scheme_flavours()}')
|
||||||
|
|
||||||
|
self._flavour = flavour
|
||||||
|
self._check_mode()
|
||||||
|
self.update_colours()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mode(self) -> str:
|
||||||
|
return self._mode
|
||||||
|
|
||||||
|
@mode.setter
|
||||||
|
def mode(self, mode: str) -> None:
|
||||||
|
if mode == self._mode:
|
||||||
|
return
|
||||||
|
|
||||||
|
if mode not in get_scheme_modes():
|
||||||
|
raise ValueError(f'Invalid scheme mode: "{mode}". Valid modes: {get_scheme_modes()}')
|
||||||
|
|
||||||
|
self._mode = mode
|
||||||
|
self.update_colours()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def variant(self) -> str:
|
||||||
|
return self._variant
|
||||||
|
|
||||||
|
@variant.setter
|
||||||
|
def variant(self, variant: str) -> None:
|
||||||
|
if variant == self._variant:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._variant = variant
|
||||||
|
self.update_colours()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def colours(self) -> dict[str, str]:
|
||||||
|
return self._colours
|
||||||
|
|
||||||
|
def get_colours_path(self) -> Path:
|
||||||
|
return (scheme_data_dir / self.name / self.flavour / self.mode).with_suffix(".txt")
|
||||||
|
|
||||||
|
def save(self) -> None:
|
||||||
|
scheme_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
atomic_dump(
|
||||||
|
scheme_path,
|
||||||
|
{
|
||||||
|
"name": self.name,
|
||||||
|
"flavour": self.flavour,
|
||||||
|
"mode": self.mode,
|
||||||
|
"variant": self.variant,
|
||||||
|
"colours": self.colours,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_random(self) -> None:
|
||||||
|
self._name = random.choice(get_scheme_names())
|
||||||
|
self._flavour = random.choice(get_scheme_flavours())
|
||||||
|
self._mode = random.choice(get_scheme_modes())
|
||||||
|
self.update_colours()
|
||||||
|
|
||||||
|
def update_colours(self) -> None:
|
||||||
|
self._update_colours()
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def _check_flavour(self) -> None:
|
||||||
|
global scheme_flavours
|
||||||
|
scheme_flavours = None
|
||||||
|
if self._flavour not in get_scheme_flavours():
|
||||||
|
self._flavour = get_scheme_flavours()[0]
|
||||||
|
|
||||||
|
def _check_mode(self) -> None:
|
||||||
|
global scheme_modes
|
||||||
|
scheme_modes = None
|
||||||
|
if self._mode not in get_scheme_modes():
|
||||||
|
self._mode = get_scheme_modes()[0]
|
||||||
|
|
||||||
|
def _update_colours(self) -> None:
|
||||||
|
if self.name == "dynamic":
|
||||||
|
self._colours = get_colours_for_image()
|
||||||
|
else:
|
||||||
|
self._colours = read_colours_from_file(self.get_colours_path())
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return (
|
||||||
|
f"Current scheme:\n"
|
||||||
|
f" Name: {self.name}\n"
|
||||||
|
f" Flavour: {self.flavour}\n"
|
||||||
|
f" Mode: {self.mode}\n"
|
||||||
|
f" Variant: {self.variant}\n"
|
||||||
|
f" Colours:\n"
|
||||||
|
f" {'\n '.join(f'{n}: \x1b[38;2;{int(c[0:2], 16)};{int(c[2:4], 16)};{int(c[4:6], 16)}m{c}\x1b[0m' for n, c in self.colours.items())}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
scheme_variants = [
|
||||||
|
"tonalspot",
|
||||||
|
"vibrant",
|
||||||
|
"expressive",
|
||||||
|
"fidelity",
|
||||||
|
"fruitsalad",
|
||||||
|
"monochrome",
|
||||||
|
"neutral",
|
||||||
|
"rainbow",
|
||||||
|
"content",
|
||||||
|
]
|
||||||
|
|
||||||
|
scheme_names: list[str] = None
|
||||||
|
scheme_flavours: list[str] = None
|
||||||
|
scheme_modes: list[str] = None
|
||||||
|
|
||||||
|
scheme: Scheme = None
|
||||||
|
|
||||||
|
|
||||||
|
def read_colours_from_file(path: Path) -> dict[str, str]:
|
||||||
|
return {k.strip(): v.strip() for k, v in (line.split(" ") for line in path.read_text().splitlines())}
|
||||||
|
|
||||||
|
|
||||||
|
def get_scheme_path() -> Path:
|
||||||
|
return get_scheme().get_colours_path()
|
||||||
|
|
||||||
|
|
||||||
|
def get_scheme() -> Scheme:
|
||||||
|
global scheme
|
||||||
|
|
||||||
|
if scheme is None:
|
||||||
|
try:
|
||||||
|
scheme_json = json.loads(scheme_path.read_text())
|
||||||
|
scheme = Scheme(scheme_json)
|
||||||
|
except (IOError, json.JSONDecodeError):
|
||||||
|
scheme = Scheme(None)
|
||||||
|
|
||||||
|
return scheme
|
||||||
|
|
||||||
|
|
||||||
|
def get_scheme_names() -> list[str]:
|
||||||
|
global scheme_names
|
||||||
|
|
||||||
|
if scheme_names is None:
|
||||||
|
scheme_names = [f.name for f in scheme_data_dir.iterdir() if f.is_dir()]
|
||||||
|
scheme_names.append("dynamic")
|
||||||
|
|
||||||
|
return scheme_names
|
||||||
|
|
||||||
|
|
||||||
|
def get_scheme_flavours() -> list[str]:
|
||||||
|
global scheme_flavours
|
||||||
|
|
||||||
|
if scheme_flavours is None:
|
||||||
|
name = get_scheme().name
|
||||||
|
if name == "dynamic":
|
||||||
|
scheme_flavours = ["default", "alt1", "alt2"]
|
||||||
|
else:
|
||||||
|
scheme_flavours = [f.name for f in (scheme_data_dir / name).iterdir() if f.is_dir()]
|
||||||
|
|
||||||
|
return scheme_flavours
|
||||||
|
|
||||||
|
|
||||||
|
def get_scheme_modes() -> list[str]:
|
||||||
|
global scheme_modes
|
||||||
|
|
||||||
|
if scheme_modes is None:
|
||||||
|
scheme = get_scheme()
|
||||||
|
if scheme.name == "dynamic":
|
||||||
|
scheme_modes = ["light", "dark"]
|
||||||
|
else:
|
||||||
|
scheme_modes = [f.stem for f in (scheme_data_dir / scheme.name / scheme.flavour).iterdir() if f.is_file()]
|
||||||
|
|
||||||
|
return scheme_modes
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from caelestia.utils.paths import config_dir, templates_dir
|
||||||
|
|
||||||
|
|
||||||
|
def gen_conf(colours: dict[str, str]) -> str:
|
||||||
|
conf = ""
|
||||||
|
for name, colour in colours.items():
|
||||||
|
conf += f"${name} = {colour}\n"
|
||||||
|
return conf
|
||||||
|
|
||||||
|
|
||||||
|
def gen_scss(colours: dict[str, str]) -> str:
|
||||||
|
scss = ""
|
||||||
|
for name, colour in colours.items():
|
||||||
|
scss += f"${name}: #{colour};\n"
|
||||||
|
return scss
|
||||||
|
|
||||||
|
|
||||||
|
def gen_replace(colours: dict[str, str], template: Path, hash: bool = False) -> str:
|
||||||
|
template = template.read_text()
|
||||||
|
for name, colour in colours.items():
|
||||||
|
template = template.replace(f"{{{{ ${name} }}}}", f"#{colour}" if hash else colour)
|
||||||
|
return template
|
||||||
|
|
||||||
|
|
||||||
|
def c2s(c: str, *i: list[int]) -> str:
|
||||||
|
"""Hex to ANSI sequence (e.g. ffffff, 11 -> \x1b]11;rgb:ff/ff/ff\x1b\\)"""
|
||||||
|
return f"\x1b]{';'.join(map(str, i))};rgb:{c[0:2]}/{c[2:4]}/{c[4:6]}\x1b\\"
|
||||||
|
|
||||||
|
|
||||||
|
def gen_sequences(colours: dict[str, str]) -> str:
|
||||||
|
"""
|
||||||
|
10: foreground
|
||||||
|
11: background
|
||||||
|
12: cursor
|
||||||
|
17: selection
|
||||||
|
4:
|
||||||
|
0 - 7: normal colours
|
||||||
|
8 - 15: bright colours
|
||||||
|
16+: 256 colours
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
c2s(colours["onSurface"], 10)
|
||||||
|
+ c2s(colours["surface"], 11)
|
||||||
|
+ c2s(colours["secondary"], 12)
|
||||||
|
+ c2s(colours["secondary"], 17)
|
||||||
|
+ c2s(colours["surfaceContainer"], 4, 0)
|
||||||
|
+ c2s(colours["red"], 4, 1)
|
||||||
|
+ c2s(colours["green"], 4, 2)
|
||||||
|
+ c2s(colours["yellow"], 4, 3)
|
||||||
|
+ c2s(colours["blue"], 4, 4)
|
||||||
|
+ c2s(colours["pink"], 4, 5)
|
||||||
|
+ c2s(colours["teal"], 4, 6)
|
||||||
|
+ c2s(colours["onSurfaceVariant"], 4, 7)
|
||||||
|
+ c2s(colours["surfaceContainer"], 4, 8)
|
||||||
|
+ c2s(colours["red"], 4, 9)
|
||||||
|
+ c2s(colours["green"], 4, 10)
|
||||||
|
+ c2s(colours["yellow"], 4, 11)
|
||||||
|
+ c2s(colours["blue"], 4, 12)
|
||||||
|
+ c2s(colours["pink"], 4, 13)
|
||||||
|
+ c2s(colours["teal"], 4, 14)
|
||||||
|
+ c2s(colours["onSurfaceVariant"], 4, 15)
|
||||||
|
+ c2s(colours["primary"], 4, 16)
|
||||||
|
+ c2s(colours["secondary"], 4, 17)
|
||||||
|
+ c2s(colours["tertiary"], 4, 18)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def try_write(path: Path, content: str) -> None:
|
||||||
|
try:
|
||||||
|
path.write_text(content)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def apply_terms(sequences: str) -> None:
|
||||||
|
pts_path = Path("/dev/pts")
|
||||||
|
for pt in pts_path.iterdir():
|
||||||
|
if pt.name.isdigit():
|
||||||
|
with pt.open("a") as f:
|
||||||
|
f.write(sequences)
|
||||||
|
|
||||||
|
|
||||||
|
def apply_hypr(conf: str) -> None:
|
||||||
|
try_write(config_dir / "hypr/scheme/current.conf", conf)
|
||||||
|
|
||||||
|
|
||||||
|
def apply_discord(scss: str) -> None:
|
||||||
|
with tempfile.TemporaryDirectory("w") as tmp_dir:
|
||||||
|
(Path(tmp_dir) / "_colours.scss").write_text(scss)
|
||||||
|
conf = subprocess.check_output(["sass", "-I", tmp_dir, templates_dir / "discord.scss"], text=True)
|
||||||
|
|
||||||
|
for client in "Equicord", "Vencord", "BetterDiscord", "equicord", "vesktop", "legcord":
|
||||||
|
try_write(config_dir / client / "themes/caelestia.theme.css", conf)
|
||||||
|
|
||||||
|
|
||||||
|
def apply_spicetify(colours: dict[str, str], mode: str) -> None:
|
||||||
|
template = gen_replace(colours, templates_dir / f"spicetify-{mode}.ini")
|
||||||
|
try_write(config_dir / "spicetify/Themes/caelestia/color.ini", template)
|
||||||
|
|
||||||
|
|
||||||
|
def apply_fuzzel(colours: dict[str, str]) -> None:
|
||||||
|
template = gen_replace(colours, templates_dir / "fuzzel.ini")
|
||||||
|
try_write(config_dir / "fuzzel/fuzzel.ini", template)
|
||||||
|
|
||||||
|
|
||||||
|
def apply_btop(colours: dict[str, str]) -> None:
|
||||||
|
template = gen_replace(colours, templates_dir / "btop.theme", hash=True)
|
||||||
|
try_write(config_dir / "btop/themes/caelestia.theme", template)
|
||||||
|
subprocess.run(["killall", "-USR2", "btop"])
|
||||||
|
|
||||||
|
|
||||||
|
def apply_colours(colours: dict[str, str], mode: str) -> None:
|
||||||
|
apply_terms(gen_sequences(colours))
|
||||||
|
apply_hypr(gen_conf(colours)) # FIXME: LAGGY
|
||||||
|
apply_discord(gen_scss(colours))
|
||||||
|
apply_spicetify(colours, mode)
|
||||||
|
apply_fuzzel(colours)
|
||||||
|
apply_btop(colours)
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
import random
|
||||||
|
from argparse import Namespace
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from materialyoucolor.hct import Hct
|
||||||
|
from materialyoucolor.utils.color_utils import argb_from_rgb
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
from caelestia.utils.hypr import message
|
||||||
|
from caelestia.utils.material import get_colours_for_image
|
||||||
|
from caelestia.utils.paths import (
|
||||||
|
compute_hash,
|
||||||
|
wallpaper_link_path,
|
||||||
|
wallpaper_path_path,
|
||||||
|
wallpaper_thumbnail_path,
|
||||||
|
wallpapers_cache_dir,
|
||||||
|
)
|
||||||
|
from caelestia.utils.scheme import Scheme, get_scheme
|
||||||
|
from caelestia.utils.theme import apply_colours
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_image(path: Path | str) -> bool:
|
||||||
|
path = Path(path)
|
||||||
|
return path.is_file() and path.suffix in [".jpg", ".jpeg", ".png", ".webp", ".tif", ".tiff"]
|
||||||
|
|
||||||
|
|
||||||
|
def check_wall(wall: Path, filter_size: tuple[int, int], threshold: float) -> bool:
|
||||||
|
with Image.open(wall) as img:
|
||||||
|
width, height = img.size
|
||||||
|
return width >= filter_size[0] * threshold and height >= filter_size[1] * threshold
|
||||||
|
|
||||||
|
|
||||||
|
def get_wallpaper() -> str:
|
||||||
|
try:
|
||||||
|
return wallpaper_path_path.read_text()
|
||||||
|
except IOError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_wallpapers(args: Namespace) -> list[Path]:
|
||||||
|
dir = Path(args.random)
|
||||||
|
if not dir.is_dir():
|
||||||
|
return []
|
||||||
|
|
||||||
|
walls = [f for f in dir.rglob("*") if is_valid_image(f)]
|
||||||
|
|
||||||
|
if args.no_filter:
|
||||||
|
return walls
|
||||||
|
|
||||||
|
monitors = message("monitors")
|
||||||
|
filter_size = monitors[0]["width"], monitors[0]["height"]
|
||||||
|
for monitor in monitors[1:]:
|
||||||
|
if filter_size[0] > monitor["width"]:
|
||||||
|
filter_size[0] = monitor["width"]
|
||||||
|
if filter_size[1] > monitor["height"]:
|
||||||
|
filter_size[1] = monitor["height"]
|
||||||
|
|
||||||
|
return [f for f in walls if check_wall(f, filter_size, args.threshold)]
|
||||||
|
|
||||||
|
|
||||||
|
def get_thumb(wall: Path, cache: Path) -> Path:
|
||||||
|
thumb = cache / "thumbnail.jpg"
|
||||||
|
|
||||||
|
if not thumb.exists():
|
||||||
|
with Image.open(wall) as img:
|
||||||
|
img = img.convert("RGB")
|
||||||
|
img.thumbnail((128, 128), Image.NEAREST)
|
||||||
|
thumb.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
img.save(thumb, "JPEG")
|
||||||
|
|
||||||
|
return thumb
|
||||||
|
|
||||||
|
|
||||||
|
def get_smart_mode(wall: Path, cache: Path) -> str:
|
||||||
|
mode_cache = cache / "mode.txt"
|
||||||
|
|
||||||
|
try:
|
||||||
|
return mode_cache.read_text()
|
||||||
|
except IOError:
|
||||||
|
with Image.open(get_thumb(wall, cache)) as img:
|
||||||
|
img.thumbnail((1, 1), Image.LANCZOS)
|
||||||
|
mode = "light" if Hct.from_int(argb_from_rgb(*img.getpixel((0, 0)))).tone > 60 else "dark"
|
||||||
|
|
||||||
|
mode_cache.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
mode_cache.write_text(mode)
|
||||||
|
|
||||||
|
return mode
|
||||||
|
|
||||||
|
|
||||||
|
def get_colours_for_wall(wall: Path | str, no_smart: bool) -> None:
|
||||||
|
scheme = get_scheme()
|
||||||
|
cache = wallpapers_cache_dir / compute_hash(wall)
|
||||||
|
|
||||||
|
if not no_smart:
|
||||||
|
scheme = Scheme(
|
||||||
|
{
|
||||||
|
"name": scheme.name,
|
||||||
|
"flavour": scheme.flavour,
|
||||||
|
"mode": get_smart_mode(wall, cache),
|
||||||
|
"variant": scheme.variant,
|
||||||
|
"colours": scheme.colours,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return get_colours_for_image(get_thumb(wall, cache), scheme)
|
||||||
|
|
||||||
|
|
||||||
|
def set_wallpaper(wall: Path | str, no_smart: bool) -> None:
|
||||||
|
if not is_valid_image(wall):
|
||||||
|
raise ValueError(f'"{wall}" is not a valid image')
|
||||||
|
|
||||||
|
# Update files
|
||||||
|
wallpaper_path_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
wallpaper_path_path.write_text(str(wall))
|
||||||
|
wallpaper_link_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
wallpaper_link_path.unlink(missing_ok=True)
|
||||||
|
wallpaper_link_path.symlink_to(wall)
|
||||||
|
|
||||||
|
cache = wallpapers_cache_dir / compute_hash(wall)
|
||||||
|
|
||||||
|
# Generate thumbnail or get from cache
|
||||||
|
thumb = get_thumb(wall, cache)
|
||||||
|
wallpaper_thumbnail_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
wallpaper_thumbnail_path.unlink(missing_ok=True)
|
||||||
|
wallpaper_thumbnail_path.symlink_to(thumb)
|
||||||
|
|
||||||
|
scheme = get_scheme()
|
||||||
|
|
||||||
|
# Change mode based on wallpaper colour
|
||||||
|
if not no_smart:
|
||||||
|
scheme.mode = get_smart_mode(wall, cache)
|
||||||
|
|
||||||
|
# Update colours
|
||||||
|
scheme.update_colours()
|
||||||
|
apply_colours(scheme.colours, scheme.mode)
|
||||||
|
|
||||||
|
|
||||||
|
def set_random(args: Namespace) -> None:
|
||||||
|
set_wallpaper(random.choice(get_wallpapers(args)), args.no_smart)
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
if ! hyprctl workspaces -j | jq -e 'first(.[] | select(.name == "special:special"))'
|
|
||||||
set activews (hyprctl activewindow -j | jq -r '.workspace.name')
|
|
||||||
string match -r -- '^special:' $activews && set togglews (string sub -s 9 $activews) || set togglews special
|
|
||||||
else
|
|
||||||
set togglews special
|
|
||||||
end
|
|
||||||
|
|
||||||
hyprctl dispatch togglespecialworkspace $togglews
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
. (dirname (status filename))/../util.fish
|
|
||||||
|
|
||||||
function move-client -a selector workspace
|
|
||||||
if hyprctl -j clients | jq -e 'first(.[] | select('$selector')).workspace.name != "special:'$workspace'"' > /dev/null
|
|
||||||
# Window not in correct workspace
|
|
||||||
set -l window_addr (hyprctl -j clients | jq -r 'first(.[] | select('$selector')).address')
|
|
||||||
hyprctl dispatch movetoworkspacesilent "special:$workspace,address:$window_addr"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function spawn-client -a selector spawn
|
|
||||||
# Spawn if doesn't exist
|
|
||||||
hyprctl -j clients | jq -e "first(.[] | select($selector))" > /dev/null
|
|
||||||
set -l stat $status
|
|
||||||
if test $stat != 0
|
|
||||||
eval "app2unit -- $spawn & disown"
|
|
||||||
end
|
|
||||||
test $stat != 0 # Exit 1 if already exists
|
|
||||||
end
|
|
||||||
|
|
||||||
function jq-var -a op json
|
|
||||||
jq -rn --argjson json "$json" "\$json | $op"
|
|
||||||
end
|
|
||||||
|
|
||||||
function toggle-workspace -a workspace
|
|
||||||
set -l apps (get-config "toggles.$workspace.apps")
|
|
||||||
|
|
||||||
for i in (seq 0 (math (jq-var 'length' "$apps") - 1))
|
|
||||||
set -l app (jq-var ".[$i]" "$apps")
|
|
||||||
set -l action (jq-var '.action' "$app")
|
|
||||||
set -l selector (jq-var '.selector' "$app")
|
|
||||||
set -l extra_cond (jq-var '.extraCond' "$app")
|
|
||||||
|
|
||||||
test $extra_cond = null && set -l extra_cond true
|
|
||||||
if eval $extra_cond
|
|
||||||
string match -qe -- 'spawn' $action && spawn-client $selector (jq-var '.spawn' "$app")
|
|
||||||
string match -qe -- 'move' $action && move-client $selector $workspace
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
hyprctl dispatch togglespecialworkspace $workspace
|
|
||||||
end
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
function _out -a colour level text
|
|
||||||
set_color $colour
|
|
||||||
# Pass arguments other than text to echo
|
|
||||||
echo $argv[4..] -- ":: [$level] $text"
|
|
||||||
set_color normal
|
|
||||||
end
|
|
||||||
|
|
||||||
function log -a text
|
|
||||||
_out cyan LOG $text $argv[2..]
|
|
||||||
end
|
|
||||||
|
|
||||||
function warn -a text
|
|
||||||
_out yellow WARN $text $argv[2..]
|
|
||||||
end
|
|
||||||
|
|
||||||
function error -a text
|
|
||||||
_out red ERROR $text $argv[2..]
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
|
|
||||||
function input -a text
|
|
||||||
_out blue INPUT $text $argv[2..]
|
|
||||||
end
|
|
||||||
|
|
||||||
function get-config -a key
|
|
||||||
test -f $C_CONFIG_FILE && set -l value (jq -r ".$key" $C_CONFIG_FILE)
|
|
||||||
test -n "$value" -a "$value" != null && echo $value || jq -r ".$key" (dirname (status filename))/data/config.json
|
|
||||||
end
|
|
||||||
|
|
||||||
function set-config -a key value
|
|
||||||
if test -f $C_CONFIG_FILE
|
|
||||||
set -l tmp (mktemp)
|
|
||||||
cp $C_CONFIG_FILE $tmp
|
|
||||||
jq -e ".$key = $value" $tmp > $C_CONFIG_FILE || cp $tmp $C_CONFIG_FILE
|
|
||||||
rm $tmp
|
|
||||||
else
|
|
||||||
jq -en ".$key = $value" > $C_CONFIG_FILE || rm $C_CONFIG_FILE
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
set -q XDG_DATA_HOME && set C_DATA $XDG_DATA_HOME/caelestia || set C_DATA $HOME/.local/share/caelestia
|
|
||||||
set -q XDG_STATE_HOME && set C_STATE $XDG_STATE_HOME/caelestia || set C_STATE $HOME/.local/state/caelestia
|
|
||||||
set -q XDG_CACHE_HOME && set C_CACHE $XDG_CACHE_HOME/caelestia || set C_CACHE $HOME/.cache/caelestia
|
|
||||||
set -q XDG_CONFIG_HOME && set CONFIG $XDG_CONFIG_HOME || set CONFIG $HOME/.config
|
|
||||||
set C_CONFIG $CONFIG/caelestia
|
|
||||||
set C_CONFIG_FILE $C_CONFIG/scripts.json
|
|
||||||
|
|
||||||
mkdir -p $C_DATA
|
|
||||||
mkdir -p $C_STATE
|
|
||||||
mkdir -p $C_CACHE
|
|
||||||
mkdir -p $C_CONFIG
|
|
||||||
-122
@@ -1,122 +0,0 @@
|
|||||||
#!/usr/bin/env fish
|
|
||||||
|
|
||||||
function get-valid-wallpapers
|
|
||||||
identify -ping -format '%i\n' $wallpapers_dir/** 2> /dev/null
|
|
||||||
end
|
|
||||||
|
|
||||||
set script_name (basename (status filename))
|
|
||||||
set wallpapers_dir (xdg-user-dir PICTURES)/Wallpapers
|
|
||||||
set threshold 80
|
|
||||||
|
|
||||||
# Max 0 non-option args | h, f and d are exclusive | F and t are also exclusive
|
|
||||||
argparse -n 'caelestia-wallpaper' -X 0 -x 'h,f,d' -x 'F,t' \
|
|
||||||
'h/help' \
|
|
||||||
'f/file=' \
|
|
||||||
'd/directory=' \
|
|
||||||
'F/no-filter' \
|
|
||||||
't/threshold=!_validate_int --min 0' \
|
|
||||||
'T/theme=!test $_flag_value = light -o $_flag_value = dark' \
|
|
||||||
-- $argv
|
|
||||||
or exit
|
|
||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
|
||||||
|
|
||||||
if set -q _flag_h
|
|
||||||
echo 'Usage:'
|
|
||||||
echo ' caelestia wallpaper'
|
|
||||||
echo ' caelestia wallpaper [ -h | --help ]'
|
|
||||||
echo ' caelestia wallpaper [ -f | --file ] [ -T | --theme ]'
|
|
||||||
echo ' caelestia wallpaper [ -d | --directory ] [ -F | --no-filter ] [ -T | --theme ]'
|
|
||||||
echo ' caelestia wallpaper [ -d | --directory ] [ -t | --threshold ] [ -T | --theme ]'
|
|
||||||
echo
|
|
||||||
echo 'Options:'
|
|
||||||
echo ' -h, --help Print this help message and exit'
|
|
||||||
echo ' -f, --file <file> The file to change wallpaper to'
|
|
||||||
echo ' -d, --directory <directory> The folder to select a random wallpaper from (default '$wallpapers_dir')'
|
|
||||||
echo ' -F, --no-filter Do not filter by size'
|
|
||||||
echo ' -t, --threshold <threshold> The minimum percentage of the size the image must be greater than to be selected (default '$threshold')'
|
|
||||||
echo ' -T, --theme <"light" | "dark"> Set light/dark theme for dynamic scheme'
|
|
||||||
else
|
|
||||||
set state_dir $C_STATE/wallpaper
|
|
||||||
|
|
||||||
# The path to the last chosen wallpaper
|
|
||||||
set last_wallpaper_path "$state_dir/last.txt"
|
|
||||||
|
|
||||||
# Use wallpaper given as argument else choose random
|
|
||||||
if set -q _flag_f
|
|
||||||
set chosen_wallpaper (realpath $_flag_f)
|
|
||||||
|
|
||||||
if ! identify -ping $chosen_wallpaper &> /dev/null
|
|
||||||
error "$chosen_wallpaper is not a valid image"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
# The path to the directory containing the selection of wallpapers
|
|
||||||
set -q _flag_d && set wallpapers_dir (realpath $_flag_d)
|
|
||||||
|
|
||||||
if ! test -d $wallpapers_dir
|
|
||||||
error "$wallpapers_dir does not exist"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get all files in $wallpapers_dir and exclude the last wallpaper (if it exists)
|
|
||||||
if test -f "$last_wallpaper_path"
|
|
||||||
set last_wallpaper (cat $last_wallpaper_path)
|
|
||||||
test -n "$last_wallpaper" && set unfiltered_wallpapers (get-valid-wallpapers | grep -v $last_wallpaper)
|
|
||||||
end
|
|
||||||
set -q unfiltered_wallpapers || set unfiltered_wallpapers (get-valid-wallpapers)
|
|
||||||
|
|
||||||
# Filter by resolution if no filter option is not given
|
|
||||||
if set -q _flag_F
|
|
||||||
set wallpapers $unfiltered_wallpapers
|
|
||||||
else
|
|
||||||
set -l screen_size (hyprctl monitors -j | jq -r 'max_by(.width * .height) | "\(.width)\n\(.height)"')
|
|
||||||
set -l wall_sizes (identify -ping -format '%w %h\n' $unfiltered_wallpapers)
|
|
||||||
|
|
||||||
# Apply threshold
|
|
||||||
set -q _flag_t && set threshold $_flag_t
|
|
||||||
set screen_size[1] (math $screen_size[1] x $threshold / 100)
|
|
||||||
set screen_size[2] (math $screen_size[2] x $threshold / 100)
|
|
||||||
|
|
||||||
# Add wallpapers that are larger than the screen size * threshold to list to choose from ($wallpapers)
|
|
||||||
for i in (seq 1 (count $wall_sizes))
|
|
||||||
set -l wall_size (string split ' ' $wall_sizes[$i])
|
|
||||||
if test $wall_size[1] -ge $screen_size[1] -a $wall_size[2] -ge $screen_size[2]
|
|
||||||
set -a wallpapers $unfiltered_wallpapers[$i]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if the $wallpapers list is unset or empty
|
|
||||||
if ! set -q wallpapers || test -z "$wallpapers"
|
|
||||||
error "No valid images found in $wallpapers_dir"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# Choose a random wallpaper from the $wallpapers list
|
|
||||||
set chosen_wallpaper (random choice $wallpapers)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Thumbnail wallpaper for colour gen
|
|
||||||
mkdir -p $C_CACHE/thumbnails
|
|
||||||
set -l thumb_path $C_CACHE/thumbnails/(sha1sum $chosen_wallpaper | cut -d ' ' -f 1).jpg
|
|
||||||
if ! test -f $thumb_path
|
|
||||||
magick -define jpeg:size=256x256 $chosen_wallpaper -thumbnail 128x128 $thumb_path
|
|
||||||
end
|
|
||||||
cp $thumb_path $state_dir/thumbnail.jpg
|
|
||||||
|
|
||||||
# Light/dark mode detection if not specified
|
|
||||||
if ! set -q _flag_T
|
|
||||||
set -l lightness (magick $state_dir/thumbnail.jpg -format '%[fx:int(mean*100)]' info:)
|
|
||||||
test $lightness -ge 60 && set _flag_T light || set _flag_T dark
|
|
||||||
end
|
|
||||||
|
|
||||||
# Generate colour scheme for wallpaper
|
|
||||||
set -l src (dirname (status filename))
|
|
||||||
MODE=$_flag_T $src/scheme/gen-scheme.fish &
|
|
||||||
|
|
||||||
# Store the wallpaper chosen
|
|
||||||
mkdir -p $state_dir
|
|
||||||
echo $chosen_wallpaper > $last_wallpaper_path
|
|
||||||
ln -sf $chosen_wallpaper "$state_dir/current"
|
|
||||||
end
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
active_ws=$(hyprctl activeworkspace -j | jq -r '.id')
|
|
||||||
|
|
||||||
if [[ "$1" == *"group" ]]; then
|
|
||||||
# Move to group
|
|
||||||
hyprctl dispatch "${1::-5}" $((($2 - 1) * 10 + ${active_ws:0-1}))
|
|
||||||
else
|
|
||||||
# Move to ws in group
|
|
||||||
hyprctl dispatch "$1" $((((active_ws - 1) / 10) * 10 + $2))
|
|
||||||
fi
|
|
||||||
Reference in New Issue
Block a user