bedHedd's NixOS Adventure

grouping librewolf windows by profile

I have multiple librewolf profiles to allow me to have separate browsing profiles for research and hobbies.
image
By default, wayland and kde’s window grouping will merge all librewolf windows together.

separating the profiles

If you want to see the config, it is here

I started by asking chatgpt how to add multiple librewolf profiles to launch specific profiles. It suggested I create a profile then apply these home manager configs.

The config worked in setting up new shortcuts from the start menu. After realizing the xdg configs, I decided it would be a good idea to consolidate all my xdg configs into a single place. You can find the file here

Once I got it all consolidated into the xdg file, I then wanted to fix the taskbar to also be separated. Chatgpt suggested I use xwayland, but I wanted to set it up for the long term

While checking the two options, I kept getting a bug where launching the individual profile would always start the choose profile

the solution chatgpt suggested was

This got it working
image

changing and recoloring the librewolf icons

I was hoping that the librewolf icons would be in a svg format as it is easier to swap colors out.

looking at the files in /nix/store/r1qc6s4mgapnbhzx10gi8g0rvw82g0m1-librewolf-143.0.3-1/share/icons/hicolor

tree /nix/store/r1qc6s4mgapnbhzx10gi8g0rvw82g0m1-librewolf-143.0.3-1/share/icons/hicolor

.
├── 128x128
│   └── apps
│       └── librewolf.png -> /nix/store/1yy909syv1dhb5y5v4zjg5kddw80l0m5-librewolf-unwrapped-143.0.3-1/lib/librewolf/browser/chrome/icons/default/default128.png
├── 16x16
│   └── apps
│       └── librewolf.png -> /nix/store/1yy909syv1dhb5y5v4zjg5kddw80l0m5-librewolf-unwrapped-143.0.3-1/lib/librewolf/browser/chrome/icons/default/default16.png
├── 32x32
│   └── apps
│       └── librewolf.png -> /nix/store/1yy909syv1dhb5y5v4zjg5kddw80l0m5-librewolf-unwrapped-143.0.3-1/lib/librewolf/browser/chrome/icons/default/default32.png
├── 48x48
│   └── apps
│       └── librewolf.png -> /nix/store/1yy909syv1dhb5y5v4zjg5kddw80l0m5-librewolf-unwrapped-143.0.3-1/lib/librewolf/browser/chrome/icons/default/default48.png
└── 64x64
    └── apps
        └── librewolf.png -> /nix/store/1yy909syv1dhb5y5v4zjg5kddw80l0m5-librewolf-unwrapped-143.0.3-1/lib/librewolf/browser/chrome/icons/default/default64.png

Unfortunately, it is only a png. I tried having chatgpt write code to recolor the icons. It would either leave a blue outline, miss the wolf icon, or miss the wolf and still have a blue icon
image

code it generated if you want it
{ config, lib, pkgs, ... }:

