initial commit (post-repo scrub)

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

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"
}
}