diff --git a/modules/modules.nix b/modules/modules.nix
index b95c5410..befe59e6 100644
--- a/modules/modules.nix
+++ b/modules/modules.nix
@@ -252,6 +252,7 @@ let
./programs/ssh.nix
./programs/starship.nix
./programs/streamlink.nix
+ ./programs/superfile.nix
./programs/swayimg.nix
./programs/swaylock.nix
./programs/swayr.nix
diff --git a/modules/programs/superfile.nix b/modules/programs/superfile.nix
new file mode 100644
index 00000000..9b774626
--- /dev/null
+++ b/modules/programs/superfile.nix
@@ -0,0 +1,150 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ cfg = config.programs.superfile;
+ tomlFormat = pkgs.formats.toml { };
+ inherit (pkgs.stdenv.hostPlatform) isDarwin;
+ inherit (lib)
+ literalExpression
+ mapAttrs'
+ mkEnableOption
+ mkIf
+ mkMerge
+ mkOption
+ mkPackageOption
+ nameValuePair
+ recursiveUpdate
+ types
+ hm
+ ;
+in
+{
+ meta.maintainers = [ hm.maintainers.LucasWagler ];
+
+ options.programs.superfile = {
+ enable = mkEnableOption "superfile - Pretty fancy and modern terminal file manager";
+
+ package = mkPackageOption pkgs "superfile" { nullable = true; };
+
+ settings = mkOption {
+ type = tomlFormat.type;
+ default = { };
+ description = ''
+ Configuration written to {file}`$XDG_CONFIG_HOME/superfile/config.toml`
+ (linux) or {file}`Library/Application Support/superfile/config.toml` (darwin), See
+ for supported values.
+ '';
+ example = literalExpression ''
+ theme = "catppuccin-frappe";
+ default_sort_type = 0;
+ transparent_background = false;
+ '';
+ };
+
+ hotkeys = mkOption {
+ type = tomlFormat.type;
+ default = { };
+ description = ''
+ Hotkey configuration written to {file}`$XDG_CONFIG_HOME/superfile/hotkeys.toml`
+ (linux) or {file}`Library/Application Support/superfile/hotkeys.toml` (darwin), See
+ for supported values.
+ '';
+ example = literalExpression ''
+ confirm = [
+ "enter"
+ "right"
+ "l"
+ ];
+ '';
+ };
+
+ themes = mkOption {
+ type = with types; attrsOf (either tomlFormat.type path);
+ default = { };
+ description = ''
+ Theme files written to {file}`$XDG_CONFIG_HOME/superfile/theme/`
+ (linux) or {file}`Library/Application Support/superfile/theme/` (darwin), See
+ for supported values.
+ '';
+ example = literalExpression ''
+ myTheme = {
+ code_syntax_highlight = "catppuccin-latte";
+
+ file_panel_border = "#101010";
+ sidebar_border = "#101011";
+ footer_border = "#101012";
+
+ gradient_color = [
+ "#101013"
+ "#101014"
+ ];
+
+ # ...
+ };
+ myOtherFavoriteTheme = {
+ code_syntax_highlight = "catppuccin-mocha";
+
+ file_panel_border = "#505050";
+ sidebar_border = "#505051";
+ footer_border = "#505052";
+
+ gradient_color = [
+ "#505053"
+ "#505054"
+ ];
+
+ # ...
+ };
+ '';
+ };
+ };
+
+ config =
+ let
+ enableXdgConfig = !isDarwin || config.xdg.enable;
+ themeSetting =
+ if (!(cfg.settings ? theme) && cfg.themes != { }) then
+ {
+ theme = "${builtins.elemAt (builtins.attrNames cfg.themes) 0}";
+ }
+ else
+ { };
+ baseConfigPath = if enableXdgConfig then "superfile" else "Library/Application Support/superfile";
+ configFile = mkIf (cfg.settings != { }) {
+ "${baseConfigPath}/config.toml".source = tomlFormat.generate "superfile-config.toml" (
+ recursiveUpdate themeSetting cfg.settings
+ );
+ };
+ hotkeysFile = mkIf (cfg.hotkeys != { }) {
+ "${baseConfigPath}/hotkeys.toml".source = tomlFormat.generate "superfile-hotkeys.toml" (
+ cfg.hotkeys
+ );
+ };
+ themeFiles = mapAttrs' (
+ name: value:
+ nameValuePair "${baseConfigPath}/theme/${name}.toml" {
+ source =
+ if types.path.check value then
+ value
+ else
+ (tomlFormat.generate "superfile-theme-${name}.toml" value);
+ }
+ ) cfg.themes;
+ configFiles = mkMerge [
+ configFile
+ hotkeysFile
+ themeFiles
+ ];
+ in
+ mkIf cfg.enable {
+ home.packages = mkIf (cfg.package != null) [ cfg.package ];
+
+ xdg.configFile = mkIf enableXdgConfig configFiles;
+ home.file = mkIf (!enableXdgConfig) configFiles;
+ };
+}
diff --git a/tests/default.nix b/tests/default.nix
index 934373e8..0e7f42a2 100644
--- a/tests/default.nix
+++ b/tests/default.nix
@@ -452,6 +452,7 @@ import nmtSrc {
./modules/programs/ssh
./modules/programs/starship
./modules/programs/streamlink
+ ./modules/programs/superfile
./modules/programs/taskwarrior
./modules/programs/tealdeer
./modules/programs/tex-fmt
diff --git a/tests/modules/programs/superfile/default.nix b/tests/modules/programs/superfile/default.nix
new file mode 100644
index 00000000..373700b4
--- /dev/null
+++ b/tests/modules/programs/superfile/default.nix
@@ -0,0 +1,5 @@
+{
+ superfile-example-settings = ./example-settings.nix;
+ superfile-empty-settings = ./empty-settings.nix;
+ superfile-partial-theme-settings = ./partial-theme-settings.nix;
+}
diff --git a/tests/modules/programs/superfile/empty-settings.nix b/tests/modules/programs/superfile/empty-settings.nix
new file mode 100644
index 00000000..31fa6614
--- /dev/null
+++ b/tests/modules/programs/superfile/empty-settings.nix
@@ -0,0 +1,16 @@
+{ pkgs, lib, ... }:
+
+{
+ programs.superfile.enable = true;
+
+ xdg.enable = lib.mkIf pkgs.stdenv.isDarwin false;
+
+ nmt.script =
+ let
+ configDir =
+ if !pkgs.stdenv.isDarwin then ".config/superfile" else "Library/Application Support/superfile";
+ in
+ ''
+ assertPathNotExists home-files/${configDir}
+ '';
+}
diff --git a/tests/modules/programs/superfile/example-config-expected.toml b/tests/modules/programs/superfile/example-config-expected.toml
new file mode 100644
index 00000000..2b6fdca1
--- /dev/null
+++ b/tests/modules/programs/superfile/example-config-expected.toml
@@ -0,0 +1,3 @@
+default_sort_type = 0
+theme = "catppuccin-frappe"
+transparent_background = false
diff --git a/tests/modules/programs/superfile/example-hotkeys-expected.toml b/tests/modules/programs/superfile/example-hotkeys-expected.toml
new file mode 100644
index 00000000..75a7db97
--- /dev/null
+++ b/tests/modules/programs/superfile/example-hotkeys-expected.toml
@@ -0,0 +1 @@
+confirm = ["enter", "right", "l"]
diff --git a/tests/modules/programs/superfile/example-settings.nix b/tests/modules/programs/superfile/example-settings.nix
new file mode 100644
index 00000000..8f682b4d
--- /dev/null
+++ b/tests/modules/programs/superfile/example-settings.nix
@@ -0,0 +1,86 @@
+{
+ config,
+ pkgs,
+ lib,
+ ...
+}:
+
+{
+ xdg.enable = lib.mkIf pkgs.stdenv.isDarwin false;
+
+ programs.superfile = {
+ enable = true;
+ package = config.lib.test.mkStubPackage { };
+
+ settings = {
+ theme = "catppuccin-frappe";
+ default_sort_type = 0;
+ transparent_background = false;
+ };
+ hotkeys = {
+ confirm = [
+ "enter"
+ "right"
+ "l"
+ ];
+ };
+ themes = {
+ test0 = {
+ code_syntax_highlight = "catppuccin-latte";
+
+ file_panel_border = "#101010";
+ sidebar_border = "#101011";
+ footer_border = "#101012";
+
+ gradient_color = [
+ "#101013"
+ "#101014"
+ ];
+ };
+
+ test1 = ./example-theme-expected.toml;
+
+ test2 = {
+ code_syntax_highlight = "catppuccin-frappe";
+
+ file_panel_border = "#202020";
+ sidebar_border = "#202021";
+ footer_border = "#202022";
+
+ gradient_color = [
+ "#202023"
+ "#202024"
+ ];
+ };
+ };
+ };
+
+ nmt.script =
+ let
+ configSubPath =
+ if !pkgs.stdenv.isDarwin then ".config/superfile" else "Library/Application Support/superfile";
+ configBasePath = "home-files/" + configSubPath;
+ in
+ ''
+ assertFileExists "${configBasePath}/config.toml"
+ assertFileContent \
+ "${configBasePath}/config.toml" \
+ ${./example-config-expected.toml}
+ assertFileExists "${configBasePath}/hotkeys.toml"
+ assertFileContent \
+ "${configBasePath}/hotkeys.toml" \
+ ${./example-hotkeys-expected.toml}
+ assertFileExists "${configBasePath}/theme/test0.toml"
+ assertFileContent \
+ "${configBasePath}/theme/test0.toml" \
+ ${./example-theme-expected.toml}
+ assertFileExists "${configBasePath}/theme/test1.toml"
+ assertFileContent \
+ "${configBasePath}/theme/test1.toml" \
+ ${./example-theme-expected.toml}
+ assertFileExists "${configBasePath}/theme/test2.toml"
+ assertFileContent \
+ "${configBasePath}/theme/test2.toml" \
+ ${./example-theme2-expected.toml}
+ '';
+}
diff --git a/tests/modules/programs/superfile/example-theme-expected.toml b/tests/modules/programs/superfile/example-theme-expected.toml
new file mode 100644
index 00000000..4a117c45
--- /dev/null
+++ b/tests/modules/programs/superfile/example-theme-expected.toml
@@ -0,0 +1,5 @@
+code_syntax_highlight = "catppuccin-latte"
+file_panel_border = "#101010"
+footer_border = "#101012"
+gradient_color = ["#101013", "#101014"]
+sidebar_border = "#101011"
diff --git a/tests/modules/programs/superfile/example-theme2-expected.toml b/tests/modules/programs/superfile/example-theme2-expected.toml
new file mode 100644
index 00000000..81280fd3
--- /dev/null
+++ b/tests/modules/programs/superfile/example-theme2-expected.toml
@@ -0,0 +1,5 @@
+code_syntax_highlight = "catppuccin-frappe"
+file_panel_border = "#202020"
+footer_border = "#202022"
+gradient_color = ["#202023", "#202024"]
+sidebar_border = "#202021"
diff --git a/tests/modules/programs/superfile/partial-theme-settings-expected.toml b/tests/modules/programs/superfile/partial-theme-settings-expected.toml
new file mode 100644
index 00000000..c13337a9
--- /dev/null
+++ b/tests/modules/programs/superfile/partial-theme-settings-expected.toml
@@ -0,0 +1,2 @@
+theme = "test0"
+transparent_background = false
diff --git a/tests/modules/programs/superfile/partial-theme-settings.nix b/tests/modules/programs/superfile/partial-theme-settings.nix
new file mode 100644
index 00000000..8bec52e1
--- /dev/null
+++ b/tests/modules/programs/superfile/partial-theme-settings.nix
@@ -0,0 +1,53 @@
+# When not specified in `programs.superfile.settings.theme`,
+# test that the first skin name (alphabetically) is used in the config file
+{ pkgs, lib, ... }:
+{
+ xdg.enable = lib.mkIf pkgs.stdenv.isDarwin false;
+
+ programs.superfile = {
+ enable = true;
+ settings = {
+ transparent_background = false;
+ };
+ themes = {
+ test2 = {
+ code_syntax_highlight = "catppuccin-frappe";
+
+ file_panel_border = "#202020";
+ sidebar_border = "#202021";
+ footer_border = "#202022";
+
+ gradient_color = [
+ "#202023"
+ "#202024"
+ ];
+ };
+
+ test0 = {
+ code_syntax_highlight = "catppuccin-latte";
+
+ file_panel_border = "#101010";
+ sidebar_border = "#101011";
+ footer_border = "#101012";
+
+ gradient_color = [
+ "#101013"
+ "#101014"
+ ];
+ };
+ };
+ };
+
+ nmt.script =
+ let
+ configSubPath =
+ if !pkgs.stdenv.isDarwin then ".config/superfile" else "Library/Application Support/superfile";
+ configBasePath = "home-files/" + configSubPath;
+ in
+ ''
+ assertFileExists "${configBasePath}/config.toml"
+ assertFileContent \
+ "${configBasePath}/config.toml" \
+ ${./partial-theme-settings-expected.toml}
+ '';
+}