feat: upd docs, rm save command
This commit is contained in:
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2025 dmitry
|
Copyright (c) 2025 Dmitry Fedotov
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||||
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
||||||
|
|||||||
215
README.md
215
README.md
@@ -1,45 +1,180 @@
|
|||||||
# themer
|
# userctx
|
||||||
|
|
||||||
Apply visual themes to various apps with ease.
|
Manage you configurations and themes with ease.
|
||||||
|
|
||||||
## Kitty
|
## Installation
|
||||||
Themer symlinks ~/.config/kitty/current-theme.conf to the
|
You just need to copy the file to a directory
|
||||||
file found within theme directory.
|
in your $PATH (for example: /usr/loca/bin) and
|
||||||
So:
|
copy/create a config file.
|
||||||
1. touch ~/.config/kitty/current-theme.conf
|
```bash
|
||||||
2. echo 'include current-theme.conf' >> ~/.config/kitty/kitty.conf
|
sudo install userctx.py /usr/local/bin/userctx
|
||||||
If you even called kitten theme 'Some Theme' then
|
mkdir -p ~/.config/userctx
|
||||||
your setup is fine and ready for Themer.
|
cp config.toml ~/.config/userctx
|
||||||
|
|
||||||
## Niri
|
|
||||||
If ~/.config/niri/configs/ dir exists then
|
|
||||||
config is assembled from this dir + theme.
|
|
||||||
Else niri.kdl from theme directory is considered the whole config and
|
|
||||||
Themer symlinks ~/.config/niri/config.kdl to you niri.kdl inside
|
|
||||||
theme directory.
|
|
||||||
|
|
||||||
## Sway
|
|
||||||
Replaces either ~/.config/sway/theme.conf or whole config.
|
|
||||||
|
|
||||||
## Swaybg
|
|
||||||
If using swaybg in other compositor besides sway
|
|
||||||
then it must be a systemd unit for Themer to manage it.
|
|
||||||
|
|
||||||
Put the following to ~/.config/systemd/user/swaybg.service
|
|
||||||
```
|
|
||||||
[Unit]
|
|
||||||
PartOf=graphical-session.target
|
|
||||||
After=graphical-session.target
|
|
||||||
Requisite=graphical-session.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
ExecStart=/usr/bin/swaybg -m fill -i "%h/.config/swaybg/wallpaper"
|
|
||||||
Restart=on-failure
|
|
||||||
```
|
```
|
||||||
|
|
||||||
1. mkdir ~/.config/swaybg
|
## userctx basics
|
||||||
2. ln -s /path/to/any/wallpaper ~/.config/swaybg/wallpaper
|
**userctx** manages configuration *contexts*. A context is a directory somewhere
|
||||||
3. systemctl --user daemon-reload
|
in your system which stores actual configuration files for your apps
|
||||||
4. systemctl --user add-wants niri.service swaybg.service
|
similar to what resides in ~/.config.
|
||||||
|
|
||||||
|
Typically a context directory looks like this.
|
||||||
|
```bash
|
||||||
|
/home/dmitry/.config/userctx/Goldfish
|
||||||
|
├── sway
|
||||||
|
│ └── theme.conf
|
||||||
|
└── wofi
|
||||||
|
└── style.css
|
||||||
|
|
||||||
|
```
|
||||||
|
In the above example, context "Goldfish" contains configs for apps "sway" and "wofi".
|
||||||
|
These configs will be applied when you apply context "Goldfish".
|
||||||
|
|
||||||
|
When a context is applied the app configs from context directory are
|
||||||
|
symlinked you ~/.config/ folder for each managed app. Goldfish/wofi/style.css symlinks to
|
||||||
|
\~/.config/wofi/style.css, Goldfish/sway/theme.conf symlinks to \~/.config/sway/theme.conf etc.
|
||||||
|
|
||||||
|
This default behaviour can be changed and specific actions or customizations
|
||||||
|
can be configured on per-app basis by editing **userctx** config file.
|
||||||
|
The default location for the config is ~/.config/userctx/config.toml.
|
||||||
|
|
||||||
|
**userctx** can also be configured to run scripts, trigger your apps to
|
||||||
|
reload configs etc. For a detailed overview of all config options see below.
|
||||||
|
|
||||||
|
There are two commands **userctx** understands: *list* and *apply*.
|
||||||
|
- *list* lists available contexts in a manner suitable for dmenu-like menu apps
|
||||||
|
- *apply* \<context\> applies named context
|
||||||
|
|
||||||
|
To test your configuration run:
|
||||||
|
```bash
|
||||||
|
userctx --nop apply <context_name>
|
||||||
|
```
|
||||||
|
The program will then only output what it will do without changing
|
||||||
|
anything in your home directory.
|
||||||
|
|
||||||
|
For a quickstart and simple examples jump to "Examples" section below.
|
||||||
|
|
||||||
|
## Configuring userctx
|
||||||
|
TODO: full description of config options.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
### Basic usecase: we only need symlinks
|
||||||
|
Let's add configuration for "foot" terminal emulator, which
|
||||||
|
will be applied when we apply context "Goldfish" assuming
|
||||||
|
we would like to switch foot's visual theme when swithching context.
|
||||||
|
|
||||||
|
1. First we need to create a separate file for visuals config in out
|
||||||
|
context directory.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir ~/.config/userctx/Goldfish/foot/
|
||||||
|
vim ~/.config/userctx/Goldfish/foot/theme.ini
|
||||||
|
```
|
||||||
|
And put the awesome "Tempus Day" into theme.ini:
|
||||||
|
```
|
||||||
|
# -*- conf -*-
|
||||||
|
# theme: Tempus Day
|
||||||
|
# author: Protesilaos Stavrou (https://protesilaos.com)
|
||||||
|
# description: Light theme with warm colours (WCAG AA compliant)
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
foreground = 464340
|
||||||
|
# original background
|
||||||
|
# background = f8f2e5
|
||||||
|
background = ffffff
|
||||||
|
regular0 = 464340
|
||||||
|
regular1 = c81000
|
||||||
|
regular2 = 107410
|
||||||
|
regular3 = 806000
|
||||||
|
regular4 = 385dc4
|
||||||
|
regular5 = b63052
|
||||||
|
regular6 = 007070
|
||||||
|
regular7 = e7e3d7
|
||||||
|
bright0 = 68607d
|
||||||
|
bright1 = b24000
|
||||||
|
bright2 = 427040
|
||||||
|
bright3 = 6f6600
|
||||||
|
bright4 = 0f64c4
|
||||||
|
bright5 = 8050a7
|
||||||
|
bright6 = 336c87
|
||||||
|
bright7 = f8f2e5
|
||||||
|
```
|
||||||
|
You could as well symlink any file (pre-existing theme for foot)
|
||||||
|
to ~/.config/userctx/Goldfish/foot/theme.ini
|
||||||
|
|
||||||
|
2. To include this theme file from main foot config add the following line
|
||||||
|
to $HOME/.config/foot/foot.ini (edit to match your user's homedir):
|
||||||
|
```
|
||||||
|
include=/home/dmitry/.config/foot/theme.ini
|
||||||
|
```
|
||||||
|
Do not forget to remove style settings from foot.ini so they do not
|
||||||
|
conflict with separate theme.ini.
|
||||||
|
|
||||||
|
3. Finally, tell userctx to manage foot config for you.
|
||||||
|
Edit ~/.config/userctx/config.toml and add "foot" to "apps"
|
||||||
|
array in "general" section of the config.
|
||||||
|
```
|
||||||
|
[general]
|
||||||
|
apps = [
|
||||||
|
"foot",
|
||||||
|
"sway",
|
||||||
|
"wofi",
|
||||||
|
]
|
||||||
|
```
|
||||||
|
4. Test you configuration
|
||||||
|
```bash
|
||||||
|
userctx --nop apply Goldfish
|
||||||
|
```
|
||||||
|
The **--nop** (no-op) flag tells **userctx** to perform a dry-run. It will just output
|
||||||
|
what it is going to do when you actually apply context.
|
||||||
|
|
||||||
|
If all looks good - that's it. When you issue **userctx apply Goldfish**
|
||||||
|
a symlink will be created in your homedir:
|
||||||
|
|
||||||
|
~/.config/foot/theme.ini -> ~/.config/userctx/Goldfish/foot/theme.ini
|
||||||
|
|
||||||
|
### More advanced usecase: run command and hot-reload
|
||||||
|
Let's configure **userctx** to apply theme to helix editor.
|
||||||
|
|
||||||
|
1. Similar to the above section, create ~/.config/userctx/Goldfish/helix/helix.toml
|
||||||
|
with the following contents:
|
||||||
|
```toml
|
||||||
|
inherits = "github_light"
|
||||||
|
"ui.background" = {}
|
||||||
|
```
|
||||||
|
Now edit ~/.config/userctx/config.toml.
|
||||||
|
|
||||||
|
Add the "helix" to "general" section.
|
||||||
|
```toml
|
||||||
|
[general]
|
||||||
|
apps = [
|
||||||
|
"foot",
|
||||||
|
"sway",
|
||||||
|
"helix",
|
||||||
|
"wofi",
|
||||||
|
]
|
||||||
|
```
|
||||||
|
2. Add "apps.helix" section:
|
||||||
|
```toml
|
||||||
|
[apps.helix]
|
||||||
|
symlink."*" = "themes/current_theme.toml"
|
||||||
|
exec = """sed -i -E 's/^theme = (.+)/theme = "current_theme"/' ~/.config/helix/config.toml"""
|
||||||
|
reload = "pkill -USR1 hx"
|
||||||
|
```
|
||||||
|
Here we instruct **userctx** to symlink any (single) file it finds in "helix" subdirectory of context folder
|
||||||
|
to ~/.config/helix/themes/current_theme.toml
|
||||||
|
|
||||||
|
Then we run sed to change the config file. This part is not really necessare if helix is
|
||||||
|
configured to use theme named "current_theme" and you're sure that config won't change.
|
||||||
|
We could just replace the file and issue USR1. The sed part if for the case when config
|
||||||
|
is changed by user or other app.
|
||||||
|
|
||||||
|
Finally we set the reload command which
|
||||||
|
will tell helix to reload config.
|
||||||
|
|
||||||
|
3. Test your configuration,
|
||||||
|
```bash
|
||||||
|
userctx --nop apply Goldfish
|
||||||
|
```
|
||||||
|
|
||||||
|
See also template "config.toml" with numerous app settings.
|
||||||
|
|
||||||
https://github.com/YaLTeR/niri/wiki/Example-systemd-Setup
|
|
||||||
|
|||||||
58
TODO.md
58
TODO.md
@@ -1,58 +0,0 @@
|
|||||||
How this must work.
|
|
||||||
|
|
||||||
1. On first launch userctx reads ~/.config/userctx/config and aquires list of apps it should manage
|
|
||||||
2. userctx backs up whole config directories to ~/.config/userctx so further manipulations are safe
|
|
||||||
Default name is userctx/original, but user can provide a different name from command line.
|
|
||||||
|
|
||||||
example:
|
|
||||||
userctx backup [--name <name>] [--apps TODO]
|
|
||||||
default name is "original"
|
|
||||||
default apps list is read from config. command line overrides config completely, no merging.
|
|
||||||
|
|
||||||
Backup algorithm:
|
|
||||||
- If directory ~/.config/sway does not exist or is empty will do nothing
|
|
||||||
- if directory exists AND not empty then create directory ~/.config/userctx/original/sway
|
|
||||||
- copy contents of all files AND direcotries in it to ~/.config/userctx/original/sway
|
|
||||||
- if any file failed to copy - rollback and remove ~/.config/userctx/original/sway
|
|
||||||
- remove original files (and directories) from ~/.config/sway
|
|
||||||
- create symlinks to copied files in ~/.config/sway
|
|
||||||
|
|
||||||
3. Swithing algorithm:
|
|
||||||
User requests theme change with:
|
|
||||||
userctx apply <theme>
|
|
||||||
|
|
||||||
userctx tries to apply configs as follows:
|
|
||||||
- looks up a list of apps it should manage; if list is empty - return non-zero
|
|
||||||
- looks up requested theme name in ~/.config/userctx/<name>; if not found - return non-zero
|
|
||||||
- looks up config dir ~/.config/userctx/<theme>/<app>; if none found - return non-zero
|
|
||||||
- for each <filename> in ~/.config/userctx/<theme>/<app> userctx does
|
|
||||||
```
|
|
||||||
mkdir -p ~/.config/<app> # creates app config directory if it does not exist (else no-op)
|
|
||||||
rm -f ~/.config/<app>/<filename> # tries to delete the config if it exists (else no-op)
|
|
||||||
ln -s ~/.config/userctx/<theme>/<app>/<filename> ~/.config/<app>/<filename> # symlinks the file from requested theme user dir
|
|
||||||
```
|
|
||||||
- after creating symlinks userctx tries to let the app know that config changed
|
|
||||||
- if app has hot-reload (niri, alacritty, hyprland) - do nothing
|
|
||||||
- if app re-reads configs on start (fuzzel, wofi) - do nothing
|
|
||||||
- if app obeys USR1/URS2 - issue the signal
|
|
||||||
- if app is a systemd service - exec systemctl --user restart <app>
|
|
||||||
|
|
||||||
This way a single copy of config files are kept in ~/.config/userctx.
|
|
||||||
This way user can choose which files to manage themselves and which userctx
|
|
||||||
should manage.
|
|
||||||
|
|
||||||
4. cli interface
|
|
||||||
userctx list - returns newline-separated list (suitable for dmenu)
|
|
||||||
userctx apply $(userctx list | fuzzel -d) - a usable way to select theme with dmenu-like apps
|
|
||||||
|
|
||||||
TODO:
|
|
||||||
|
|
||||||
0. Update README.md, make app usable for a random person on the internet.
|
|
||||||
1. implement backup
|
|
||||||
2. parse cli args in a decent way
|
|
||||||
3. implement behaviour discussed above
|
|
||||||
4. Think of config options: configure ability to merge several files; map contents of theme dir to specific files
|
|
||||||
|
|
||||||
Wider TODO:
|
|
||||||
|
|
||||||
1. Think of this app as a user context switcher, not only theme manager.
|
|
||||||
15
userctx.py
15
userctx.py
@@ -2,10 +2,9 @@
|
|||||||
import os, sys, subprocess, tomllib, argparse
|
import os, sys, subprocess, tomllib, argparse
|
||||||
|
|
||||||
COMMAND_LIST = 'list'
|
COMMAND_LIST = 'list'
|
||||||
COMMAND_LOAD = 'load'
|
COMMAND_APPLY = 'apply'
|
||||||
COMMAND_SAVE = 'save'
|
|
||||||
COMMAND_CTX_NAME = 'context_name'
|
COMMAND_CTX_NAME = 'context_name'
|
||||||
COMMANDS_ALL = [COMMAND_LIST, COMMAND_LOAD, COMMAND_SAVE]
|
COMMANDS_ALL = [COMMAND_LIST, COMMAND_APPLY]
|
||||||
|
|
||||||
SECTION_GENERAL = 'general'
|
SECTION_GENERAL = 'general'
|
||||||
KEY_APPS = 'apps'
|
KEY_APPS = 'apps'
|
||||||
@@ -269,11 +268,9 @@ class Runner(object):
|
|||||||
p.add_argument('-n', '--nop', action='store_true', default=False,
|
p.add_argument('-n', '--nop', action='store_true', default=False,
|
||||||
help='do nothing, just print out what userctx will do')
|
help='do nothing, just print out what userctx will do')
|
||||||
subp = p.add_subparsers(help='command to execute', dest='command')
|
subp = p.add_subparsers(help='command to execute', dest='command')
|
||||||
loadp = subp.add_parser(COMMAND_LOAD, help='load context (provide name of context)')
|
applyp = subp.add_parser(COMMAND_APPLY, help='load context (provide name of context)')
|
||||||
loadp.add_argument(COMMAND_CTX_NAME, help='name of context to load')
|
applyp.add_argument(COMMAND_CTX_NAME, help='name of context to load')
|
||||||
listp = subp.add_parser(COMMAND_LIST, help='list available contexts')
|
listp = subp.add_parser(COMMAND_LIST, help='list available contexts')
|
||||||
savep = subp.add_parser(COMMAND_SAVE, help='save current context (provide name of context)')
|
|
||||||
savep.add_argument(COMMAND_CTX_NAME, help='name of context to save')
|
|
||||||
try: # just in case
|
try: # just in case
|
||||||
args = p.parse_args()
|
args = p.parse_args()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -288,10 +285,8 @@ class Runner(object):
|
|||||||
ctxmgr = ContextManager(config)
|
ctxmgr = ContextManager(config)
|
||||||
if args.command == COMMAND_LIST:
|
if args.command == COMMAND_LIST:
|
||||||
ctxmgr.print_contexts_list()
|
ctxmgr.print_contexts_list()
|
||||||
elif args.command == COMMAND_LOAD:
|
elif args.command == COMMAND_APPLY:
|
||||||
ctxmgr.load_context(args.__dict__[COMMAND_CTX_NAME]) # guaranteed to be present by argparse
|
ctxmgr.load_context(args.__dict__[COMMAND_CTX_NAME]) # guaranteed to be present by argparse
|
||||||
elif args.command == COMMAND_SAVE:
|
|
||||||
raise Exception('"save" not implemented')
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'error executing command:', e)
|
print(f'error executing command:', e)
|
||||||
return 3
|
return 3
|
||||||
|
|||||||
Reference in New Issue
Block a user