{ config, lib, pkgs, ... }: let cfg = config.programs.ghostty; keyValueSettings = { listsAsDuplicateKeys = true; mkKeyValue = lib.generators.mkKeyValueDefault { } " = "; }; keyValue = pkgs.formats.keyValue keyValueSettings; in { meta.maintainers = with lib.maintainers; [ HeitorAugustoLN khaneliman ]; options.programs.ghostty = let mkShellIntegrationOption = option: option // { description = '' ${option.description} This ensures that shell integration works in more scenarios, such as switching shells within Ghostty. But it is not needed to have shell integration. See for more information. ''; }; in { enable = lib.mkEnableOption "Ghostty"; package = lib.mkPackageOption pkgs "ghostty" { nullable = true; extraDescription = "Set programs.ghostty.package to null on platfroms where ghostty is not available or marked broken"; }; settings = lib.mkOption { inherit (keyValue) type; default = { }; example = lib.literalExpression '' { theme = "catppuccin-mocha"; font-size = 10; keybind = [ "ctrl+h=goto_split:left" "ctrl+l=goto_split:right" ]; } ''; description = '' Configuration written to {file}`$XDG_CONFIG_HOME/ghostty/config`. See for more information. ''; }; themes = lib.mkOption { type = lib.types.attrsOf keyValue.type; default = { }; example = { catppuccin-mocha = { palette = [ "0=#45475a" "1=#f38ba8" "2=#a6e3a1" "3=#f9e2af" "4=#89b4fa" "5=#f5c2e7" "6=#94e2d5" "7=#bac2de" "8=#585b70" "9=#f38ba8" "10=#a6e3a1" "11=#f9e2af" "12=#89b4fa" "13=#f5c2e7" "14=#94e2d5" "15=#a6adc8" ]; background = "1e1e2e"; foreground = "cdd6f4"; cursor-color = "f5e0dc"; selection-background = "353749"; selection-foreground = "cdd6f4"; }; }; description = '' Custom themes written to {file}`$XDG_CONFIG_HOME/ghostty/themes`. See for more information. ''; }; clearDefaultKeybinds = lib.mkEnableOption "" // { description = "Whether to clear default keybinds."; }; installVimSyntax = lib.mkEnableOption "installation of Ghostty configuration syntax for Vim"; installBatSyntax = lib.mkEnableOption "installation of Ghostty configuration syntax for bat" // { default = cfg.package != null; defaultText = lib.literalMD "`true` if programs.ghostty.package is not null"; }; enableBashIntegration = mkShellIntegrationOption (lib.hm.shell.mkBashIntegrationOption { inherit config; }); enableFishIntegration = mkShellIntegrationOption (lib.hm.shell.mkFishIntegrationOption { inherit config; }); enableZshIntegration = mkShellIntegrationOption (lib.hm.shell.mkZshIntegrationOption { inherit config; }); }; config = lib.mkIf cfg.enable (lib.mkMerge [ { home.packages = lib.optionals (cfg.package != null) [ cfg.package ]; programs.ghostty.settings = lib.mkIf cfg.clearDefaultKeybinds { keybind = lib.mkBefore [ "clear" ]; }; # MacOS also supports XDG configuration directory, so we use it for both # Linux and macOS to reduce complexity xdg.configFile = let validate = file: lib.mkIf (cfg.package != null) "${ lib.getExe cfg.package } +validate-config --config-file=${config.xdg.configHome}/ghostty/${file}"; in lib.mkMerge [ { "ghostty/config" = lib.mkIf (cfg.settings != { }) { source = keyValue.generate "ghostty-config" cfg.settings; onChange = validate "config"; }; } (lib.mkIf (cfg.themes != { }) (lib.mapAttrs' (name: value: { name = "ghostty/themes/${name}"; value = { source = keyValue.generate "ghostty-${name}-theme" value; onChange = validate "themes/${name}"; }; }) cfg.themes)) ]; } (lib.mkIf cfg.installVimSyntax { assertions = [{ assertion = cfg.installVimSyntax -> cfg.package != null; message = "programs.ghostty.installVimSyntax cannot be enabled when programs.ghostty.package is null"; }]; programs.vim.plugins = lib.optionals (cfg.package != null) [ cfg.package.vim ]; }) (lib.mkIf cfg.installBatSyntax { assertions = [{ assertion = cfg.installBatSyntax -> cfg.package != null; message = "programs.ghostty.installBatSyntax cannot be enabled when programs.ghostty.package is null"; }]; programs.bat = lib.mkIf (cfg.package != null) { syntaxes.ghostty = { src = cfg.package; file = "share/bat/syntaxes/ghostty.sublime-syntax"; }; config.map-syntax = [ "${config.xdg.configHome}/ghostty/config:Ghostty Config" ]; }; }) (lib.mkIf cfg.enableBashIntegration { # Make order 101 to be placed exactly after bash completions, as Ghostty # documentation suggests sourcing the script as soon as possible programs.bash.initExtra = lib.mkOrder 101 '' if [[ -n "''${GHOSTTY_RESOURCES_DIR}" ]]; then builtin source "''${GHOSTTY_RESOURCES_DIR}/shell-integration/bash/ghostty.bash" fi ''; }) (lib.mkIf cfg.enableFishIntegration { programs.fish.shellInit = '' if set -q GHOSTTY_RESOURCES_DIR source "$GHOSTTY_RESOURCES_DIR/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish" end ''; }) (lib.mkIf cfg.enableZshIntegration { programs.zsh.initExtra = '' if [[ -n $GHOSTTY_RESOURCES_DIR ]]; then source "$GHOSTTY_RESOURCES_DIR"/shell-integration/zsh/ghostty-integration fi ''; }) ]); }