hadalized 0.4.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.
hadalized/homedirs.py ADDED
@@ -0,0 +1,69 @@
1
+ """XDG base directories the application uses."""
2
+
3
+ from pathlib import Path # required for pydantic
4
+
5
+ import xdg_base_dirs
6
+
7
+ from hadalized.const import APP_NAME
8
+
9
+ APP_DIR = Path(APP_NAME)
10
+
11
+
12
+ def config() -> Path:
13
+ """Application user configuration home.
14
+
15
+ Returns:
16
+ The application configuration home directory.
17
+
18
+ """
19
+ return xdg_base_dirs.xdg_config_home() / APP_DIR
20
+
21
+
22
+ def cache() -> Path:
23
+ """Application cache home.
24
+
25
+ Returns:
26
+ The application configuration home directory.
27
+
28
+ """
29
+ return xdg_base_dirs.xdg_cache_home() / APP_DIR
30
+
31
+
32
+ def state() -> Path:
33
+ """Application state home.
34
+
35
+ Returns:
36
+ The application configuration home directory.
37
+
38
+ """
39
+ return xdg_base_dirs.xdg_state_home() / APP_DIR
40
+
41
+
42
+ def data() -> Path:
43
+ """Application data home.
44
+
45
+ Returns:
46
+ The application configuration home directory.
47
+
48
+ """
49
+ return xdg_base_dirs.xdg_data_home() / APP_DIR
50
+
51
+
52
+ def template() -> Path:
53
+ """Application user config templates home.
54
+
55
+ Returns:
56
+ The application configuration home directory.
57
+
58
+ """
59
+ return config() / "templates"
60
+
61
+
62
+ def build() -> Path:
63
+ """Application home for built themes.
64
+
65
+ Returns:
66
+ The application configuration home directory.
67
+
68
+ """
69
+ return state() / "build"
hadalized/options.py ADDED
@@ -0,0 +1,134 @@
1
+ """Common configuration options. Passed to many objects and CLI functions."""
2
+
3
+ from pathlib import Path
4
+ from typing import Annotated, Any
5
+
6
+ from cyclopts.parameter import Parameter
7
+ from pydantic import AfterValidator, Field
8
+
9
+ from hadalized import homedirs
10
+ from hadalized.base import BaseNode
11
+
12
+
13
+ def validate_nullable_path(val: str | Path | None) -> Path | None:
14
+ """Convert special values of `null` or `none` to None.
15
+
16
+ Returns:
17
+ A ``Path`` instance or None.
18
+
19
+ """
20
+ match str(val).lower():
21
+ case "null" | "none":
22
+ out = None
23
+ case _:
24
+ out = Path(val) if isinstance(val, str) else val
25
+ return out
26
+
27
+
28
+ @Parameter(name="*")
29
+ class Options(BaseNode):
30
+ """Common options available to all CLI commands and configuration."""
31
+
32
+ # fixme: Annotated[
33
+ # Path | None,
34
+ # Parameter(alias=["--fix"]),
35
+ # AfterValidator(validate_nullable_path),
36
+ # ] = None
37
+ include_builds: Annotated[
38
+ list[str],
39
+ Parameter(name="app", alias=["-a"], negative=""),
40
+ ] = Field(default=[])
41
+ """Application themes to build. The elements must correspond to a
42
+ ``Config.builds`` key, which is typically an application name.
43
+ If not specified, all applications in ``Config.builds`` will be
44
+ generated.
45
+ """
46
+ cache_dir: Annotated[Path, Parameter(parse=True)] = homedirs.cache()
47
+ """Location of cache directory."""
48
+ config_file: Path | None = None
49
+ """Specify a toml file to load configuration from. When specified,
50
+ the standard configurations specified in ``UserConfig`` are ignored.
51
+ """
52
+ cache_in_memory: Annotated[bool, Parameter(negative="")] = False
53
+ """Whether to use in-memory application cache."""
54
+ dry_run: Annotated[bool, Parameter(alias="-n", negative="")] = False
55
+ """Do not output any files or write to cache."""
56
+ force: Annotated[bool, Parameter(alias="-f", negative="")] = False
57
+ """Force rewriting of files. If set during theme building, files will
58
+ be regenerated and cache populated."""
59
+ no_cache: Annotated[bool, Parameter(negative="")] = False
60
+ """Ignore cache completely. If set during theme building, hash digests
61
+ of generated files will not be cached."""
62
+ no_config: Annotated[bool, Parameter(negative="")] = False
63
+ """Do not read settings from user config files. Implies `--no-templates`."""
64
+ no_templates: Annotated[bool, Parameter(negative="")] = False
65
+ """Ignore user defined templates. Implied by `--no-config`."""
66
+ output_dir: Annotated[
67
+ Path | None,
68
+ Parameter(alias=["--output", "--out", "-o"]),
69
+ AfterValidator(validate_nullable_path),
70
+ ] = Field(
71
+ default=None,
72
+ examples=[Path("./build"), Path("./colors")],
73
+ )
74
+ """Directory to copy built theme files to or output files."""
75
+ parse: Annotated[bool, Parameter(negative="")] = False
76
+ """Whether palette colors should be parsed and expanded upon loading."""
77
+ include_palettes: Annotated[
78
+ list[str],
79
+ Parameter(name="palette", alias=["-p"], negative=""),
80
+ ] = Field(default=[])
81
+ """Palettes to include when building application theme files. The
82
+ items must include a key or alias in the ``Config.palettes`` definitions.
83
+ If not specified, all defined palettes will be utilized.
84
+ """
85
+ prefix: Annotated[bool, Parameter(negative="")] = False
86
+ """When set in conjunction with an output directory, built themes will
87
+ be placed in a subdirectory determined by built theme file's parent
88
+ directory. Typically this is just the applicate name, e.g., 'neovim'."""
89
+ quiet: Annotated[bool, Parameter(alias="-q", negative="")] = False
90
+ """Suppress logging to stdout."""
91
+ state_dir: Annotated[Path, Parameter(parse=True)] = homedirs.state()
92
+ """Directory containing application state such as built theme files."""
93
+ template_dir: Annotated[Path, Parameter(parse=True)] = homedirs.template()
94
+ """Directory where templates will be searched for initially. If a template
95
+ is not found in this directory, it will be loaded from those defined in the
96
+ package."""
97
+ verbose: Annotated[bool, Parameter(alias="-v", negative="")] = False
98
+ """Log more details."""
99
+
100
+ def model_post_init(self, context: Any, /) -> None:
101
+ """Check if settings do not conflict.
102
+
103
+ Raises:
104
+ ValueError: When mutually exclusive options are set.
105
+
106
+
107
+ """
108
+ if self.verbose and self.quiet:
109
+ raise ValueError("Cannot set both verbose and quiet.")
110
+ if self.no_cache and self.cache_in_memory:
111
+ raise ValueError("Cannot set both no_cache and cache_in_memory.")
112
+ if self.no_config and self.config_file:
113
+ raise ValueError("Cannot set both no_config and config_file.")
114
+
115
+ return super().model_post_init(context)
116
+
117
+ @property
118
+ def build_dir(self) -> Path:
119
+ """Location of built theme files."""
120
+ return self.state_dir / "build"
121
+
122
+ @property
123
+ def use_cache(self) -> bool:
124
+ """Opposite of `no_cache`."""
125
+ return not self.no_cache
126
+
127
+ @property
128
+ def use_templates(self) -> bool:
129
+ """Whether to use user-defined templates.
130
+
131
+ False if ``no_config`` is set.
132
+
133
+ """
134
+ return not self.no_config and not self.no_templates
hadalized/palette.py ADDED
@@ -0,0 +1,133 @@
1
+ """Schema for a color palette.
2
+
3
+ A palette is the primary context used to render a color theme.
4
+ """
5
+
6
+ from typing import Literal, Self
7
+
8
+ from pydantic import Field, PrivateAttr
9
+
10
+ from hadalized.base import BaseNode
11
+ from hadalized.color import (
12
+ Bases,
13
+ ColorFieldHandler,
14
+ ColorFieldType,
15
+ ColorMap,
16
+ Extractor,
17
+ Grayscale,
18
+ Hues,
19
+ Parser,
20
+ )
21
+
22
+
23
+ class PaletteMeta(BaseNode):
24
+ """Palette metadata."""
25
+
26
+ name: str
27
+ desc: str
28
+ version: str = "2.1"
29
+ """Version of the palette color definitions."""
30
+ mode: Literal["dark", "light"]
31
+ """Whether the theme is dark or light mode."""
32
+ gamut: str = "srgb"
33
+ """Gamut to fit to when parsing."""
34
+ aliases: list[str] = Field(default=[])
35
+ """Palette aliases."""
36
+
37
+
38
+ class PaletteColors(BaseNode):
39
+ """Palette color maps."""
40
+
41
+ hue: Hues
42
+ """Named main color hues."""
43
+ base: Bases
44
+ """Named bases relative to the palette."""
45
+ bright: Hues = Hues.bright()
46
+ """Named bright color variants."""
47
+ hl: Hues = Hues.highlights()
48
+ """Name highlight color variants."""
49
+ gs: Grayscale = Grayscale()
50
+ """Grayscale colors."""
51
+
52
+
53
+ class Palette(PaletteColors, PaletteMeta):
54
+ """A collection of hues and bases.
55
+
56
+ The primary data structure used to render an application theme template.
57
+ """
58
+
59
+ _cache: dict = PrivateAttr(default={})
60
+ _meta: PaletteMeta | None = PrivateAttr(default=None)
61
+ _colors: PaletteColors | None = PrivateAttr(default=None)
62
+ _is_parsed: bool = PrivateAttr(default=False)
63
+ """Indicates the underlying colortype is ColorInfo."""
64
+ # TODO: We might want to make the typing here explicit.
65
+
66
+ @property
67
+ def meta(self) -> PaletteMeta:
68
+ """Palette metadata."""
69
+ if self._meta is None:
70
+ self._meta = PaletteMeta.model_validate({
71
+ k: v for k, v in self if not isinstance(v, ColorMap)
72
+ })
73
+ return self._meta
74
+
75
+ @property
76
+ def colors(self) -> PaletteColors:
77
+ """Palette color maps."""
78
+ if self._colors is None:
79
+ self._colors = PaletteColors.model_validate({
80
+ k: v for k, v in self if isinstance(v, ColorMap)
81
+ })
82
+ return self._colors
83
+
84
+ def map(self, handler: ColorFieldHandler) -> Self:
85
+ """Map a handler accross color fields.
86
+
87
+ Returns:
88
+ A new Palette instance with the handler applied to each
89
+ field that contains a ColorMap instance.
90
+
91
+ """
92
+ return self.model_validate({
93
+ k: v.map(handler) if isinstance(v, ColorMap) else v for k, v in self
94
+ })
95
+
96
+ def to(self, color_type: ColorFieldType | str) -> Self:
97
+ """Entry point for the `map` method that accepts a directive.
98
+
99
+ When called on an unparsed instance, a new parsed instance is created
100
+ and acted on.
101
+
102
+ Usage:
103
+ palette = palette.to("hex")
104
+
105
+ Returns:
106
+ A new Palette instance with the handler applied to each
107
+ field that contains a ColorMap instance.
108
+
109
+ """
110
+ inst = self if self._is_parsed else self.parse()
111
+ if color_type == ColorFieldType.info:
112
+ return inst
113
+ if (pal := inst._cache.get(color_type)) is None:
114
+ pal = self.map(Extractor(color_type))
115
+ inst._cache[color_type] = pal
116
+ return pal
117
+
118
+ def parse(self, gamut: str | None = None) -> Self:
119
+ """Parse each colormap.
120
+
121
+ Returns:
122
+ A new instance where each ColorMap field is parsed to contain
123
+ full ColorInfo values.
124
+
125
+ """
126
+ gamut = gamut or self.gamut
127
+ if self._is_parsed and gamut == self.gamut:
128
+ out = self
129
+ else:
130
+ parser = Parser(gamut=gamut)
131
+ out = self.map(parser)
132
+ out._is_parsed = True
133
+ return out
@@ -0,0 +1,21 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Color Box Example</title>
8
+ </head>
9
+
10
+ <style>
11
+ .box {
12
+ display: inline-block;
13
+ width: 30px;
14
+ height: 30px;
15
+ border: 1px solid rgba(0, 0, 0, .2);
16
+ }
17
+ </style>
18
+
19
+ <ul>
20
+ {{text}}
21
+ </ul>
@@ -0,0 +1,15 @@
1
+
2
+ {% for pal_key, pal in context.palettes.items() -%}
3
+ [palettes."{{pal_key}}"]
4
+ name = "{{ pal.name }}"
5
+ desc = "{{ pal.desc }}"
6
+ gamut = "{{ pal.gamut }}"
7
+ mode = "{{ pal.mode }}"
8
+
9
+ {% for cmap_key, cmap in pal.colors -%}
10
+ [palettes."{{pal_key}}"."{{cmap_key}}"]
11
+ {% for color_name, color in cmap -%}
12
+ "{{color_name}}" = "{{color.}}"
13
+ {% endfor -%}
14
+ {% endfor -%}
15
+ {% endfor -%}
@@ -0,0 +1 @@
1
+ {{data}}
@@ -0,0 +1 @@
1
+ <li><div class="box" style="background: {css}"></div> {color_name} {css}</li>
@@ -0,0 +1,6 @@
1
+ -- Generated from gen.py
2
+ -- WARNING: Do not edit by hand.
3
+
4
+ local M = {}
5
+
6
+ return M
@@ -0,0 +1 @@
1
+ {{data.model_dump_json(indent=4)}}
@@ -0,0 +1,24 @@
1
+ -- Auto-generated by {{context.app_info}}
2
+ -- WARNING: Do not edit by hand.
3
+
4
+ -- By setting our module to nil, we clear lua's cache,
5
+ -- which means the require ahead will *always* occur.
6
+ --
7
+ -- This isn't strictly required but it can be a useful trick if you are
8
+ -- incrementally editing your config a lot and want to be sure your themes
9
+ -- changes are being picked up without restarting neovim.
10
+ --
11
+ -- Note if you're working in on your theme and have :Lushify'd the buffer,
12
+ -- your changes will be applied with or without the following line.
13
+ --
14
+ -- The performance impact of this call can be measured in the hundreds of
15
+ -- *nanoseconds* and such could be considered "production safe".
16
+ --
17
+
18
+ ---@type Palette
19
+ local palette = {{context.model_dump_lua()}}
20
+
21
+
22
+ package.loaded['hadalized'] = nil
23
+ require('hadalized').load({palette = palette})
24
+
@@ -0,0 +1,7 @@
1
+ -- Generated file for a neovim hadalized data in lua/hadalized/palettes/
2
+ -- Load via require("hadalized.palettes" .. "{{name}}")
3
+ -- WARNING: Do not edit by hand.
4
+
5
+ local M = {{data}}
6
+
7
+ return M
@@ -0,0 +1,53 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Palette Colors for Palette {{context.name}}</title>
8
+ </head>
9
+
10
+ <style>
11
+ .box {
12
+ display: inline-block;
13
+ width: 60px;
14
+ height: 60px;
15
+ border: 1px solid rgba(0, 0, 0, .2);
16
+ }
17
+ </style>
18
+
19
+ <h1>Palette Metadata for {{context.name}}</h1>
20
+ <ul>
21
+ <li>name = {{context.name}}</li>
22
+ <li>desc = {{context.desc}}</li>
23
+ <li>mode = {{context.mode}}</li>
24
+ <li>gamut = {{context.gamut}}</li>
25
+ </ul>
26
+
27
+ <h1>Hues</h1>
28
+ <ul>
29
+ {% for hue_name, code in context.hue -%}
30
+ <li><div class="box" style="background: {{code}}"></div> {{hue_name}}</li>
31
+ {% endfor %}
32
+ </ul>
33
+
34
+ <h1>Bases</h1>
35
+ <ul>
36
+ {% for hue_name, code in context.base -%}
37
+ <li><div class="box" style="background: {{code}}"></div> {{hue_name}}</li>
38
+ {% endfor %}
39
+ </ul>
40
+
41
+ <h1>Highlights</h1>
42
+ <ul>
43
+ {% for hue_name, code in context.hl -%}
44
+ <li><div class="box" style="background: {{code}}"></div> {{hue_name}}</li>
45
+ {% endfor %}
46
+ </ul>
47
+
48
+ <h1>Brights</h1>
49
+ <ul>
50
+ {% for hue_name, code in context.bright -%}
51
+ <li><div class="box" style="background: {{code}}"></div> {{hue_name}}</li>
52
+ {% endfor %}
53
+ </ul>
@@ -0,0 +1 @@
1
+ {{context.model_dump_json(indent=4)}}
@@ -0,0 +1,10 @@
1
+ [palettes."{{context.name}}"]
2
+ name = "{{context.name}}"
3
+ mode = "{{context.mode}}"
4
+ gamut = "{{context.gamut}}"
5
+
6
+
7
+ [palettes."{{context.name}}".hue]
8
+ {% for key, val in context.hue -%}
9
+ "{{key}}" = "{{val}}"
10
+ {% endfor -%}
@@ -0,0 +1,98 @@
1
+ format = '$all'
2
+
3
+ # Which is equivalent to
4
+ format = """
5
+ $username\
6
+ $hostname\
7
+ $localip\
8
+ $shlvl\
9
+ $singularity\
10
+ $kubernetes\
11
+ $directory\
12
+ $vcsh\
13
+ $fossil_branch\
14
+ $fossil_metrics\
15
+ $git_branch\
16
+ $git_commit\
17
+ $git_state\
18
+ $git_metrics\
19
+ $git_status\
20
+ $hg_branch\
21
+ $hg_state\
22
+ $pijul_channel\
23
+ $docker_context\
24
+ $package\
25
+ $c\
26
+ $cmake\
27
+ $cobol\
28
+ $daml\
29
+ $dart\
30
+ $deno\
31
+ $dotnet\
32
+ $elixir\
33
+ $elm\
34
+ $erlang\
35
+ $fennel\
36
+ $fortran\
37
+ $gleam\
38
+ $golang\
39
+ $guix_shell\
40
+ $haskell\
41
+ $haxe\
42
+ $helm\
43
+ $java\
44
+ $julia\
45
+ $kotlin\
46
+ $gradle\
47
+ $lua\
48
+ $nim\
49
+ $nodejs\
50
+ $ocaml\
51
+ $opa\
52
+ $perl\
53
+ $php\
54
+ $pulumi\
55
+ $purescript\
56
+ $python\
57
+ $quarto\
58
+ $raku\
59
+ $rlang\
60
+ $red\
61
+ $ruby\
62
+ $rust\
63
+ $scala\
64
+ $solidity\
65
+ $swift\
66
+ $terraform\
67
+ $typst\
68
+ $vlang\
69
+ $vagrant\
70
+ $zig\
71
+ $buf\
72
+ $nix_shell\
73
+ $conda\
74
+ $meson\
75
+ $spack\
76
+ $memory_usage\
77
+ $aws\
78
+ $gcloud\
79
+ $openstack\
80
+ $azure\
81
+ $nats\
82
+ $direnv\
83
+ $env_var\
84
+ $mise\
85
+ $crystal\
86
+ $custom\
87
+ $sudo\
88
+ $cmd_duration\
89
+ $line_break\
90
+ $jobs\
91
+ $battery\
92
+ $time\
93
+ $status\
94
+ $os\
95
+ $container\
96
+ $netns\
97
+ $shell\
98
+ $character"""