Added a module to configure browser based on the gecko engine, such as Firefox.

This module should provide similar functionality to the firefox module in
home-manager. Some notable differences between the two include:

* home-manager configures a single browser. This means that any configuration
  that cannot be done on a per-profile basis is shared between all
  profiles. This module configures a new copy of the browser for every profile,
  ensuring that *all* configuration can be on a per-profile basis.

  This might be seen as insanity in a regular distro, but in NixOS this is
  trivial to do and requires no extra storage space.

* home-manager modifies files in the user's directory to configure things such
  as extensions and search engines. This module avoids that when possible by
  pushing configuration into policies and preferences at a browser level.
  This is much nicer for impermanence-based systems.
This commit is contained in:
hylodon 2026-02-22 19:57:42 +00:00
parent 977595f7e8
commit 0f0c4c7727
10 changed files with 962 additions and 1 deletions

View file

@ -0,0 +1,118 @@
{ lib, ... }:
let
submodule =
{ config, ... }:
{
options = {
default = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "ddg";
description = "The default search engine used in the address bar and search bar.";
};
engines = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule engineSubmodule);
default = { };
};
};
};
engineSubmodule =
{ name, ... }:
{
options = {
name = lib.mkOption {
type = lib.types.str;
default = name;
};
searchUri = {
base = lib.mkOption {
type = lib.types.strMatching "[a-z]+://[-A-Za-z0-9.]+(:[0-9]+)?(/[^?]*)?";
};
params = lib.mkOption {
type = lib.types.listOf (
lib.types.submodule {
options = {
key = lib.mkOption { type = lib.types.str; };
value = lib.mkOption { type = lib.types.str; };
};
}
);
default = [ ];
};
};
method = lib.mkOption {
type = lib.types.enum [
"GET"
"POST"
];
default = "GET";
};
alias = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
};
icon = lib.mkOption {
type = lib.types.nullOr (
lib.types.oneOf [
lib.types.package
lib.types.str
]
);
default = null;
};
};
};
buildPolicyFromEngine =
{
name,
searchUri,
method,
alias,
icon,
}:
let
params =
builtins.concatStringsSep "&" <| builtins.map (x: "${x.key}=${x.value}") <| searchUri.params;
in
{
Name = name;
URLTemplate =
if method == "POST" then
searchUri.base
else if params == "" then
searchUri.base
else
"${searchUri.base}?${params}";
Method = method;
${if method == "POST" then "PostData" else null} = params;
${if icon != null then "IconURL" else null} = "file://${builtins.toString icon}";
${if alias != null then "Alias" else null} = alias;
};
in
{
options = {
search = lib.mkOption {
type = lib.types.submodule submodule;
default = { };
description = "Declarative search engine configuration.";
};
};
process =
{ search, ... }:
{
policies.SearchEngines = {
${if search.default == null then null else "Default"} = search.default;
Add = lib.mapAttrsToList (_: buildPolicyFromEngine) search.engines;
};
};
}