1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
import subprocess
import tempfile
from pathlib import Path
from caelestia.utils.paths import c_state_dir, 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:
state = c_state_dir / "sequences.txt"
state.parent.mkdir(parents=True, exist_ok=True)
state.write_text(sequences)
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)
|