lightdark 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
lightdark/__init__.py ADDED
File without changes
lightdark/__main__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .main import main
2
+
3
+ main()
lightdark/main.py ADDED
@@ -0,0 +1,81 @@
1
+ import argparse
2
+ import json
3
+ from pathlib import Path
4
+ import logging
5
+
6
+ import atomicwrites
7
+ import mergedeep
8
+ from platformdirs import PlatformDirs
9
+ from . import modules as mods
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ def main():
15
+ ap = argparse.ArgumentParser()
16
+ ap.add_argument("-t", "--theme", help="set to this theme instead of cycling")
17
+ ap.add_argument("-k", "--keep", help="keep the same theme instead of cycling", action="store_true")
18
+ ap.add_argument("-v", "--verbose", help="show errors from module configuration", action="store_true")
19
+ args = ap.parse_args()
20
+
21
+ pd = PlatformDirs(appname="lightdark")
22
+ config_dirs = [Path(d) for d in pd.iter_config_dirs()]
23
+ for d in config_dirs:
24
+ if (state_path := d / "current.txt").exists():
25
+ break
26
+ else:
27
+ state_path = config_dirs[0] / "current.txt"
28
+
29
+ config_dicts = []
30
+ config_paths = []
31
+ for d in config_dirs[::-1]:
32
+ config_paths.append(config_path := d / "config.json")
33
+ if config_path.exists():
34
+ try:
35
+ config_dicts.append(json.loads(config_path.read_bytes()))
36
+ except Exception:
37
+ logger.warning("failed to read", extra=dict(data_path=str(config_path)))
38
+
39
+ if not config_dicts:
40
+ raise RuntimeError(f"config.json not found, paths searched: {config_paths!r}")
41
+
42
+ try:
43
+ old_theme = state_path.read_bytes().decode("utf-8").strip()
44
+ except Exception:
45
+ old_theme = None
46
+
47
+ mergedeep.merge((config := {}), *config_dicts[::-1])
48
+
49
+ if args.keep:
50
+ new_theme = old_theme
51
+ elif args.theme:
52
+ new_theme = args.theme
53
+ else:
54
+ cycle = config["cycle"]
55
+ try:
56
+ i = (cycle.index(old_theme) + 1) % len(cycle)
57
+ except ValueError:
58
+ i = 0
59
+ new_theme = cycle[i]
60
+
61
+ with atomicwrites.atomic_write(state_path, mode="wt", encoding="utf-8", overwrite=True) as f:
62
+ f.write(new_theme)
63
+
64
+ theme_config = config["themes"][new_theme]
65
+
66
+ # now apply the theme:
67
+ utility = mods.Utility()
68
+ for cls in mods.Module.REGISTRY.values():
69
+ try:
70
+ module: mods.Module = cls.from_config(
71
+ config=theme_config[cls.config_key], theme_name=new_theme, utility=utility, global_config=config
72
+ )
73
+ except Exception:
74
+ # mostly likely the configuration is not set
75
+ if args.verbose:
76
+ logger.warning(f"error initializing configuration module: {cls!r}", exc_info=True)
77
+ continue
78
+ try:
79
+ module.run()
80
+ except Exception:
81
+ logger.warning(f"error applying configuration module: {module!r}", exc_info=True)
lightdark/modules.py ADDED
@@ -0,0 +1,134 @@
1
+ from pathlib import Path
2
+ import re
3
+ import subprocess as sbp
4
+
5
+ import attr
6
+ import atomicwrites
7
+
8
+ sbp_run_pipe_check_kw = dict(stdout=sbp.PIPE, stderr=sbp.PIPE, check=True)
9
+
10
+
11
+ class Utility:
12
+ def simple_run_command(self, args, **kw):
13
+ sbp.run(args, **(sbp_run_pipe_check_kw | kw))
14
+
15
+
16
+ @attr.s(eq=False, hash=False)
17
+ class Module:
18
+ utility: Utility = attr.ib(repr=False)
19
+
20
+ REGISTRY = {}
21
+
22
+ @classmethod
23
+ def _make_kwargs(cls, output, kw):
24
+ output["utility"] = kw["utility"]
25
+
26
+ @classmethod
27
+ def from_config(cls, **kw):
28
+ cls._make_kwargs((cls_kw := {}), kw)
29
+ return cls(**cls_kw)
30
+
31
+ def __init_subclass__(cls, **kwargs):
32
+ super().__init_subclass__(**kwargs)
33
+ cls.REGISTRY[cls.__name__] = cls
34
+
35
+
36
+ @attr.s(eq=False, hash=False)
37
+ class ModuleKitty(Module):
38
+ config_key = "kitty"
39
+ p_theme_name: str = attr.ib()
40
+
41
+ @classmethod
42
+ def _make_kwargs(cls, output, kw):
43
+ super()._make_kwargs(output, kw)
44
+ output.update(p_theme_name=kw["config"]["theme_name"])
45
+
46
+ def run(self):
47
+ self.utility.simple_run_command(["kitty", "+kitten", "themes", "--cache-age=-1", self.p_theme_name])
48
+
49
+
50
+ @attr.s(eq=False, hash=False)
51
+ class ModuleEmacsClient(Module):
52
+ config_key = "emacsclient"
53
+ p_theme_names: list[str] = attr.ib()
54
+
55
+ @classmethod
56
+ def _make_kwargs(cls, output, kw):
57
+ super()._make_kwargs(output, kw)
58
+ output.update(p_theme_names=kw["config"]["theme_names"])
59
+
60
+ def run(self):
61
+ rx_safe = re.compile("^[a-zA-Z0-9_+/-]+$")
62
+ for name in self.p_theme_names:
63
+ if not rx_safe.search(name):
64
+ raise ValueError(f"unsafe name: {name!r}")
65
+ self.utility.simple_run_command(
66
+ ["emacsclient", "-n", "-r", "--eval", f"(set-custom-enable-themes '({' '.join(self.p_theme_names)}))"]
67
+ )
68
+
69
+
70
+ @attr.s(eq=False, hash=False)
71
+ class ModuleKDE(Module):
72
+ config_key = "kde"
73
+ p_color_scheme: str = attr.ib()
74
+ p_desktop_theme: str = attr.ib()
75
+
76
+ @classmethod
77
+ def _make_kwargs(cls, output, kw):
78
+ super()._make_kwargs(output, kw)
79
+ output.update(p_color_scheme=kw["config"]["plasma_color_scheme"])
80
+ output.update(p_desktop_theme=kw["config"]["plasma_desktop_theme"])
81
+
82
+ def run(self):
83
+ self.utility.simple_run_command(["plasma-apply-desktoptheme", self.p_desktop_theme])
84
+ self.utility.simple_run_command(["plasma-apply-colorscheme", self.p_color_scheme])
85
+
86
+
87
+ @attr.s(eq=False, hash=False)
88
+ class ModuleLyx(Module):
89
+ config_key = "lyx"
90
+ p_color_section: str = attr.ib()
91
+
92
+ @classmethod
93
+ def _make_kwargs(cls, output, kw):
94
+ super()._make_kwargs(output, kw)
95
+ output.update(p_color_section=kw["config"]["preferences_color_section"])
96
+
97
+ def run(self):
98
+ p = Path.home() / ".lyx/preferences"
99
+ repl = "".join(("\n", self.p_color_section.strip(), "\n\n"))
100
+ out = re.sub(
101
+ "^(# COLOR SECTION #+\n#\n).*?(?=^#\n)",
102
+ lambda m: "".join([m.group(1), repl]),
103
+ p.read_text(encoding="utf-8"),
104
+ count=1,
105
+ flags=re.M + re.S,
106
+ )
107
+ with atomicwrites.atomic_write(str(p), mode="wt") as fh:
108
+ fh.write(out)
109
+
110
+
111
+ @attr.s(eq=False, hash=False)
112
+ class ModuleGTK(Module):
113
+ config_key = "gtk2"
114
+ p_gtk_theme: str = attr.ib()
115
+
116
+ @classmethod
117
+ def _make_kwargs(cls, output, kw):
118
+ super()._make_kwargs(output, kw)
119
+ output.update(p_theme=kw["config"]["theme_name"])
120
+
121
+ def run(self):
122
+ (Path.home() / ".gtkrc-2.0").delete()
123
+ self.utility.simple_run_command(
124
+ [
125
+ "xfconf-query" "-c",
126
+ "xsettings",
127
+ "-p",
128
+ "/Net/ThemeName",
129
+ "-t",
130
+ "string",
131
+ "-s",
132
+ self.p_theme,
133
+ ],
134
+ )
lightdark/py.typed ADDED
File without changes
@@ -0,0 +1,30 @@
1
+ Metadata-Version: 2.4
2
+ Name: lightdark
3
+ Version: 0.9.0
4
+ Summary: Cycle between your desktop themes (such as light and dark)
5
+ Author-email: Eduard Christian Dumitrescu <eduard.c.dumitrescu@gmail.com>
6
+ Maintainer-email: Eduard Christian Dumitrescu <eduard.c.dumitrescu@gmail.com>
7
+ License: Apache Software License 2.0
8
+ Project-URL: Homepage, https://hydra.ecd.space/eduard/darklight/timeline
9
+ Project-URL: Changelog, https://hydra.ecd.space/eduard/darklight/file?name=CHANGELOG.md&ci=trunk
10
+ Classifier: Programming Language :: Python :: 3
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ License-File: AUTHORS.md
14
+ Requires-Dist: attrs
15
+ Requires-Dist: atomicwrites
16
+ Requires-Dist: mergedeep
17
+ Requires-Dist: platformdirs
18
+ Dynamic: license-file
19
+
20
+ # lightdark
21
+
22
+ ## Quick start guide
23
+
24
+ Copy `config.json.example` to `~/.config/lightdark/config.json` then modify it to your liking. Then you can just run
25
+
26
+ ```sh
27
+ lightdark
28
+ ```
29
+
30
+ and it will cycle through the themes for you. Try `lightdark --help` to see more options.
@@ -0,0 +1,12 @@
1
+ lightdark/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ lightdark/__main__.py,sha256=vBQ82334kX06ImDbFlPFgiBRiLIinwNk3z8Khs6hd74,31
3
+ lightdark/main.py,sha256=ZaFLvyClsXovv-V4p0qk4iwhT-eNw5Pque11YQtrFcM,2668
4
+ lightdark/modules.py,sha256=qsmW1fXsadH1vXUVl0Gqu3uOonKLBU7XH6CY0ZNucZM,3825
5
+ lightdark/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ lightdark-0.9.0.dist-info/licenses/AUTHORS.md,sha256=sXkm88GaYJ63k0Hy7UlGboAT4aytU8e1_K0wNo7WwD8,144
7
+ lightdark-0.9.0.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ lightdark-0.9.0.dist-info/METADATA,sha256=5fuiqDwTT7QhN4XU1YBod9ZSGC-9YvPNCupB-pR2rOU,1004
9
+ lightdark-0.9.0.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
10
+ lightdark-0.9.0.dist-info/entry_points.txt,sha256=Hk9I0f-ccY2Hqw2TNGW9kcVZaCa2FLtA0ms5gIHNmAM,50
11
+ lightdark-0.9.0.dist-info/top_level.txt,sha256=XGZEM1hC63FWTf1fxJNd9-1PWUopBN0CNKkdneb8xpQ,10
12
+ lightdark-0.9.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (78.1.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ lightdark = lightdark.main:main
@@ -0,0 +1,9 @@
1
+ # Credits
2
+
3
+ ## Development Lead
4
+
5
+ - Eduard Christian Dumitrescu <eduard.c.dumitrescu@gmail.com>
6
+
7
+ ## Contributors
8
+
9
+ None yet. Why not be the first?
File without changes
@@ -0,0 +1 @@
1
+ lightdark