initial commit (post-repo scrub)

This commit is contained in:
Sam G. 2024-04-19 15:50:31 -07:00
commit 5da45c97d8
11 changed files with 762 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
docs/_build
.python-version

233
README.md Normal file
View File

@ -0,0 +1,233 @@
# local-config
The `local-config` project is an attempt at wrangling the complexity of configuring many
applications across one's Linux system. It provides a simple operational model for pulling
many application config files into one place, as well as generating/setting color schemes
across apps.
Quick terminology rundown for theme-related items:
- **Theme**: loose term referring generally to the overall aesthetic of a visual setting.
Ignoring stylistic changes (only applicable to some apps; example here might be a
a particular setting of the `waybar` layout), a theme is often just the wrapper term for
a choice of color _palette_ and _scheme_. For example, "tone4-light" could be a _theme_
setting for an app like `kitty`, referring to both a palette and scheme.
- **Palette**: a set of base colors used to style text or other aspects of an app's
displayed assets
- **Scheme**: an indication of lightness, typically either "light" or "dark.
As far as managing settings across apps, there are current two useful classifications
here:
1. **Inseparable from theme**: some apps (e.g., `sway`, `waybar`) have color scheme
components effectively built in to their canonical configuration file. This can make it
hard to set themes dynamically, as it would likely require some involved
matching/substitution rules. This is not a level of complexity I'm willing to embrace,
so we simply split the config files according to theme and/or scheme.
2. **Can load an external theme file**: some apps (e.g., `kitty`) have a clear mechanism
for loading themes. This typically implies some distinct color format, although usually
somewhat easy to generate (don't have to navigate non-color settings, for instance).
Such apps allow for an even less "invasive" config swapping process when setting a new
theme, as one can just swap out the external theme file.
To be clear on operation implications here: apps of type (1) must have _manually
maintained_ config variations according the desired themes. General theme settings must
follow the naming scheme `<app-name>-<palette>-<scheme>.<ext>`. For example, if I wanted to set
`sway` to a light variation (which, at the time of writing, would just entail changing a
single background color), I must have explicitly created a `sway-tone4-light.conf` file
that captures this setting. The canonical config file will then be symlinked to the
theme-specific file when the theme is set. (Note that the palette in this example is pretty much
irrelevant, but it needs to be present in order to match the overarching setting; here you
can just think of the format being `<app-name>-<theme>.<ext>`, where `tone4-light` is the
provided theme name.)
For apps of type (2), the canonical config file can remain untouched so long as it refers
to a fixed, generic theme file. For example, with `kitty`, my config file can point to a
`current-theme.conf` file, which will be symlinked to a specific theme file here in
`local-config` when a change is requested. This enables a couple of conveniences:
- The true config directory on disk remains unpolluted with theme variants.
- If the set theme is regenerated, there is no intervention necessary to propagate its
changes to the target app. The symlinked file itself will be updated when the theme
does, ensuring the latest theme version is always immediately available and pointed to
by the app.
Keep in mind that some apps may fall into some grey area here, allowing some external
customization but locking down other settings internally. In such instances, there's no
need to overcomplicate things; just stick to explicit config variants under the type (1)
umbrella. Type (2) only works for generated themes anyhow; even if the target app can load
an external theme, type (1) should be used if preset themes are fixed.
## Naming standards
To keep things simple, we use a few fixed naming standards for setting app config files
and their themed counterparts. The app registry requires each theme-eligible app to
provide a config directory (`config_dir`), containing some canonical config file
(`config_file`) and to serve as a place for theme-specific config variations. The
following naming schemes must be used in order for theme switching to behave
appropriately:
- When setting a theme for a particular app, the following variables will be available:
* `<app-name>`
* `<palette>`
* `<scheme>`
- For apps with `external_theme = False`, config variants must named as
`<app-name>-<palette>-<scheme>.<ext>`, where `<ext>` is the app's default config file
extension.
- For apps with `external_theme = True`, the file `<config-dir>/current-theme.conf` will
be used when symlinking the requested theme. The config file thus must point to this
file in order to change with the set theme.
Additionally, the theme symlink will be created from the file
```
<local-config-root>/local-config/themes/<palette>/apps/<app-name>/generated/<scheme>.conf
```
to `<config-dir>/current-theme.conf`.
## Directory structure
- `local-config/`: main repo directory
* `config/`: app-specific configuration files. Each folder inside this directory is
app-specific, and the target of associated copy operations when a config sync is
performed. Nothing in this directory should pertain to any repo functionality; it
should only contain config files that originated elsewhere on the system.
* `themes/`: app-independent theme data files. Each folder in this directory should
correspond to a specific color palette and house any relevant color spec files
(currently likely be a `colors.json`). Also servers the output location for
generated theme files
* `<palette>/colors.json`: JSON formatted color key-value pairings for palette
colors. There's no standard here aside from the filename and format; downstream
app-specific TOML templates can be dependent on any key naming scheme within the
JSON.
+ `<palette>/apps/<app-name>/templates/`: houses the TOML maps for the color
palette `<palette>` under app `<app-name>`. Files `<fname>.toml` will be mapped to
`<fname>.conf` in the theme output folder (below), so ensure the naming
standards align with those outlined above.
+ `<palette>/apps/<app-name>/generated/`: output directory for generated scheme
variants. These are the symlink targets for dynamically set external themes.
* `app_registry.toml`: global application "registry" used by sync and theme-setting
scripts. This lets apps be dynamically added or removed from being eligible for
config-related operations.
## Scripts
`set_theme.py`: sets a theme across select apps.
- Applies to specific app with `-a <app>` , or to all apps in the `app_registry.toml` with
`-a "*"`.
- Uses symlinks to set canonical config files to theme-based variations. Which files get
set depends on the _app type_ (see above), which really just boils down to whether
theming (1) can be specified with an external format, and (2) if it depends on
auto-generated theme files from within `local-config`.
- Palette and scheme are specified as expected. They are used to infer proper paths
according to naming and structure standards.
- Real config files will never be overwritten. To begin setting themes with the script,
you must delete the canonical config file expected by the app (and specified in the app
registry) to allow the first symlink to be set. From there on out, symlinks will be
automatically flushed.
- A report will be provided on which apps were successfully set to the requested theme,
along with the file stems. A number of checks are in place for the existence of involved
files and directories. Overall, the risk of overwritting a real config file is low; we
only flush existing symlinks, and if the would-be target for the requested theme (be it
from an auto-generated theme file, or from a manually manage config variant) doesn't
exist, that app's config will be completed skipped. Essentially, everything must be in
perfect shape before the symlink trigger is officially pulled.
`gen_theme.py`: generates theme files for palettes by mapping their color definitions
through app-specific templates. These templates specific how to relate an app's theme
variables to the color names provided by the template.
- An app and palette are the two required parameters. If no template or output paths are
provided, they will be inferred according to the theme path standards seen above.
- The `--template` argument can be a directory or a file, depending on what theme files
you'd like to render.
- The `--output` path, if specified, must be a directory. Generated theme files take on
a name with the same stem as their source template, but using the `.conf` extension.
- The TOML templates should make config variable names to JSON dot-notation accessors. If
color definitions are nested, the dot notation should be properly expanded by the script
when mapping the colors to keyword values.
- There are a number of checks for existing paths, even those inferred (e.g., template and
output) from the palette and app. If the appropriate setup hasn't been followed, the
script will fail. Make sure the `theme` folder in question and it's nested `app`
directory are correctly setup before running the script. (Perhaps down the line there
are some easy auto-setup steps to take here, but I'm not making that jump now.)
- TODO: open up different app "writers," or make it easy to extend output syntax based on
the app in question. This would like be as simple as mapping app names to
line-generating functions, which accept the keyword and color (among other items). This
can be fleshed out as needed.
`sync.sh`: copies relevant configuration files from local paths into the `local-config`
subpath. Markdown files in the docs directory then reference the local copies of these
files, meaning the documentation updates dynamically when the configuration files do. That
is, the (possibly extracted) config snippets will change with the current state of my
system config without any manual intervention of the documentation files.
### Specific theme-setting example
To make clear how the theme setting script works on my system, the following breaks down
exactly what steps are taken to exert as much scheme control as possible. Everything at this
point is wrapped up in a single `make set-<palette>-<scheme>` call; suppose we're
currently running the dark scheme (see first image) and I run `make set-tone4-light`:
![
Starting point; have a GTK app (GNOME files), `kitty`, and Firefox (with the
system-dependent default theme set). In Firefox, I have open `localsys` with its
scheme-mode to set to "auto," which should reflect the theme setting picked up by the
browser (and note the white tab icon).
](_static/set-theme-1.png)
_(Starting point; have a GTK app (GNOME files), `kitty`, and Firefox (with the
system-dependent default theme set). In Firefox, I have open `localsys` with its
scheme-mode to set to "auto," which should reflect the theme setting picked up by the
browser (and note the white tab icon).)_
1. `set_theme.py` is invoked. Global settings are applied first, based on my OS (`Linux`),
which calls
```
gsettings set org.gnome.desktop.interface color-scheme 'prefer-light'
```
controlling settings for GTK apps and other `desktop-portal`-aware programs. This
yields the following:
![Portal-aware apps changed, config apps not yet set](_static/set-theme-2.png)
_(Portal-aware apps changed, config apps not yet set. Scheme-aware sites are updated
without page refresh.)_
2. Specific application styles are set. For now the list is small, including `kitty`,
`waybar`, and `sway`. `kitty` is the only type (2) application here, whereas the other
two are type (1).
a. For the type (1) apps, the canonical config files as specified in the app registry
are symlinked to their light variants. For `sway`, this is `~/.config/sway/config`,
and if we look at the `file`:
```sh
.config/sway/config: symbolic link to ~/.config/sway/sway-tone4-light
```
b. For the type (2) apps, just the `current-theme.conf` file is symlinked to the
relevant palette-scheme file. `kitty` is such an app, with a supported theme file
for `tone4`, and those files have been auto-generated via `gen_theme.py`. Looking at
this file under the `kitty` config directory:
```sh
.config/kitty/current-theme.conf: symbolic link to ~/Documents/projects/local-config/local-config/themes/tone4/apps/kitty/generated/light.conf
```
The `kitty.conf` file isn't changed, as all palette-related items are specified in
the theme file. (Note that the general notion of a "theme" could include changes to
other stylistic aspects, like the font family; this would likely require some hybrid
type 1-2 approach not yet implemented).
3. Live application instances are reloaded, according to the registered `refresh_cmd`s.
Here the apps with style/config files set in step (2) are reloaded to reflect those
changes. Again, in this example, this is `kitty`, `sway`, and the `waybar`.
![Final light setting: portal-dependent apps _and_ config-based apps changed](_static/set-theme-3.png)
_(Final light setting: portal-dependent apps _and_ config-based apps changed)_
4. `set_theme.py` provides a report for the actions taken; in this case, the following was
printed:
![`set_theme.py` output](_static/set-theme-4.png)
_(`set_theme.py` output)_

View File

@ -0,0 +1,42 @@
# App registry -- apps eligible for theme switching
#
# Each app must be register under the "app" directive, i.e., as "[app.<app-name>]"
#
# Option details:
# - external_theme: if False (default), indicates an app of type 1 as per the README. That
# is, an external theme file cannot be used, and theme switching will involve switch the
# canonical config setting. If True, the app's theme can be set through an external theme
# file.
# - support_os: OSes that support theme switching according to the implemented paths.
# Accepts a list of `uname -s` strings to match system.
# - refresh_cmd: a command to run for live refreshing the application's color scheme after
# it's been set for running instances.
#
# Default example
# [app.default]
# external_theme = False
# config_dir = '~/.config/default/'
# config_file = 'default.conf'
# refresh_cmd = 'app reload-config'
#
[app.kitty]
external_theme = true
config_dir = '~/.config/kitty/'
config_file = 'kitty.conf'
supported_oses = ['Linux', 'Darwin']
refresh_cmd = 'kill -s USR1 $(pgrep kitty)'
[app.sway]
external_theme = false
config_dir = '~/.config/sway/'
config_file = 'config'
supported_oses = ['Linux']
#refresh_cmd = 'swaymsg reload'
[app.waybar]
external_theme = false
config_dir = '~/.config/waybar/'
config_file = 'style.css'
supported_oses = ['Linux']
refresh_cmd = 'swaymsg reload'

108
autoconf/gen_theme.py Normal file
View File

@ -0,0 +1,108 @@
import argparse
import inspect
import json
import tomllib as toml
from pathlib import Path
def get_running_path():
calling_module = inspect.getmodule(inspect.stack()[-1][0])
return Path(calling_module.__file__).parent
parser = argparse.ArgumentParser(
description='Generate theme files for various applications. Uses a template (in TOML ' \
+ 'format) to map application-specific config keywords to colors (in JSON ' \
+ 'format).'
)
parser.add_argument(
'-a', '--app',
required=True,
help='Application target for theme. Supported: ["kitty"]'
)
parser.add_argument(
'-p', '--palette',
required=True,
help='Palette to use for template mappings. Uses local "theme/<palette>/colors.json".'
)
parser.add_argument(
'-t', '--template',
default=None,
help='Path to TOML template file. If omitted, app\'s default template path is used.' \
+ 'If a directory is provided, all TOML files in the folder will be used.'
)
parser.add_argument(
'-o', '--output',
default=None,
help='Output file path for theme. If omitted, app\'s default theme output path is used.'
)
args = parser.parse_args()
# separation sequences to use base on app
app_sep_map = {
'kitty': ' ',
}
def generate_theme_files():
basepath = get_running_path()
# set arg conditional variables
palette_path = Path(basepath, 'themes', args.palette)
colors_path = Path(palette_path, 'colors.json')
theme_app = args.app
template_path = None
output_path = None
if args.template is None:
template_path = Path(palette_path, 'apps', theme_app, 'templates')
else:
template_path = Path(args.template).resolve()
if args.output is None:
output_path = Path(palette_path, 'apps', theme_app, 'generated')
else:
output_path = Path(args.output).resolve()
# check paths
if not colors_path.exists():
print(f'Resolved colors path [{colors_path}] doesn\'t exist, exiting')
return
if not template_path.exists():
print(f'Template path [{template_path}] doesn\'t exist, exiting')
return
if not output_path.exists() or not output_path.is_dir():
print(f'Output path [{output_path}] doesn\'t exist or not a directory, exiting')
return
print(f'Using palette colors [{colors_path}]')
print(f'-> with templates in [{template_path}]')
print(f'-> to output path [{output_path}]\n')
# load external files (JSON, TOML)
colors_json = json.load(colors_path.open())
# get all matching TOML files
template_list = [template_path]
if template_path.is_dir():
template_list = template_path.rglob('*.toml')
for template_path in template_list:
template_toml = toml.load(template_path.open('rb'))
# lookup app-specific config separator
config_sep = app_sep_map.get(theme_app, ' ')
output_lines = []
for config_key, color_key in template_toml.items():
color_value = colors_json
for _key in color_key.split('.'):
color_value = color_value.get(_key, {})
output_lines.append(f'{config_key}{config_sep}{color_value}')
output_file = Path(output_path, template_path.stem).with_suffix('.conf')
output_file.write_text('\n'.join(output_lines))
print(f'[{len(output_lines)}] lines written to [{output_file}] for app [{theme_app}]')
if __name__ == '__main__':
generate_theme_files()

145
autoconf/set_theme.py Normal file
View File

@ -0,0 +1,145 @@
import argparse
import inspect
import subprocess
import json
import os
import tomllib as toml
from pathlib import Path
from colorama import Fore
parser = argparse.ArgumentParser(
description='Generate theme files for various applications. Uses a template (in TOML ' \
+ 'format) to map application-specific config keywords to colors (in JSON ' \
+ 'format).'
)
parser.add_argument(
'-p', '--palette',
required=True,
help='Palette name, must match a folder in themes/'
)
parser.add_argument(
'-s', '--scheme',
required=True,
help='Preferred lightness scheme, either "light" or "dark".'
)
parser.add_argument(
'-a', '--app',
required=True,
help='Application target for theme. App must be present in the registry. ' \
+ 'Use "*" to apply to all registered apps'
)
args = parser.parse_args()
def get_running_path():
calling_module = inspect.getmodule(inspect.stack()[-1][0])
return Path(calling_module.__file__).parent
def os_scheme_settings(scheme):
'''
Groups of settings/commands to invoke globally based on the provided `scheme`. This
may control things like default app light/dark behavior, for instance.
'''
os_cmd_groups = {
'Linux': [
"gsettings set org.gnome.desktop.interface color-scheme 'prefer-{scheme}'",
],
'Darwin': [],
}
if scheme not in ['light', 'dark']: return
osname = os.uname().sysname
cmd_list = os_cmd_groups.get(osname, [])
for cmd in cmd_list:
subprocess.check_call(cmd.format(scheme=scheme).split())
def update_theme_settings():
osname = os.uname().sysname
basepath = get_running_path()
app_registry = toml.load(Path(basepath, 'app_registry.toml').open('rb'))
app_registry = app_registry.get('app', {})
if args.app not in app_registry and args.app != '*':
print(f'App {args.app} not registered, exiting')
return
app_list = []
if args.app == '*':
app_list = list(app_registry.items())
else:
app_list = [(args.app, app_registry[args.app])]
links_succ = {}
links_fail = {}
for app_name, app_settings in app_list:
config_dir = Path(app_settings['config_dir']).expanduser()
config_file = app_settings['config_file']
config_path = Path(config_dir, config_file)
if osname not in app_settings['supported_oses']:
print(f'OS [{osname}] not support for app [{app_name}]')
continue
if app_settings['external_theme']:
# symlink from "current-theme.conf" in app's config-dir ...
from_conf_path = Path(config_dir, 'current-theme.conf')
# ... to appropriate generated theme path here in local-config
to_conf_path = Path(
basepath,
f'themes/{args.palette}/apps/{app_name}/generated/{args.scheme}.conf'
)
else:
# symlink from the canonical config file ...
from_conf_path = config_path
# ... to appropriate theme variant
to_conf_path = Path(
config_dir,
f'{app_name}-{args.palette}-{args.scheme}{config_path.suffix}'
)
if not to_conf_path.exists():
print(
f'Expected symlink target [{to_conf_path}] doesn\'t exist, skipping'
)
links_fail[app_name] = (from_conf_path.name, to_conf_path.name)
continue
# if config file being symlinked exists & isn't already a symlink (i.e.,
# previously set by this script), throw an error.
if from_conf_path.exists() and not from_conf_path.is_symlink():
print(
f'Symlink origin [{from_conf_path}] exists and isn\'t a symlink; please ' \
+ 'first manually remove this file so this script can set the symlink.'
)
links_fail[app_name] = (from_conf_path.name, to_conf_path.name)
continue
else:
# if path doesn't exist, or exists and is symlink, remove the symlink in
# preparation for the new symlink setting
from_conf_path.unlink(missing_ok=True)
print(f'Linking [{from_conf_path}] -> [{to_conf_path}]')
# run color scheme live-reload for app, if available
# TODO: store the status of this cmd & print with the messages
if 'refresh_cmd' in app_settings:
subprocess.check_call(app_settings['refresh_cmd'], shell=True)
from_conf_path.symlink_to(to_conf_path)
links_succ[app_name] = (from_conf_path.name, to_conf_path.name)
for app, (from_p, to_p) in links_succ.items():
print(Fore.GREEN + f'> {app} :: {from_p} -> {to_p}')
for app, (from_p, to_p) in links_fail.items():
print(Fore.RED + f'> {app} :: {from_p} -> {to_p}')
if __name__ == '__main__':
os_scheme_settings(args.scheme)
update_theme_settings()

View File

@ -0,0 +1,21 @@
background #1b1b1b
foreground #c6c6c6
selection_background #303030
selection_foreground #ababab
cursor #d4d4d4
color0 #262626
color8 #262626
color1 #ed625d
color9 #ed625d
color2 #34a543
color10 #34a543
color3 #a68f03
color11 #a68f03
color4 #618eff
color12 #618eff
color5 #ed625d
color13 #ed625d
color6 #618eff
color14 #618eff
color7 #d4d4d4
color15 #d4d4d4

View File

@ -0,0 +1,21 @@
background #f1f1f1
foreground #262626
selection_background #ababab
selection_foreground #303030
cursor #262626
color0 #1b1b1b
color8 #1b1b1b
color1 #c25244
color9 #c25244
color2 #298835
color10 #298835
color3 #897600
color11 #897600
color4 #4e75d4
color12 #4e75d4
color5 #c25244
color13 #c25244
color6 #4e75d4
color14 #4e75d4
color7 #d4d4d4
color15 #d4d4d4

View File

@ -0,0 +1,40 @@
# base settings
background = 'black.bg+1'
foreground = 'black.fg+3'
selection_background = 'black.bg+3'
selection_foreground = 'black.m+4'
cursor = 'black.fg+2'
# black
color0 = 'black.bg+2'
color8 = 'black.bg+2'
# red
color1 = 'red.m+2'
color9 = 'red.m+2'
# green
color2 = 'green.m+2'
color10 = 'green.m+2'
# yellow
color3 = 'yellow.m+2'
color11 = 'yellow.m+2'
# blue
color4 = 'blue.m+2'
color12 = 'blue.m+2'
# purple (red)
color5 = 'red.m+2'
color13 = 'red.m+2'
# cyan (blue)
color6 = 'blue.m+2'
color14 = 'blue.m+2'
## white
color7 = 'black.fg+2'
color15 = 'black.fg+2'

View File

@ -0,0 +1,40 @@
# base settings
background = 'black.fg+1'
foreground = 'black.bg+2'
selection_background = 'black.m+4'
selection_foreground = 'black.bg+3'
cursor = 'black.bg+2'
# black
color0 = 'black.bg+1'
color8 = 'black.bg+1'
# red
color1 = 'red.m+0'
color9 = 'red.m+0'
# green
color2 = 'green.m+0'
color10 = 'green.m+0'
# yellow
color3 = 'yellow.m+0'
color11 = 'yellow.m+0'
# blue
color4 = 'blue.m+0'
color12 = 'blue.m+0'
# purple (red)
color5 = 'red.m+0'
color13 = 'red.m+0'
# cyan (blue)
color6 = 'blue.m+0'
color14 = 'blue.m+0'
## white
color7 = 'black.fg+2'
color15 = 'black.fg+2'

View File

@ -0,0 +1,107 @@
{
"red": {
"bg+0": "#1c0d0a",
"bg+1": "#2d1412",
"bg+2": "#401a17",
"bg+3": "#531f1d",
"bg+4": "#652624",
"m-4": "#782d2b",
"m-3": "#8b3633",
"m-2": "#9d3e3b",
"m-1": "#b14643",
"m+0": "#c25244",
"m+1": "#d95854",
"m+2": "#ed625d",
"m+3": "#f27870",
"m+4": "#f68c83",
"fg+4": "#faa097",
"fg+3": "#fdb3ab",
"fg+2": "#fec6c0",
"fg+1": "#ffdad3",
"fg+0": "#ffede9"
},
"yellow": {
"bg+0": "#151100",
"bg+1": "#211b00",
"bg+2": "#2d2500",
"bg+3": "#393000",
"bg+4": "#453b00",
"m-4": "#524600",
"m-3": "#5f5100",
"m-2": "#6d5d00",
"m-1": "#7a6900",
"m+0": "#897600",
"m+1": "#978200",
"m+2": "#a68f03",
"m+3": "#b29c34",
"m+4": "#bfaa53",
"fg+4": "#cbb770",
"fg+3": "#d6c58c",
"fg+2": "#e1d3a9",
"fg+1": "#ebe2c5",
"fg+0": "#f5f0e2"
},
"green": {
"bg+0": "#081406",
"bg+1": "#0e1f0c",
"bg+2": "#0f2c10",
"bg+3": "#103814",
"bg+4": "#114518",
"m-4": "#15521d",
"m-3": "#1a5f23",
"m-2": "#1e6c29",
"m-1": "#247a2f",
"m+0": "#298835",
"m+1": "#2e973b",
"m+2": "#34a543",
"m+3": "#56b15a",
"m+4": "#71bc72",
"fg+4": "#8ac789",
"fg+3": "#a2d3a0",
"fg+2": "#badeb8",
"fg+1": "#d1e9cf",
"fg+0": "#e8f4e7"
},
"blue": {
"bg+0": "#0f101b",
"bg+1": "#151b2f",
"bg+2": "#1a2544",
"bg+3": "#202f59",
"bg+4": "#263a6e",
"m-4": "#2d4582",
"m-3": "#355196",
"m-2": "#3d5daa",
"m-1": "#4669bf",
"m+0": "#4e75d4",
"m+1": "#5781ea",
"m+2": "#618eff",
"m+3": "#7b9bff",
"m+4": "#91a9ff",
"fg+4": "#a6b7ff",
"fg+3": "#b9c5ff",
"fg+2": "#cbd3ff",
"fg+1": "#dde1ff",
"fg+0": "#eef0fe"
},
"black": {
"bg+0": "#111111",
"bg+1": "#1b1b1b",
"bg+2": "#262626",
"bg+3": "#303030",
"bg+4": "#3b3b3b",
"m-4": "#474747",
"m-3": "#525252",
"m-2": "#5e5e5e",
"m-1": "#6a6a6a",
"m+0": "#777777",
"m+1": "#848484",
"m+2": "#919191",
"m+3": "#9e9e9e",
"m+4": "#ababab",
"fg+4": "#b9b9b9",
"fg+3": "#c6c6c6",
"fg+2": "#d4d4d4",
"fg+1": "#e2e2e2",
"fg+0": "#f1f1f1"
}
}

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
sphinx
furo
myst-parser