let
  # Your palette
  colorPersonal     = "#8d4953";
  colorProfessional = "#5b7b65";
  colorMaster       = "#0e0e0e";

  sizes = [ 16 32 48 64 128 256 512 ];
  candidateBlues = [ "#00acff" "#00a9e0" "#23a3dc" "#1ea0db" ];

  # Prefer SVG if present (sharper at big sizes); else choose the largest PNG available.
  svgPath = "${pkgs.librewolf}/share/icons/hicolor/scalable/apps/librewolf.svg";
  pngCandidates =
    map (sz: "${pkgs.librewolf}/share/icons/hicolor/${sz}/apps/librewolf.png")
      [ "512x512" "256x256" "128x128" "64x64" "48x48" "32x32" ];

  baseIsSvg = builtins.pathExists svgPath;
  basePng =
    let xs = builtins.filter (p: builtins.pathExists p) pngCandidates;
    in if xs == [] then
         throw "LibreWolf icon PNG not found under ${pkgs.librewolf}"
       else
         builtins.head xs;

  mkPngSet = name: hex:
    pkgs.runCommand "icons-${name}" {
      buildInputs = [ pkgs.imagemagick pkgs.librsvg pkgs.coreutils ];
    } ''
      set -eu
      outdir="$out/share/icons/hicolor"
      mkdir -p "$outdir"

      # Nix-escaped values
      HEX=${lib.escapeShellArg hex}

      render_base () {
        # $1 = target size (e.g. 128)
        # print path to a temp PNG at that size
        sz="$1"
        tmp="$(mktemp --suffix=.png)"
        if ${lib.boolToString baseIsSvg}; then
          ${pkgs.librsvg}/bin/rsvg-convert -w "$sz" -h "$sz" ${lib.escapeShellArg svgPath} -o "$tmp"
        else
          ${pkgs.imagemagick}/bin/convert ${basePng} -resize ''${sz}x''${sz} "$tmp"
        fi
        printf "%s\n" "$tmp"
      }

      recolor_into () {
        # $1 = inpng, $2 = outpng
        inpng="$1"; outpng="$2"
        # Work on RGB only; keep alpha untouched to avoid halos
        work="$(mktemp --suffix=.png)"
        cp "$inpng" "$work"

        # Slightly stricter fuzz to avoid re-coloring greys/whites unintentionally
        for BLUE in ${lib.concatStringsSep " " (map (b: "'${b}'") candidateBlues)}; do
          ${pkgs.imagemagick}/bin/convert "$work" \
            -alpha on -channel RGB -fuzz 20% -fill "$HEX" -opaque "$BLUE" +channel \
            "$work"
        done
        mv "$work" "$outpng"
      }

      for sz in ${lib.concatStringsSep " " (map toString sizes)}; do
        dir="$outdir/''${sz}x''${sz}/apps"
        mkdir -p "$dir"
        base_png="$(render_base "$sz")"
        recolor_into "$base_png" "$dir/librewolf-${name}.png"
      done
    '';

  pngPersonal     = mkPngSet "personal"     colorPersonal;
  pngProfessional = mkPngSet "professional" colorProfessional;
  pngMaster       = mkPngSet "master"       colorMaster;

  mkXdgPngFiles = name: drv:
    lib.listToAttrs (map (sz: {
      name  = "icons/hicolor/${toString sz}x${toString sz}/apps/librewolf-${name}.png";
      value = { source = "${drv}/share/icons/hicolor/${toString sz}x${toString sz}/apps/librewolf-${name}.png"; };
    }) sizes);

in {
  xdg.dataFile =
    mkXdgPngFiles "personal"      pngPersonal
    // mkXdgPngFiles "professional" pngProfessional
    // mkXdgPngFiles "master"        pngMaster;
}

image

code it generated if you want it
let
  # … your colors …
  candidateBlues = [
    # originals
    "#00acff" "#00a9e0" "#23a3dc" "#1ea0db"
    # common ring/halo variants (lighter/darker cyans)
    "#00b4ff" "#00bfff" "#00c0ff" "#00ccff" "#00d4ff" "#00e5ff" "#18ffff"
    "#26c6da" "#29b6f6" "#33b5e5" "#40c4ff" "#4dd0e1" "#80deea"
  ];

  mkPngSet = name: hex:
    pkgs.runCommand "icons-${name}" {
      buildInputs = [ pkgs.imagemagick pkgs.librsvg pkgs.coreutils ];
    } ''
      set -eu
      outdir="$out/share/icons/hicolor"
      mkdir -p "$outdir"

      HEX=${lib.escapeShellArg hex}

      # 1) Make a large master (do this once)
      master="$(mktemp --suffix=.png)"
      if ${lib.boolToString (builtins.pathExists svgPath)}; then
        ${pkgs.librsvg}/bin/rsvg-convert -w 1024 -h 1024 ${lib.escapeShellArg svgPath} -o "$master"
      else
        ${pkgs.imagemagick}/bin/convert ${basePng} -resize 1024x1024 "$master"
      fi

      # 2) Recolor ONCE at full res: broader fuzz to catch halo pixels; RGB only to preserve alpha
      work="$(mktemp --suffix=.png)"
      cp "$master" "$work"
      for BLUE in ${lib.concatStringsSep " " (map (b: "'${b}'") candidateBlues)}; do
        ${pkgs.imagemagick}/bin/convert "$work" \
          -alpha on -channel RGB -fuzz 40% -fill "$HEX" -opaque "$BLUE" +channel \
          "$work"
      done

      # Optional: clamp tiny remaining cyan halos by desaturating very-blue remnants a touch
      ${pkgs.imagemagick}/bin/convert "$work" \
        -alpha on -modulate 100,98,100 +alpha "$work"

      # 3) Downscale the recolored master to all sizes (no new blue is introduced)
      for sz in ${lib.concatStringsSep " " (map toString sizes)}; do
        dir="$outdir/''${sz}x''${sz}/apps"
        mkdir -p "$dir"
        ${pkgs.imagemagick}/bin/convert "$work" \
          -filter Lanczos -define filter:lobes=3 -resize ''${sz}x''${sz} \
          "$dir/librewolf-${name}.png"
      done
    '';

