commit f9daee029e430040ae38afdcfe6192d995ec163e Author: Daniel Thwaites Date: Sun Dec 20 18:30:22 2020 +0000 Inital commit diff --git a/default.nix b/default.nix new file mode 100644 index 00000000..19a35438 --- /dev/null +++ b/default.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ./stylix/default.nix + ]; +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..ce5906fb --- /dev/null +++ b/flake.nix @@ -0,0 +1,5 @@ +{ + outputs = inputs: { + nixosModules.stylix = import ./default.nix; + }; +} diff --git a/stylix/colors.nix b/stylix/colors.nix new file mode 100644 index 00000000..e84a8438 --- /dev/null +++ b/stylix/colors.nix @@ -0,0 +1,27 @@ +{ pkgs, lib, config, ... }: + +with lib; + +let + cfg = config.stylix; + + # TODO: This Python library should be included in Nixpkgs + colorgram = with pkgs.python3Packages; buildPythonPackage rec { + pname = "colorgram.py"; + version = "1.2.0"; + src = fetchPypi { + inherit pname version; + sha256 = "1gzxgcmg3ndra2j4dg73x8q9dw6b0akj474gxyyhfwnyz6jncxz7"; + }; + propagatedBuildInputs = [ pillow ]; + }; + colorgramPython = pkgs.python3.withPackages (ps: [ colorgram ]); + + # Pass the wallpaper to ./colors.py and return a JSON file containing the + # generated colorscheme + colorsJSON = pkgs.runCommand "stylix-colors" {} + "${colorgramPython}/bin/python ${./colors.py} ${cfg.image} > $out"; + +in { + config.lib.stylix.colors = importJSON colorsJSON; +} diff --git a/stylix/colors.py b/stylix/colors.py new file mode 100644 index 00000000..56cab0b6 --- /dev/null +++ b/stylix/colors.py @@ -0,0 +1,85 @@ +import json +import sys +import colorgram +import colorsys + + +# Select 9 colors from the image passed on the command line +colors = colorgram.extract(sys.argv[1], 9) + + +# Extract the most dominant color to use as the background +colors.sort(key=lambda c: c.proportion) +dominant_color = colors.pop(0) + +# Decide whether to generate a light or dark scheme based +# on the lightness of the dominant color +if dominant_color.hsl.l >= 128: + def scale(i): + scale = 0.8 - (0.1 * i) + return scale * 255 + + def clamp(l): + return min(l, 100) +else: + def scale(i): + scale = 0.1 + (0.1 * i) + return scale * 255 + + def clamp(l): + return max(l, 155) + + +def int_to_base(i): + return "base{0:02X}".format(i) + +scheme = {} + +# base00 to base07 use the dominant color's hue and saturation, +# lightness is a linear scale +for i in range(8): + scheme[int_to_base(i)] = ( + dominant_color.hsl.h, + dominant_color.hsl.s, + scale(i), + ) + +# base08 to base0A use the remaining 8 colors from the image, +# with their lightness clamped to enforce adequate contrast +colors.sort(key=lambda c: c.hsl.h) # sort by hue +for i in range(8, 16): + color = colors[i-8] + scheme[int_to_base(i)] = ( + color.hsl.h, + color.hsl.s, + clamp(color.hsl.l), + ) + + +data = {} + +for key, color in scheme.items(): + r, g, b = colorsys.hls_to_rgb( + # Function wants HLS, stored as HSL + color[0] / 255, + color[2] / 255, + color[1] / 255, + ) + data[key + "-dec-r"] = r + data[key + "-dec-g"] = g + data[key + "-dec-b"] = b + data[key + "-rgb-r"] = r * 255 + data[key + "-rgb-g"] = g * 255 + data[key + "-rgb-b"] = b * 255 + + hex_color = "{0:02x}{1:02x}{2:02x}".format( + round(r * 255), + round(g * 255), + round(b * 255), + ) + data[key + "-hex"] = hex_color + data[key + "-hex-r"] = hex_color[0:2] + data[key + "-hex-g"] = hex_color[2:4] + data[key + "-hex-b"] = hex_color[4:6] + +json.dump(data, sys.stdout) diff --git a/stylix/default.nix b/stylix/default.nix new file mode 100644 index 00000000..90fea24e --- /dev/null +++ b/stylix/default.nix @@ -0,0 +1,16 @@ +{ lib, ... }: + +with lib; + +{ + imports = [ + ./colors.nix + ./fonts.nix + ./home-manager.nix + ]; + + options.stylix.image = mkOption { + type = types.package; + description = "Wallpaper image."; + }; +} diff --git a/stylix/fonts.nix b/stylix/fonts.nix new file mode 100644 index 00000000..1b5b0751 --- /dev/null +++ b/stylix/fonts.nix @@ -0,0 +1,74 @@ +{ pkgs, config, lib, ... }: + +with lib; + +let + cfg = config.stylix.fonts; + + fontType = types.submodule { options = { + package = mkOption { + description = "Package providing the font."; + type = types.package; + }; + + name = mkOption { + description = "Name of the font within the package."; + type = types.str; + }; + }; }; + +in { + options.stylix.fonts = { + serif = mkOption { + description = "Serif font."; + type = fontType; + default = { + package = pkgs.dejavu_fonts; + name = "DejaVu Serif"; + }; + }; + + sansSerif = mkOption { + description = "Sans-serif font."; + type = fontType; + default = { + package = pkgs.dejavu_fonts; + name = "DejaVu Sans"; + }; + }; + + monospace = mkOption { + description = "Monospace font."; + type = fontType; + default = { + package = pkgs.dejavu_fonts; + name = "DejaVu Sans Mono"; + }; + }; + + emoji = mkOption { + description = "Emoji font."; + type = fontType; + default = { + package = pkgs.noto-fonts-emoji; + name = "Noto Color Emoji"; + }; + }; + }; + + config.fonts = { + fonts = [ + cfg.monospace.package + cfg.serif.package + cfg.sansSerif.package + cfg.emoji.package + ]; + + fontconfig.defaultFonts = { + monospace = [ cfg.monospace.name ]; + serif = [ cfg.serif.name ]; + sansSerif = [ cfg.sansSerif.name ]; + emoji = [ cfg.emoji.name ]; + }; + }; +} diff --git a/stylix/home-manager.nix b/stylix/home-manager.nix new file mode 100644 index 00000000..2b892767 --- /dev/null +++ b/stylix/home-manager.nix @@ -0,0 +1,25 @@ +{ lib, config, ... }: + +with lib; + +let + cfg = config.stylix; + +in { + options.stylix = { + homeManagerUsers = mkOption { + type = types.listOf types.str; + description = "Users for which to enable Home Manager integration."; + default = []; + }; + + homeModule = mkOption { + internal = true; + description = "Home Manager module to apply for all users."; + }; + }; + + config = { + home-manager.users = genAttrs cfg.homeManagerUsers (user: cfg.homeModule); + }; +}