Python tool for templating and managing dotfiles
I always told myself I would never release this code. For some time I considered the appearance of my desktop environment a form of creative expression, and by putting the tooling I wrote into the hands of others, I might dilute, somehow, the quality of what I had set out to create. As embarrassing as it is to admit this - after all, the purpose of this work was to create a desktop environment that was functional and aesthetically pleasing, and had nothing to do with anyone else's desktop - I hope that by stating my thought processes I can convey how much I had invested in this project.
In this post I'll describe the tooling I wrote to automate the process of adjusting the appearance of the various software I use on a daily basis. Below is a screenshot of my desktop at the time of writing, with a couple applications running:
At a high level, there's nothing unique about my tools versus some of the other template-rendering scripts. The basic idea is the same: given a context, which contains a mapping of variable name to value, take a template file and replace all instances of a variable with it's corresponding value. It's the details of how these scripts work that, in my opinion, makes them particularly useful.
Config values are dynamically resolved
By this I mean that the values associated with a given configuration key can be variables. In the example below, note how the key
foreground refers to
white (which occurs later in the config) - this kind of thing is no problem:
settings: foreground: $white red: '#8c3333' white: '#fefefe' openbox: active: label: $foreground titlebar: $red inactive: label: $openbox.active.label titlebar: $openbox.active.titlebar
As you can see, the values associated with a given key support variables, which are recursively resolved. This is pretty neat and leads to a lot less typing and mistakes!
Master config vs theme configs
In addition to using variables in the configuration file, the tools also differentiate between the master config file and the individual theme configs. By doing some clever work when you save a theme's config file, the tooling only stores values that differ from those in the master config. Essentially, each individual theme stores only a diff against the master config. Here's the kicker: whenever the master config changes, the individual themes will pick up that change (unless they specifically override whichever keys were updated in the master).
In this way, I can make big changes to the look-and-feel of all my individual themes by editing a single file. But I can also choose to override the defaults in any way in the actual theme configuration.
Theme generation from Xresources file
Suppose you're cruising r/unixporn and someone posts the dotfiles to a theme you think is really nice. You'd like to keep the look-and-feel of your own setup, but would like to incorporate the colors they used. Well, you can simply pass in an
.Xdefaults) file and the script parses out the color values and uses them to populate the colors in a new theme config file. This makes it super easy to try out new colors without having to change up everything in your setup.
It might seem like a limitation that the tool only supports YAML or Xresources, but I've had good luck following a unix-ey philosophy and writing other tools to target
Xresources as their output format. For example, I have one script that accepts an image file as input, and outputs 16-colors conforming to a linux terminal color scheme. Another script then takes those colors and turns it into an Xresources-formatted file. Etc. (See end of post for code to these scripts).
Beyond these two features, the rest of the code is a fairly straightforward Jinja2 template renderer wrapped up in some nice APIs. There are also some cool built-in Jinja filters you can use in your templates to do things like:
- Adjust the brightness of a color by a particular amount (useful for dynamically choosing the color for disabled windows, etc).
- Hex to RGB
- Shell quote
- Percent to 16-bit
- Read YAML file into dictionary
Since it's Jinja, you get all the built-in features like template inheritance / fragments, control structures (for loop, conditional, etc), and you can write your own macros or filters very easily.
Obtaining and using the tools
The main script is called
ta (because it's easy to type) and the code, along with a sample config and templates, is hosted on GitHub at github.com/coleifer/dot-theme. All the config and templates should be placed in
ta has a number of sub-commands which I've found useful, and are described below.
In the screenshot below, I'm using
ta to add a new theme named jellybeans, set the current theme to jellybeans, and then list all the themes currently available on my laptop:
In the following screenshot, I'm using the diff sub-command to show any keys that have been overridden in the new theme. Since the only thing extracted from the
.Xresources file were the colors, only the colors are listed in the diff:
Then, since I'm curious about some other settings, I am using
ta show -p <key> to show the values of certain keys (or sub-keys) in the currently-active theme. In the example screenshot, I'm showing the values of the
font key, and the
There are a few more commands that are also useful:
update: re-renders the theme and optionally runs any post-add/post-set hooks you've configured
rm: removes a theme
rename: renames a theme, or with
-x, copies and renames.
get_value: sets or gets the value associated with the given key or sub-key.
Editing theme configs
The tools come with a special script,
te, which is used to edit the theme configs. It wraps a call to
vim and, depending on what changes you make, will automatically re-render the appropriate theme(s). For instance, if I edit the master theme config,
te will automatically re-render all the individual theme's templates, because they might need to inherit new values. Similarly, if I edit an individual theme's config, then only that theme's templates are re-rendered.
Check it out!
If you're interested, I encourage you to dig into the source code and make it your own (or just take the parts that interest you and integrate into your own tooling). I've really found it useful to have
.Xresources files be the inputs to the theme generator. It's a super easy format to target as the output if you want to write other tools, and it's ubiquitous -- typically someone always includes one when posting their dotfiles.
I hope you found this post interesting, and I hope that the code may be useful to you. Happy hacking!
- Source code for
- Script for extracting colors from an image (also check out my post on the subject, if you're interested in details)
- Script for turning list of colors into Xresources
Commenting has been closed, but please feel free to contact me