diff --git a/README.md b/README.md index 8fa70e7..2badf31 100644 --- a/README.md +++ b/README.md @@ -61,13 +61,21 @@ in { inherit palette; userStyles = [ "github" - "reddit" + { + name = "reddit"; + defaultSites = true; + sites = [ ''domain("old.reddit.com")'' ]; + } "youtube" ]; }; } ``` +Each `userStyles` entry can be either a style name string or an attribute set. Attribute set entries accept `name`, `defaultSites`, and `sites`. `defaultSites` defaults to `true`, so configured `sites` are added to the upstream Catppuccin site selectors. Set `defaultSites = false` to replace the upstream selectors with your own. + +`sites` entries are raw Firefox `@-moz-document` selector fragments, such as `''domain("example.com")''`, `''url-prefix("https://example.com/app")''`, or `''regexp("https://example\\.com/.*")''`. + ### Append your own CSS ```nix diff --git a/flake.nix b/flake.nix index 00f2517..af0c4bd 100644 --- a/flake.nix +++ b/flake.nix @@ -1,4 +1,6 @@ { + description = "Small flake library for generating Firefox userstyles from the upstream Catppuccin themes, then remapping them to any Base16-compatible palette."; + inputs = { # keep-sorted start block=yes newline_separated=yes catppuccin-userstyles = { diff --git a/lib/discord.nix b/lib/discord.nix index 20745fd..109aadc 100644 --- a/lib/discord.nix +++ b/lib/discord.nix @@ -2,10 +2,18 @@ # keep-sorted start catppuccin, concatStringsSep, - escapeShellArg, palette24, + pkgs, # keep-sorted end }: let + inherit + (pkgs) + # keep-sorted start + symlinkJoin + writeTextDir + # keep-sorted end + ; + scssColorVars = concatStringsSep "\n" ( map ( mapping: "$" + mapping.name + ": #${palette24.${mapping.base}};" @@ -148,15 +156,15 @@ } ''; - setupScript = '' - mkdir -p node_modules/@catppuccin/palette/scss - mkdir -p node_modules/@catppuccin/highlightjs/sass - - printf '%s\n' ${escapeShellArg scssColorVars} > node_modules/@catppuccin/palette/scss/mocha.scss - printf '%s\n' ${escapeShellArg scssColorVars} > node_modules/@catppuccin/palette/scss/latte.scss - printf '%s\n' ${escapeShellArg scssPaletteMap} > node_modules/@catppuccin/palette/scss/catppuccin.scss - printf '%s\n' ${escapeShellArg scssHighlightTheme} > node_modules/@catppuccin/highlightjs/sass/theme.scss - ''; + sassLoadPath = symlinkJoin { + name = "discord-sass-load-path"; + paths = [ + (writeTextDir "@catppuccin/highlightjs/sass/theme.scss" scssHighlightTheme) + (writeTextDir "@catppuccin/palette/scss/catppuccin.scss" scssPaletteMap) + (writeTextDir "@catppuccin/palette/scss/latte.scss" scssColorVars) + (writeTextDir "@catppuccin/palette/scss/mocha.scss" scssColorVars) + ]; + }; in { - inherit setupScript; + inherit sassLoadPath; } diff --git a/lib/importantize.nix b/lib/importantize.nix index e15edcb..2e9bb7c 100644 --- a/lib/importantize.nix +++ b/lib/importantize.nix @@ -1,39 +1,47 @@ -{pkgs, ...}: -pkgs.writers.writeJSBin "importantize" { - libraries = [pkgs.postcss]; -} '' - const postcss = require("postcss"); +{pkgs, ...}: let + inherit + (pkgs) + # keep-sorted start + postcss + writers + # keep-sorted end + ; +in + writers.writeJSBin "importantize" { + libraries = [postcss]; + } '' + const postcss = require("postcss"); - const importantPlugin = () => ({ - postcssPlugin: "postcss-important", - Declaration(decl) { - decl.important = true; - }, - }); + const importantPlugin = () => ({ + postcssPlugin: "postcss-important", + Declaration(decl) { + decl.important = true; + }, + }); - importantPlugin.postcss = true; + importantPlugin.postcss = true; - let css = ""; - process.stdin.setEncoding("utf8"); + let css = ""; + process.stdin.setEncoding("utf8"); - process.stdin.on("data", chunk => { - css += chunk; - }); + process.stdin.on("data", chunk => { + css += chunk; + }); - process.stdin.on("end", () => { - postcss([importantPlugin()]) - .process(css, { from: undefined }) - .then(result => { - process.stdout.write(result.css); - }) - .catch(error => { - console.error("Error processing CSS:", error); - process.exit(1); - }); - }); + process.stdin.on("end", () => { + postcss([importantPlugin()]) + .process(css, { from: undefined }) + .then(result => { + process.stdout.write(result.css); + }) + .catch(error => { + console.error("Error processing CSS:", error); + process.exit(1); + }); + }); - process.stdin.on("error", err => { - console.error("Error reading from stdin:", err); - process.exit(1); - }); -'' + process.stdin.on("error", err => { + console.error("Error reading from stdin:", err); + process.exit(1); + }); + '' diff --git a/lib/mkUserStyles.nix b/lib/mkUserStyles.nix index cab9df0..333299b 100644 --- a/lib/mkUserStyles.nix +++ b/lib/mkUserStyles.nix @@ -10,165 +10,88 @@ inherit (builtins) # keep-sorted start + all concatStringsSep - elem - filter # keep-sorted end ; inherit (lib) getExe; - inherit (lib.attrsets) mapAttrsToList; - inherit (lib.strings) escapeShellArg; - importantize = pkgs.callPackage ./importantize.nix inputs; - catppuccinStyles = filter (s: s != "discord") userStyles; + inherit (lib.strings) optionalString; + inherit + (pkgs) + # keep-sorted start + callPackage + stdenvNoCC + # keep-sorted end + ; + inherit (stdenvNoCC) mkDerivation; + importantize = callPackage ./importantize.nix inputs; catppuccin = import ./catppuccin.nix; - discord = import ./discord.nix { + palette24 = import ./mkUserStyles/palette.nix {inherit palette;}; + configs = import ./mkUserStyles/configs.nix {inherit userStyles;}; + catppuccinBuild = import ./mkUserStyles/catppuccin.nix { inherit # keep-sorted start catppuccin - concatStringsSep - escapeShellArg + catppuccin-userstyles + lib palette24 + pkgs # keep-sorted end ; }; - - palette24 = - palette - // { - base11 = palette.base00; - base12 = palette.base08; - base15 = palette.base0C; - base16 = palette.base0D; - base17 = palette.base0E; - }; - - lessVarDecl = vars: - concatStringsSep " " ( - mapAttrsToList ( - name: value: "@${name}: ${toString value};" - ) - vars - ); - - lessPaletteDecl = colorDecls: "{ ${concatStringsSep " " colorDecls} };"; - - lessPaletteOverride = let - colorDecls = - map ( - mapping: "@${mapping.name}: #${palette24.${mapping.base}};" - ) - catppuccin.replacements; - flavors = [ + discordBuild = import ./mkUserStyles/discord.nix { + inherit # keep-sorted start - "frappe" - "latte" - "macchiato" - "mocha" + catppuccin + concatStringsSep + palette24 + pkgs # keep-sorted end - ]; - in '' - @catppuccin: { - ${concatStringsSep "\n " (map (flavor: "@${flavor}: ${lessPaletteDecl colorDecls}") flavors)} - }; - ''; - - lessVars = { - # keep-sorted start - accentColor = "lavender"; - additions = 0; - applyToDocument = 0; - bg-blur = "20px"; - bg-opacity = 0.2; - checkColor = "red"; - colorizeLogo = 0; - contrastColor = "@accentColor"; - darkFlavor = "mocha"; - darkenShadows = 1; - graphUseAccentColor = 1; - hideColorSampleTint = 1; - hideProfilePictures = 0; - highlight-redirect = 0; - highlightColor = "@accentColor"; - highlightColor1 = "lavender"; - highlightColor2 = "green"; - highlightColor3 = "peach"; - highlightColor4 = "blue"; - lastMoveColor = "red"; - lightFlavor = "mocha"; - lighterMessages = 0; - logo = 1; - oled = 0; - sponsorBlock = 1; - styleBoard = 1; - styleBoardAndPieces = 1; - stylePieces = 1; - styleVideoPlayer = 1; - urls = "localhost"; - zen = 0; - # keep-sorted end + ; + inherit (configs) documentSelector; + styleConfig = configs.discordStyleConfig; }; - - catppuccinStylesStr = concatStringsSep " " catppuccinStyles; - buildDiscordStyle = elem "discord" userStyles; in - pkgs.stdenvNoCC.mkDerivation { - name = "userstyles.css"; - phases = ["buildPhase"]; - nativeBuildInputs = with pkgs; [ - clean-css-cli - dart-sass - lessc - ]; + assert all (style: style.defaultSites || style.sites != []) configs.userStyleConfigs; + mkDerivation { + name = "userstyles.css"; + phases = ["buildPhase"]; + nativeBuildInputs = with pkgs; [ + # keep-sorted start + clean-css-cli + dart-sass + lessc + # keep-sorted end + ]; - buildPhase = '' - cp "${catppuccin-userstyles}/lib/lib.less" lib-base16.less - chmod u+w lib-base16.less - printf '%s\n' ${escapeShellArg lessPaletteOverride} >> lib-base16.less + buildPhase = '' + : > catppuccin.userstyles.css - # build catppuccin userstyles - for style in ${catppuccinStylesStr}; do - file="${catppuccin-userstyles}/styles/$style/catppuccin.user.less" - if [ -f "$file" ]; then - (cat lib-base16.less; cat "$file" | sed '\|@import "https://userstyles.catppuccin.com/lib/lib.less";|d'; echo ${escapeShellArg (lessVarDecl lessVars)}) | \ - lessc --source-map-no-annotation - | \ - cleancss -O1 >> catppuccin.userstyles.css - fi - done + ${concatStringsSep "\n" (catppuccinBuild.buildCommands configs.catppuccinStyleConfigs)} - # add discord userstyle - if [ "${ - if buildDiscordStyle - then "1" - else "0" - }" = "1" ]; then - ${discord.setupScript} - - { - echo '@-moz-document domain("discord.com") {' + # add discord userstyle + if [ "${ + if configs.buildDiscordStyle + then "1" + else "0" + }" = "1" ]; then sass \ - --load-path="$PWD/node_modules" \ + --load-path=${discordBuild.sassLoadPath} \ --style=compressed \ --no-charset \ --no-source-map \ - "${discord-userstyle}/src/catppuccin-mocha.theme.scss" - echo '}' - } >> extra.userstyles.css - fi + "${discord-userstyle}/src/catppuccin-mocha.theme.scss" \ + > discord.userstyles.css + fi - # replace catppuccin mocha colors with user-defined palette colors - cat catppuccin.userstyles.css extra.userstyles.css > userstyles.css 2>/dev/null || cat extra.userstyles.css > userstyles.css - printf '%s\n' ${escapeShellArg ( - concatStringsSep "\n" ( - map ( - mapping: "s|${catppuccin.palette.${mapping.name}}|${palette24.${mapping.base}}|gI" - ) - catppuccin.replacements - ) - )} > palette-replacements.sed - sed -i -f palette-replacements.sed userstyles.css + # replace catppuccin mocha colors with user-defined palette colors + sed -f ${catppuccinBuild.paletteReplacementsFile} \ + catppuccin.userstyles.css \ + ${optionalString configs.buildDiscordStyle "${discordBuild.documentStartFile} discord.userstyles.css ${discordBuild.documentEndFile}"} \ + > userstyles.css - # !important - ${getExe importantize} < userstyles.css > $out - ''; - } + # !important + ${getExe importantize} < userstyles.css > $out + ''; + } diff --git a/lib/mkUserStyles/catppuccin.nix b/lib/mkUserStyles/catppuccin.nix new file mode 100644 index 0000000..67d9d4d --- /dev/null +++ b/lib/mkUserStyles/catppuccin.nix @@ -0,0 +1,160 @@ +{ + # keep-sorted start + catppuccin, + catppuccin-userstyles, + lib, + palette24, + pkgs, + # keep-sorted end +}: let + inherit + (builtins) + # keep-sorted start + concatMap + concatStringsSep + head + length + pathExists + readFile + replaceStrings + tail + # keep-sorted end + ; + inherit (lib.attrsets) mapAttrsToList; + inherit + (lib.strings) + # keep-sorted start + sanitizeDerivationName + splitString + # keep-sorted end + ; + inherit (pkgs) writeText; + + lessVarDecl = vars: + concatStringsSep " " ( + mapAttrsToList ( + name: value: "@${name}: ${toString value};" + ) + vars + ); + + lessPaletteDecl = colorDecls: "{ ${concatStringsSep " " colorDecls} };"; + + lessPaletteOverride = let + colorDecls = + map ( + mapping: "@${mapping.name}: #${palette24.${mapping.base}};" + ) + catppuccin.replacements; + flavors = [ + # keep-sorted start + "frappe" + "latte" + "macchiato" + "mocha" + # keep-sorted end + ]; + in '' + @catppuccin: { + ${concatStringsSep "\n " (map (flavor: "@${flavor}: ${lessPaletteDecl colorDecls}") flavors)} + }; + ''; + + lessVars = { + # keep-sorted start + accentColor = "lavender"; + additions = 0; + applyToDocument = 0; + bg-blur = "20px"; + bg-opacity = 0.2; + checkColor = "red"; + colorizeLogo = 0; + contrastColor = "@accentColor"; + darkFlavor = "mocha"; + darkenShadows = 1; + graphUseAccentColor = 1; + hideColorSampleTint = 1; + hideProfilePictures = 0; + highlight-redirect = 0; + highlightColor = "@accentColor"; + highlightColor1 = "lavender"; + highlightColor2 = "green"; + highlightColor3 = "peach"; + highlightColor4 = "blue"; + lastMoveColor = "red"; + lightFlavor = "mocha"; + lighterMessages = 0; + logo = 1; + oled = 0; + sponsorBlock = 1; + styleBoard = 1; + styleBoardAndPieces = 1; + stylePieces = 1; + styleVideoPlayer = 1; + urls = "localhost"; + zen = 0; + # keep-sorted end + }; + + stripLibImport = replaceStrings [''@import "https://userstyles.catppuccin.com/lib/lib.less";''] [""]; + + patchDocumentSelectors = style: css: + if style.sites == [] + then css + else let + sites = concatStringsSep ", " style.sites; + documentParts = splitString "@-moz-document " css; + patchDocumentPart = part: let + blockParts = splitString "{" part; + selector = head blockParts; + body = concatStringsSep "{" (tail blockParts); + patchedSelector = + if style.defaultSites + then "${selector}, ${sites}" + else sites; + in + if length blockParts == 1 + then part + else "${patchedSelector} {${body}"; + in + concatStringsSep "@-moz-document " ([(head documentParts)] ++ map patchDocumentPart (tail documentParts)); + + buildCommands = styleConfigs: + concatMap (style: let + stylePath = "${catppuccin-userstyles}/styles/${style.name}/catppuccin.user.less"; + styleSource = writeText "${sanitizeDerivationName style.name}.user.less" '' + @import "${catppuccin-userstyles}/lib/lib.less"; + + ${lessPaletteOverride} + + ${patchDocumentSelectors style (stripLibImport (readFile stylePath))} + + ${lessVarDecl lessVars} + ''; + in + if pathExists stylePath + then [ + '' + lessc --source-map-no-annotation ${styleSource} \ + | cleancss -O1 >> catppuccin.userstyles.css + '' + ] + else []) + styleConfigs; + + paletteReplacementsFile = writeText "palette-replacements.sed" ( + concatStringsSep "\n" ( + map ( + mapping: "s|${catppuccin.palette.${mapping.name}}|${palette24.${mapping.base}}|gI" + ) + catppuccin.replacements + ) + ); +in { + inherit + # keep-sorted start + buildCommands + paletteReplacementsFile + # keep-sorted end + ; +} diff --git a/lib/mkUserStyles/configs.nix b/lib/mkUserStyles/configs.nix new file mode 100644 index 0000000..592cca7 --- /dev/null +++ b/lib/mkUserStyles/configs.nix @@ -0,0 +1,54 @@ +{userStyles}: let + inherit + (builtins) + # keep-sorted start + filter + head + isString + # keep-sorted end + ; + + userStyleDefaults = { + defaultSites = true; + sites = []; + }; + + normalizeUserStyle = userStyle: + if isString userStyle + then userStyleDefaults // {name = userStyle;} + else + userStyleDefaults + // userStyle + // { + name = userStyle.name or (throw "userStyle attrsets must set `name`"); + }; + + userStyleConfigs = map normalizeUserStyle userStyles; + + catppuccinStyleConfigs = filter (style: style.name != "discord") userStyleConfigs; + discordStyleConfigs = filter (style: style.name == "discord") userStyleConfigs; + + buildDiscordStyle = discordStyleConfigs != []; + discordStyleConfig = + if buildDiscordStyle + then head discordStyleConfigs + else userStyleDefaults // {name = "discord";}; + + documentSelector = style: defaultSelectors: + ( + if style.defaultSites + then defaultSelectors + else [] + ) + ++ style.sites; +in { + inherit + # keep-sorted start + buildDiscordStyle + catppuccinStyleConfigs + discordStyleConfig + documentSelector + userStyleConfigs + # keep-sorted end + ; +} diff --git a/lib/mkUserStyles/discord.nix b/lib/mkUserStyles/discord.nix new file mode 100644 index 0000000..7127083 --- /dev/null +++ b/lib/mkUserStyles/discord.nix @@ -0,0 +1,39 @@ +{ + # keep-sorted start + catppuccin, + concatStringsSep, + documentSelector, + palette24, + pkgs, + styleConfig, + # keep-sorted end +}: let + inherit (pkgs) writeText; + + discord = import ../discord.nix { + inherit + # keep-sorted start + catppuccin + concatStringsSep + palette24 + pkgs + # keep-sorted end + ; + }; + + documentStartFile = writeText "discord-document-start.css" '' + @-moz-document ${concatStringsSep ",\n " (documentSelector styleConfig [''domain("discord.com")''])} { + ''; + + documentEndFile = writeText "discord-document-end.css" '' + } + ''; +in { + inherit (discord) sassLoadPath; + inherit + # keep-sorted start + documentEndFile + documentStartFile + # keep-sorted end + ; +} diff --git a/lib/mkUserStyles/palette.nix b/lib/mkUserStyles/palette.nix new file mode 100644 index 0000000..1c4a95d --- /dev/null +++ b/lib/mkUserStyles/palette.nix @@ -0,0 +1,9 @@ +{palette}: +palette +// { + base11 = palette.base00; + base12 = palette.base08; + base15 = palette.base0C; + base16 = palette.base0D; + base17 = palette.base0E; +} diff --git a/lib/withExtraCss.nix b/lib/withExtraCss.nix index e9d912e..4697dde 100644 --- a/lib/withExtraCss.nix +++ b/lib/withExtraCss.nix @@ -1,18 +1,19 @@ -{ - # keep-sorted start - lib, - pkgs, - # keep-sorted end - ... -}: cssFile: extraCss: let - inherit (lib.strings) escapeShellArg; +{pkgs, ...}: cssFile: extraCss: let + inherit + (pkgs) + # keep-sorted start + concatText + writeText + # keep-sorted end + ; in - pkgs.runCommand "userContent.css" {} '' - extraCss=${escapeShellArg extraCss} + if extraCss == "" + then cssFile + else + concatText "userContent.css" [ + cssFile + (writeText "extra.css" '' - cat ${cssFile} > $out - - if [ -n "$extraCss" ]; then - printf '\n%s\n' "$extraCss" >> $out - fi - '' + ${extraCss} + '') + ]