image

code it generated if you want it

mkPngSet = name: hex:
pkgs.runCommand “icons-${name}” { buildInputs = [ pkgs.imagemagick pkgs.coreutils ]; } ‘’
set -eu
outdir=“$out/share/icons/hicolor”
mkdir -p “$outdir”

  # 1) Make a single high-res recolor using a mask (preserves anti-aliasing)
  workdir="$(${pkgs.coreutils}/bin/mktemp -d)"
  master="$workdir/master.png"
  mask="$workdir/mask.png"
  tmp="$workdir/tmp.png"

  # Start from the best available base (your basePng), do NOT resize yet.
  ${pkgs.coreutils}/bin/cp ${basePng} "$master"

  # Build a mask that ORs all candidate blues together, with a small blur to soften edges.
  # Result: white where the cyan stroke is, black elsewhere.
  ${pkgs.imagemagick}/bin/convert -size "$(${pkgs.imagemagick}/bin/identify -format '%wx%h' "$master")" xc:black "$mask"
  for BLUE in ${lib.concatStringsSep " " (map (b: "'${b}'") candidateBlues)}; do
    ${pkgs.imagemagick}/bin/convert "$master" \
      -colorspace sRGB -fuzz 18% -fill white -opaque "$BLUE" \
      -alpha extract -threshold 50% "$tmp"
    ${pkgs.imagemagick}/bin/convert "$mask" "$tmp" -compose Lighten -composite "$mask"
  done
  # Feather the mask slightly to avoid crunchy edges.
  ${pkgs.imagemagick}/bin/convert "$mask" -morphology close disk:1 -blur 0x0.6 "$mask"

  # Create a solid color layer and copy the mask into its alpha, then composite over original.
  ${pkgs.imagemagick}/bin/convert "$master" -fill '${hex}' -colorize 100 "$tmp"
  ${pkgs.imagemagick}/bin/convert "$tmp" "$mask" -compose CopyOpacity -composite "$tmp"
  ${pkgs.imagemagick}/bin/convert "$master" "$tmp" -compose Over -composite "$workdir/recolored.png"

  # 2) Now generate all sizes from the recolored master (better resampling, consistent edges).
  for sz in ${lib.concatStringsSep " " (map toString sizes)}; do
    dir="$outdir/''${sz}x''${sz}/apps"
    ${pkgs.coreutils}/bin/mkdir -p "$dir"
    ${pkgs.imagemagick}/bin/convert "$workdir/recolored.png" \
      -filter Lanczos -define filter:lobes=3 \
      -resize ''${sz}x''${sz} \
      -unsharp 0x0.75+0.75+0.02 \
      "$dir/librewolf-${name}.png"
  done
'';

After getting nowhere with having chatgpt write code to recolor the png, I decided to have it write code to modify the librewolf svg from wikipedia

I used the dev tools to get the svg code and passed this prompt to chatgpt

I tested the code and it worked

I then added the librewolf icons to my user import

in