From a806ae81bc254990b1ac6e0eb8c26a87ce7af2a7 Mon Sep 17 00:00:00 2001 From: Joe Frikker Date: Wed, 16 Sep 2026 21:27:37 -0400 Subject: [PATCH] Switching to flake-parts --- flake.lock | 34 ++ flake.nix | 170 ++------ home.org | 204 +++++---- home/home-laptop.nix | 46 ++ home/home.nix | 6 + home/redbook.nix | 9 + modules/home/default.nix | 646 +++++++++++++++++++++++++++ modules/home/emacs.el | 871 +++++++++++++++++++++++++++++++++++++ modules/home/home/slack.el | 10 + modules/home/slack.el | 10 + modules/home/wezterm.lua | 154 +++++++ 11 files changed, 1918 insertions(+), 242 deletions(-) create mode 100644 home/home-laptop.nix create mode 100644 home/home.nix create mode 100644 home/redbook.nix create mode 100644 modules/home/default.nix create mode 100644 modules/home/emacs.el create mode 100644 modules/home/home/slack.el create mode 100644 modules/home/slack.el create mode 100644 modules/home/wezterm.lua diff --git a/flake.lock b/flake.lock index 50392d8..1eec4a3 100644 --- a/flake.lock +++ b/flake.lock @@ -21,6 +21,24 @@ "type": "github" } }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1788450739, + "narHash": "sha256-glZLQlzIn1fXH6PazR2iUmTo7kzzyYSshrWhLS9TqCU=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "31729ca8cbdb4fa927b34e5f4353e6a83f39e993", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -78,9 +96,25 @@ "type": "github" } }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1788057806, + "narHash": "sha256-DTQSMxzDWmT0zhguthvegnVkn7CFqGCv4IHCzk5ZUpM=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "596e2e3940e09b2abbeb03f75fa1828c57fcd72c", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, "root": { "inputs": { "claude-code": "claude-code", + "flake-parts": "flake-parts", "home-manager": "home-manager", "nix-index-database": "nix-index-database", "nixpkgs": "nixpkgs" diff --git a/flake.nix b/flake.nix index 0a179a7..09097dc 100644 --- a/flake.nix +++ b/flake.nix @@ -22,170 +22,66 @@ url = "github:nix-community/nix-index-database"; inputs.nixpkgs.follows = "nixpkgs"; }; + + flake-parts.url = "github:hercules-ci/flake-parts"; }; outputs = - { + inputs@{ + self, nixpkgs, + flake-parts, home-manager, claude-code, nix-index-database, ... }: let - modules-for-system = - pkgs: - pkgs.stdenv.mkDerivation { - name = "home-manager-modules"; - src = pkgs.lib.fileset.toSource { - root = ./.; - fileset = pkgs.lib.fileset.unions [ - ./home.org - ]; - }; - - buildPhase = '' - runHook preBuild - ${pkgs.emacs}/bin/emacs --batch --eval "(require 'org)" --eval '(org-babel-tangle-file "home.org")' - runHook postBuild - ''; - installPhase = '' - runHook preInstall - cp -R target $out - runHook postInstall - ''; + pkgs-for-system = + system: + import nixpkgs { + inherit system; + config.allowUnfree = true; + overlays = [ + claude-code.overlays.default + ]; }; in - { - homeConfigurations.redbook = - let - pkgs = import nixpkgs { - system = "aarch64-darwin"; - config.allowUnfree = true; - }; - modules = modules-for-system pkgs; - in - home-manager.lib.homeManagerConfiguration { - inherit pkgs; + flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ + inputs.home-manager.flakeModules.home-manager + ]; + flake = { + homeModules.jfrikker = import ./modules/home; + homeConfigurations.redbook = home-manager.lib.homeManagerConfiguration { + pkgs = pkgs-for-system "aarch64-darwin"; modules = [ nix-index-database.homeModules.default - "${modules}" - ] - ++ [ - { - home.stateVersion = "22.05"; - home.username = "jfrikker"; - home.homeDirectory = "/Users/jfrikker"; - home.packages = [ pkgs.pinentry_mac ]; - jfrikker.enable = true; - jfrikker.desktop.enable = true; - jfrikker.redbook.enable = true; - } + self.homeModules.jfrikker + (import ./home/redbook.nix) ]; - - extraSpecialArgs = { - claude-code = claude-code.packages.aarch64-darwin.default; - }; }; - homeConfigurations.home = - let - pkgs = import nixpkgs { - system = "x86_64-linux"; - config.allowUnfree = true; - }; - modules = modules-for-system pkgs; - in - home-manager.lib.homeManagerConfiguration { - inherit pkgs; + homeConfigurations.home = home-manager.lib.homeManagerConfiguration { + pkgs = pkgs-for-system "x86_64-linux"; modules = [ nix-index-database.homeModules.default - "${modules}" - ] - ++ [ - { - home.stateVersion = "22.05"; - home.username = "jfrikker"; - home.homeDirectory = "/home/jfrikker"; - jfrikker.enable = true; - } + self.homeModules.jfrikker + (import ./home/home.nix) ]; - - extraSpecialArgs = { - claude-code = claude-code.packages.x86_64-linux.default; - }; }; - homeConfigurations.home-laptop = - let - pkgs = import nixpkgs { - system = "x86_64-linux"; - config.allowUnfree = true; - }; - modules = modules-for-system pkgs; - in - home-manager.lib.homeManagerConfiguration { - inherit pkgs; + + homeConfigurations.home-laptop = home-manager.lib.homeManagerConfiguration { + pkgs = pkgs-for-system "x86_64-linux"; modules = [ nix-index-database.homeModules.default - "${modules}" - ] - ++ [ - { - home.stateVersion = "22.05"; - home.username = "jfrikker"; - home.homeDirectory = "/home/jfrikker"; - # home.packages = [ pkgs.pinentry ]; - jfrikker.enable = true; - jfrikker.desktop.enable = true; - - programs.ghostty = { - enable = true; - settings = { - font-family = "Iosevka Term Extended"; - font-size = 10; - }; - }; - - programs.foot = { - enable = true; - settings = { - main = { - font = "Iosevka Term:size=10:width=expanded"; - }; - }; - }; - - home.pointerCursor = { - enable = true; - package = pkgs.vanilla-dmz; - name = "Vanilla-DMZ"; - sway.enable = true; - gtk.enable = true; - }; - - wayland.windowManager.sway = { - enable = true; - config.modifier = "Mod4"; - # config.terminal = "${pkgs.ghostty}/bin/ghostty"; - config.input = { - "1452:628:bcm5974" = { - scroll_factor = "0.4"; - }; - }; - }; - - programs.emacs.package = pkgs.emacs-pgtk; - } + self.homeModules.jfrikker + (import ./home/home-laptop.nix) ]; - # extraSpecialArgs = { kube = kube.packages.x86_64-linux.default; }; - extraSpecialArgs = { - claude-code = claude-code.packages.x86_64-linux.default; - }; }; - packages.x86_64-linux.default = modules-for-system nixpkgs.legacyPackages.x86_64-linux; - packages.aarch64-darwin.default = modules-for-system nixpkgs.legacyPackages.aarch64-darwin; + }; }; } diff --git a/home.org b/home.org index 7975c75..87933b4 100644 --- a/home.org +++ b/home.org @@ -1,8 +1,8 @@ #+PROPERTY: header-args :mkdirp yes :noweb yes #+PROPERTY: header-args:elisp :lexical t * Categories -#+begin_src nix :tangle target/default.nix - {lib, pkgs, config, claude-code, ...}: let +#+begin_src nix :tangle modules/home/default.nix + {lib, pkgs, config, ...}: let inherit (lib) mkIf mkMerge @@ -114,6 +114,13 @@ tools. They boost better speed, or simply more streamlined features. tokei xh ]; + + programs.eza = { + enable = true; + enableBashIntegration = true; + enableZshIntegration = true; + icons = "auto"; + }; } #+END_SRC @@ -158,8 +165,6 @@ are still Redbook-specific; ideally I'd find another way to install them. home.packages = with pkgs; [ openssh ]; - - fonts.fontconfig.enable = true; }) #+END_SRC @@ -179,6 +184,8 @@ These programs require a full desktop environment: proton-pass wireshark ]; + + fonts.fontconfig.enable = true; }) #+END_SRC @@ -265,21 +272,6 @@ leverage this with the emacs ~envrc~ package. } #+END_SRC -** Eza - -~eza~ is an ~ls~ replacement that offers icons, colors, and some easier command-line options. - -#+BEGIN_SRC nix - { - programs.eza = { - enable = true; - enableBashIntegration = true; - enableZshIntegration = true; - icons = "auto"; - }; - } -#+END_SRC - ** Fzf ~fzf~ offers a variety of fuzzy finders right from the shell. I haven't been using this, so I'll @@ -496,7 +488,7 @@ config in two blocks, so we can get lua syntax highlighting in org. First, the n Then the real lua config: -#+begin_src lua :tangle target/wezterm.lua :noweb-ref nil +#+begin_src lua :tangle modules/home/wezterm.lua :noweb-ref nil local config = {} <> @@ -505,7 +497,6 @@ Then the real lua config: #+end_src #+BEGIN_SRC lua - config.default_prog = { '/Users/jfrikker/.nix-profile/bin/zsh', '-l' } config.font = wezterm.font 'Iosevka Term SS14' config.font_size = 13.0 -- config.font = wezterm.font("JetBrains Mono") @@ -775,21 +766,10 @@ Actually, nix *really* requires ~bash~. This is a plugin that allows using zsh f } #+END_SRC -** Claude Code - -#+BEGIN_SRC nix - { - programs.claude-code = { - enable = true; - package = claude-code; - }; - } -#+END_SRC - * Emacs :PROPERTIES: :header-args:nix: :noweb-ref nix-configs -:header-args:elisp: :tangle target/emacs.el +:header-args:elisp: :tangle modules/home/emacs.el :END: Emacs is a huge portion of this configuration, so although it's a home-manager "program", I'm @@ -977,6 +957,7 @@ repeats set up that only apply in that mode. Let's unbind some builtin keys that I don't use. I'm not sure if I'll replace them with anything or not, but at a minimum it'll avoid accidental keypresses and clean up which-key output. #+begin_src elisp + (require 'bind-key) (unbind-key "C-x RET") (unbind-key "C-x #") (unbind-key "C-x 5") @@ -1013,7 +994,7 @@ Ligature mode combines character sequences like <= into a single on-screen glyph cool, so I'm turning it on in prog mode. The enabled ligatures are font-specific, taken from [[https://github.com/mickeynp/ligature.el/wiki#jetbrains-mono][the ligature wiki]]. -#+BEGIN_SRC elisp +#+BEGIN_SRC elisp :noweb no (ligature-set-ligatures 'prog-mode '("--" "---" "==" "===" "!=" "!==" "=!=" "=:=" "=/=" "<=" ">=" "&&" "&&&" "&=" "++" "+++" "***" ";;" "!!" @@ -1955,21 +1936,30 @@ for completion. That makes it really annoying to use; don't use yasnippet with e (setopt consult-narrow-key "<") ;; "C-+" - (setopt vertico-count 25) - (vertico-mode) + (use-package vertico + :custom + (vertico-count 25) + :config + (vertico-mode)) (setopt completion-styles '(orderless basic)) - (marginalia-mode) + (use-package marginalia + (marginalia-mode)) - (setopt corfu-cycle t) - (setopt corfu-popupinfo-mode nil) - (setopt corfu-quit-at-boundary nil) - (setopt corfu-on-exact-match 'show) - (global-corfu-mode) + (use-package corfu + :custom + (corfu-cycle t) + (corfu-popupinfo-mode nil) + (corfu-quit-at-boundary nil) + (corfu-on-exact-match 'show) + :config + (global-corfu-mode)) ;; (setq completion-in-region-function #'consult-completion-in-region) - (add-hook 'completion-at-point-functions 'cape-file) + (use-package cape + :custom + (add-hook 'completion-at-point-functions 'cape-file)) #+END_SRC ** Org @@ -1980,7 +1970,6 @@ for completion. That makes it really annoying to use; don't use yasnippet with e org-modern org-present org-roam - ox-slack verb visual-fill-column ]; @@ -1989,50 +1978,51 @@ for completion. That makes it really annoying to use; don't use yasnippet with e #+END_SRC #+BEGIN_SRC elisp - (require 'org) - - (bind-keys ("C-c A" . org-agenda) - ("C-c C" . org-capture) - ("C-c l" . org-id-store-link)) - - (setopt org-babel-load-languages '((emacs-lisp . t) (sql . t))) - (setopt org-confirm-babel-evaluate nil) - (add-hook 'org-mode-hook 'visual-line-mode) - (setopt org-link-keep-stored-after-insertion t - org-capture-templates - '(("t" "Todo" entry - (here) - "** TODO %?\n:LOGBOOK:\n- State \"TODO\" %U\n:END:\n") - ("w" "Work stuff") - ("wp" "Work Project" entry - (file+headline "~/org/todo/work.org.age" "Next Actions") - "*** PROJ %?\n- State \"PROJ\" %U\n") - ("wa" "Work Next Action" entry - (file+headline "~/org/todo/work.org.age" "Next Actions") - "*** TODO %?\n- State \"TODO\" %U\n") - ("p" "Personal stuff") - ("pp" "Personal Project" entry - (file+headline "~/org/todo/personal.org.age" "Next Actions") - "*** PROJ %?\n- State \"PROJ\" %U\n") - ("pa" "Personal Next Action" entry - (file+headline "~/org/todo/personal.org.age" "Next Actions") - "*** TODO %?\n- State \"TODO\" %U\n")) - org-agenda-files '("~/org/todo/work.org" "~/org/todo/personal.org.age") - org-agenda-todo-ignore-scheduled 'future - org-agenda-tags-todo-honor-ignore-options t - org-enforce-todo-dependencies t - org-agenda-dim-blocked-tasks 'invisible) - (set-face-attribute 'org-level-1 nil :font "Iosevka Aile") - (set-face-attribute 'org-level-2 nil :font "Iosevka Aile") - (set-face-attribute 'org-level-3 nil :font "Iosevka Aile") - (set-face-attribute 'org-level-4 nil :font "Iosevka Aile") - (set-face-attribute 'org-level-5 nil :font "Iosevka Aile") - (set-face-attribute 'org-level-6 nil :font "Iosevka Aile") - (set-face-attribute 'org-level-7 nil :font "Iosevka Aile") - (set-face-attribute 'org-level-8 nil :font "Iosevka Aile") + (use-package org + :bind (("C-c A" . org-agenda) + ("C-c C" . org-capture) + ("C-c l" . org-id-store-link)) + :custom + (org-babel-load-languages '((emacs-lisp . t) (sql . t))) + (org-confirm-babel-evaluate nil) + (org-link-keep-stored-after-insertion t) + (org-capture-templates + '(("t" "Todo" entry + (here) + "** TODO %?\n:LOGBOOK:\n- State \"TODO\" %U\n:END:\n") + ("w" "Work stuff") + ("wp" "Work Project" entry + (file+headline "~/org/todo/work.org.age" "Next Actions") + "*** PROJ %?\n- State \"PROJ\" %U\n") + ("wa" "Work Next Action" entry + (file+headline "~/org/todo/work.org.age" "Next Actions") + "*** TODO %?\n- State \"TODO\" %U\n") + ("p" "Personal stuff") + ("pp" "Personal Project" entry + (file+headline "~/org/todo/personal.org.age" "Next Actions") + "*** PROJ %?\n- State \"PROJ\" %U\n") + ("pa" "Personal Next Action" entry + (file+headline "~/org/todo/personal.org.age" "Next Actions") + "*** TODO %?\n- State \"TODO\" %U\n"))) + (org-agenda-files '("~/org/todo/work.org" "~/org/todo/personal.org.age")) + (org-agenda-todo-ignore-scheduled 'future) + (org-agenda-tags-todo-honor-ignore-options t) + (org-enforce-todo-dependencies t) + (org-agenda-dim-blocked-tasks 'invisible) + :config + (add-hook 'org-mode-hook 'visual-line-mode) + (set-face-attribute 'org-level-1 nil :font "Iosevka Aile") + (set-face-attribute 'org-level-2 nil :font "Iosevka Aile") + (set-face-attribute 'org-level-3 nil :font "Iosevka Aile") + (set-face-attribute 'org-level-4 nil :font "Iosevka Aile") + (set-face-attribute 'org-level-5 nil :font "Iosevka Aile") + (set-face-attribute 'org-level-6 nil :font "Iosevka Aile") + (set-face-attribute 'org-level-7 nil :font "Iosevka Aile") + (set-face-attribute 'org-level-8 nil :font "Iosevka Aile")) (defvar-local my/org-present-face-remapping-cookies nil) - (with-eval-after-load 'org-present + (use-package org-present + :config (require 'face-remap) (require 'visual-fill-column) @@ -2100,7 +2090,7 @@ for completion. That makes it really annoying to use; don't use yasnippet with e ;; (org-roam-db-autosync-mode) (use-package org-modern - :hook org-mode-hook) + :hook org-mode) ;; (define-key org-mode-map (kbd "C-c C-r") verb-command-map) #+END_SRC @@ -2124,16 +2114,18 @@ for completion. That makes it really annoying to use; don't use yasnippet with e ** Slack #+BEGIN_SRC nix - { + (mkIf (cfg.redbook.enable && cfg.desktop.enable) { programs.emacs = { extraPackages = epkgs: with epkgs; [ slack + ox-slack ]; + extraConfig = builtins.readFile ./slack.el; }; - } + }) #+END_SRC -#+BEGIN_SRC elisp +#+BEGIN_SRC elisp :tangle modules/home/home/slack.el :noweb-ref nil (use-package lui :defer t :custom @@ -2171,7 +2163,7 @@ for completion. That makes it really annoying to use; don't use yasnippet with e #+BEGIN_SRC elisp (use-package age :custom - (setopt age-default-identity "~/source/dotfiles/keys/yubi1age.txt") + (age-default-identity "~/source/dotfiles/keys/yubi1age.txt") (age-default-recipient "~/source/dotfiles/keys/myself.txt") :config (setenv "PINENTRY_PROGRAM" "pinentry-mac") @@ -2181,6 +2173,10 @@ for completion. That makes it really annoying to use; don't use yasnippet with e ** Claude Code #+BEGIN_SRC nix { + programs.claude-code = { + enable = true; + }; + programs.emacs = { extraPackages = epkgs: with epkgs; [ (epkgs.trivialBuild { @@ -2304,7 +2300,7 @@ I like Catppuccin Frappe as my theme. Let's use it in as many places as we can. ** Emacs :PROPERTIES: -:header-args:elisp: :tangle target/emacs.el +:header-args:elisp: :tangle modules/home/emacs.el :END: #+BEGIN_SRC nix @@ -2313,19 +2309,17 @@ I like Catppuccin Frappe as my theme. Let's use it in as many places as we can. extraPackages = epkgs: with epkgs; [ catppuccin-theme ]; - extraConfig = "(defvar my-base-font-size ${if pkgs.stdenv.isDarwin then "130" else "100"})"; + extraConfig = ''(defvar my-base-font-size ${if pkgs.stdenv.isDarwin then "130" else "100"}) + (require 'catppuccin-theme) + (setopt catppuccin-flavor 'frappe) + (load-theme 'catppuccin :no-confirm) + + ;; (set-face-attribute 'default nil :font "JetBrainsMono Nerd Font" :height 130) + ;; (set-face-attribute 'fixed-pitch nil :font "JetBrainsMono Nerd Font" :height 130) + (set-face-attribute 'default nil :font "Iosevka Term SS14" :height my-base-font-size) + (set-face-attribute 'fixed-pitch nil :font "Iosevka Term SS14" :height my-base-font-size) + (set-face-attribute 'variable-pitch nil :font "Iosevka Etoile" :height my-base-font-size) + ''; }; } #+END_SRC - -#+BEGIN_SRC elisp - (require 'catppuccin-theme) - (setopt catppuccin-flavor 'frappe) - (load-theme 'catppuccin :no-confirm) - - ;; (set-face-attribute 'default nil :font "JetBrainsMono Nerd Font" :height 130) - ;; (set-face-attribute 'fixed-pitch nil :font "JetBrainsMono Nerd Font" :height 130) - (set-face-attribute 'default nil :font "Iosevka Term SS14" :height my-base-font-size) - (set-face-attribute 'fixed-pitch nil :font "Iosevka Term SS14" :height my-base-font-size) - (set-face-attribute 'variable-pitch nil :font "Iosevka Etoile" :height my-base-font-size) -#+END_SRC diff --git a/home/home-laptop.nix b/home/home-laptop.nix new file mode 100644 index 0000000..27db685 --- /dev/null +++ b/home/home-laptop.nix @@ -0,0 +1,46 @@ +{ pkgs, ... }: { + home.stateVersion = "22.05"; + home.username = "jfrikker"; + home.homeDirectory = "/home/jfrikker"; + # home.packages = [ pkgs.pinentry ]; + jfrikker.enable = true; + jfrikker.desktop.enable = true; + + programs.ghostty = { + enable = true; + settings = { + font-family = "Iosevka Term Extended"; + font-size = 10; + }; + }; + + programs.foot = { + enable = true; + settings = { + main = { + font = "Iosevka Term:size=10:width=expanded"; + }; + }; + }; + + home.pointerCursor = { + enable = true; + package = pkgs.vanilla-dmz; + name = "Vanilla-DMZ"; + sway.enable = true; + gtk.enable = true; + }; + + wayland.windowManager.sway = { + enable = true; + config.modifier = "Mod4"; + # config.terminal = "${pkgs.ghostty}/bin/ghostty"; + config.input = { + "1452:628:bcm5974" = { + scroll_factor = "0.4"; + }; + }; + }; + + programs.emacs.package = pkgs.emacs-pgtk; +} diff --git a/home/home.nix b/home/home.nix new file mode 100644 index 0000000..3dd3576 --- /dev/null +++ b/home/home.nix @@ -0,0 +1,6 @@ +{ + home.stateVersion = "22.05"; + home.username = "jfrikker"; + home.homeDirectory = "/home/jfrikker"; + jfrikker.enable = true; +} diff --git a/home/redbook.nix b/home/redbook.nix new file mode 100644 index 0000000..24cd0eb --- /dev/null +++ b/home/redbook.nix @@ -0,0 +1,9 @@ +{ pkgs, ... }: { + home.stateVersion = "22.05"; + home.username = "jfrikker"; + home.homeDirectory = "/Users/jfrikker"; + home.packages = [ pkgs.pinentry_mac ]; + jfrikker.enable = true; + jfrikker.desktop.enable = true; + jfrikker.redbook.enable = true; +} diff --git a/modules/home/default.nix b/modules/home/default.nix new file mode 100644 index 0000000..ed85634 --- /dev/null +++ b/modules/home/default.nix @@ -0,0 +1,646 @@ +{lib, pkgs, config, ...}: let + inherit (lib) + mkIf + mkMerge + ; + cfg = config.jfrikker; + in { + options.jfrikker = { + enable = lib.mkEnableOption "Joe Frikker's configuration"; + desktop.enable = lib.mkEnableOption "Desktop (GUI) applications"; + redbook.enable = lib.mkEnableOption "Redbook-specific configuration"; + + + }; + + config = mkIf cfg.enable (mkMerge [ + { + home.packages = with pkgs; [ + rlwrap + (sbcl.withPackages (ps: with ps; [ + alexandria + cl-markup + cl-mock + clack + clack-handler-woo + dbi + defstar + dexador + fare-csv + group-by + lack-request + lack-response + local-time + log4cl + mito + myway + prove + prove-asdf + replic + serapeum + shasht + slynk + smug + split-sequence + str + sxql-composer + trivia + trivial-mmap + trivial-ws + trivial-open-browser + trivial-timeout + uuid + ])) + ]; + } + { + home.packages = with pkgs; [ + rage + age-plugin-yubikey + yubikey-manager + ]; + } + { + home.packages = with pkgs; [ + dust + fd + ripgrep + tokei + xh + ]; + + programs.eza = { + enable = true; + enableBashIntegration = true; + enableZshIntegration = true; + icons = "auto"; + }; + } + { + home.packages = with pkgs; [ + ledger + ]; + } + { + home.packages = with pkgs; [ + nil + nixfmt + ]; + } + { + home.packages = with pkgs; [ + magic-wormhole + ]; + } + (mkIf cfg.redbook.enable { + home.packages = with pkgs; [ + openssh + ]; + }) + (mkIf cfg.desktop.enable { + home.packages = with pkgs; [ + google-chrome + nerd-fonts.jetbrains-mono + nerd-fonts.symbols-only + (iosevka-bin.override { variant = "Aile"; }) + (iosevka-bin.override { variant = "Etoile"; }) + (iosevka-bin.override { variant = "SGr-IosevkaTermSS14"; }) + nerd-fonts.symbols-only + keymapp + proton-pass + wireshark + ]; + + fonts.fontconfig.enable = true; + }) + (mkIf (cfg.redbook.enable && cfg.desktop.enable) { + home.packages = with pkgs; [ + linear + notion-app + (slack.overrideAttrs { + version = "4.50.143"; + src = pkgs.fetchurl { + url = "https://downloads.slack-edge.com/desktop-releases/mac/arm64/4.50.143/Slack-4.50.143-macOS.dmg"; + hash = "sha256-f5oYi8NPKXWpDcowFYMUjptbQs4V9hK2bm1TuaQeJn0="; + }; + }) + teams + ]; + }) + { + programs = { + bash.enable = true; + gh.enable = true; + home-manager.enable = true; + jq.enable = true; + lazygit.enable = true; + zoxide.enable = true; + }; + } + { + programs.bat = { + enable = true; + config.style = "numbers"; + }; + } + { + programs.nix-index.enable = true; + programs.nix-index-database.comma.enable = true; + } + { + programs.direnv = { + enable = true; + nix-direnv.enable = true; + }; + } + { + programs.fzf = { + defaultCommand = "fd --type f"; + fileWidgetCommand = "fd --type f"; + fileWidgetOptions = ["--preview" "'bat --color=always --style=numbers --line-range=:500 {}'"]; + changeDirWidgetCommand = "fd --type d"; + tmux.enableShellIntegration = true; + }; + } + { + programs.git = { + enable = true; + settings.user.name = "Joe Frikker"; + settings.user.email = "joe@redbookcattle.com"; + settings.github.user = "joe@redbookcattle.com"; + settings.merge.conflictstyle = "zdiff3"; + }; + + programs.delta = { + enable = true; + enableGitIntegration = true; + }; + } + { + programs.helix = { + enable = true; + defaultEditor = true; + + settings.editor = { + auto-format = false; + auto-pairs = false; + auto-save.after-delay.enable = true; + auto-save.after-delay.timeout = 1000; + cursorline = true; + idle-timeout = 0; + rulers = [120]; + text-width = 100; + lsp.goto-reference-include-declaration = false; + cursor-shape.insert = "bar"; + smart-tab.supersede-menu = true; + whitespace.render.newline = "all"; + }; + + settings.keys.normal.space = { + space = "file_picker"; + "," = "buffer_picker"; + # b = "@:sh wezterm cli spawn --cwd . -- tig blame %"; + }; + + + languages.language-server.eslint = { + args = ["--stdio"]; + command = "vscode-eslint-language-server"; + config.nodePath = ""; + config.quiet = false; + config.rulesCustomizations = []; + config.run = "onType"; + config.validate = "on"; + config.codeAction.disableRuleComment.enable = true; + config.codeAction.disableRuleComment.location = "separateLine"; + config.codeAction.showDocumentation.enable = true; + config.problems.shortenToSingleLine = false; + }; + + languages.language-server.jdtls.config.java = { + autobuild.enabled = true; + completion.maxResults = 1000; + format.settings.url = "https://raw.githubusercontent.com/google/styleguide/gh-pages/eclipse-java-google-style.xml"; + }; + + languages.language-server.rust-analyzer.config.files.watcher = "client"; + + languages.language = [ + { + name = "typescript"; + language-servers = [ "typescript-language-server" "vscode-eslint-language-server" ]; + indent.tab-width = 4; + indent.unit = " "; + } + { + name = "tsx"; + language-servers = [ "typescript-language-server" "vscode-eslint-language-server" ]; + indent.tab-width = 4; + indent.unit = " "; + } + { + name = "java"; + indent.tab-width = 4; + indent.unit = " "; + } + ]; + }; + } + { + programs.nh = { + enable = true; + }; + } + { + programs.starship = { + enable = true; + settings = { + add_newline = false; + container.disabled = true; + git_branch.symbol = " "; + git_status.ahead = "⇡\${count}"; + git_status.behind = "⇣\${count}"; + git_status.diverged = "⇕⇡\${ahead_count}⇣\${behind_count}"; + git_status.format = "([\\[\$conflicted\$staged\$ahead_behind\\]](\$style) )"; + java.symbol = " "; + line_break.disabled = true; + nix_shell.symbol = " "; + nodejs.symbol = " "; + package.disabled = true; + palette = "catppuccin_frappe"; + python.symbol = " "; + package.symbol = " "; + rust.symbol = " "; + docker_context.disabled = true; + shell.disabled = false; + }; + }; + } + { + programs.tmux = { + baseIndex = 1; + clock24 = true; + customPaneNavigationAndResize = true; + historyLimit = 50000; + enable = true; + escapeTime = 0; + keyMode = "vi"; + mouse = true; + shell = pkgs.zsh + "/bin/zsh"; + shortcut = "Space"; + tmuxinator.enable = true; + terminal = "xterm-256color"; + extraConfig = '' + bind-key -T copy-mode-vi 'v' send -X begin-selection + bind-key -T copy-mode-vi 'y' send -X copy-selection-and-cancel + + bind-key -T launcher g popup -h 100% -w 100% -E lazygit + bind-key g switch-client -Tlauncher + set -g default-command "" + ''; + }; + } + (mkIf cfg.desktop.enable { + programs.wezterm = { + enable = true; + extraConfig = '' + local config = {} + config.default_prog = { '${pkgs.zsh}/bin/zsh', '-l'} + config.font_size = ${if pkgs.stdenv.isDarwin then "13.0" else "10.0"} + '' + builtins.readFile ./wezterm.lua; + }; + }) + (mkIf cfg.desktop.enable { + programs.kitty = { + enable = true; + font = { + name = "Iosevka Term SS14"; + package = pkgs.iosevka-bin.override { variant = "SGr-IosevkaTermSS14"; }; + size = 13; + }; + themeFile = "Catppuccin-Frappe"; + keybindings = { + "ctrl+1" = "goto_tab 1"; + "ctrl+2" = "goto_tab 2"; + "ctrl+3" = "goto_tab 3"; + "ctrl+4" = "goto_tab 4"; + "ctrl+5" = "goto_tab 5"; + "ctrl+6" = "goto_tab 6"; + "ctrl+7" = "goto_tab 7"; + "ctrl+8" = "goto_tab 8"; + "ctrl+9" = "goto_tab 9"; + }; + }; + }) + { + programs.ghostty = { + enable = false; + package = pkgs.ghostty-bin; + settings = { + adjust-cell-height = "15%"; + font-family = "Iosevka Term SS14"; + font-size = 13; + }; + }; + } + { + programs.zsh = { + enable = true; + oh-my-zsh.enable = true; + syntaxHighlighting.enable = true; + autosuggestion.enable = true; + historySubstringSearch.enable = true; + history.share = false; + cdpath = ["~/source"]; + }; + } + { + programs.zsh.plugins = [ + { + name = "zsh-nix-shell"; + file = "nix-shell.plugin.zsh"; + src = pkgs.fetchFromGitHub { + owner = "chisui"; + repo = "zsh-nix-shell"; + rev = "v0.7.0"; + sha256 = "149zh2rm59blr2q458a5irkfh82y3dwdich60s9670kl3cl5h2m1"; + }; + } + ]; + } + (mkIf cfg.desktop.enable { + programs.emacs = { + enable = true; + extraConfig = builtins.readFile ./emacs.el; + }; + }) + { + programs.emacs = { + extraPackages = epkgs: with epkgs; [ + ace-window + doom-modeline + free-keys + ligature + mixed-pitch + popper + ]; + }; + } + { + programs.emacs = { + extraPackages = epkgs: with epkgs; [ + apheleia + avy + edit-indirect + embark + god-mode + # evil + # evil-collection + # evil-org + string-inflection + ]; + }; + } + { + programs.emacs = { + extraPackages = epkgs: with epkgs; [ + buffer-terminator + ]; + }; + } + { + programs.emacs = { + extraPackages = epkgs: with epkgs; [ + consult-eglot + graphql-ts-mode + haskell-ts-mode + jenkinsfile-mode + jq-mode + (lispy.overrideAttrs { + packageRequires = [ + ace-window iedit swiper zoutline hydra + ]; + ignoreCompilationError = true; + }) + # lispyville + lua-mode + nix-mode + pnpm-mode + prisma-ts-mode + rust-mode + sly + sly-macrostep + sql-indent + tree-sitter + (treesit-grammars.with-grammars (g: [ + g.tree-sitter-bash + g.tree-sitter-commonlisp + g.tree-sitter-css + g.tree-sitter-dockerfile + g.tree-sitter-dot + g.tree-sitter-elisp + g.tree-sitter-graphql + g.tree-sitter-gitcommit + g.tree-sitter-gitignore + g.tree-sitter-haskell + g.tree-sitter-html + g.tree-sitter-java + g.tree-sitter-javascript + g.tree-sitter-jq + g.tree-sitter-json + g.tree-sitter-json5 + g.tree-sitter-just + g.tree-sitter-ledger + g.tree-sitter-llvm + g.tree-sitter-make + g.tree-sitter-nginx + g.tree-sitter-nix + g.tree-sitter-org + g.tree-sitter-php + g.tree-sitter-prisma + g.tree-sitter-python + g.tree-sitter-rust + g.tree-sitter-rust-format-args + g.tree-sitter-scss + g.tree-sitter-sql + g.tree-sitter-toml + g.tree-sitter-tsx + g.tree-sitter-typescript + g.tree-sitter-xml + g.tree-sitter-yaml + ])) + yaml-pro + ]; + }; + } + { + programs.emacs = { + extraPackages = epkgs: with epkgs; [ + envrc + ]; + }; + } + { + programs.emacs = { + extraPackages = epkgs: with epkgs; [ + consult-gh + diff-hl + forge + git-timemachine + magit + ]; + }; + } + { + programs.emacs = { + extraPackages = epkgs: with epkgs; [ + cape + consult + corfu + embark-consult + marginalia + orderless + vertico + ]; + }; + } + { + programs.emacs = { + extraPackages = epkgs: with epkgs; [ + org-modern + org-present + org-roam + ox-slack + verb + visual-fill-column + ]; + }; + } + { + programs.emacs = { + extraPackages = epkgs: with epkgs; [ + eat + ]; + }; + } + (mkIf (cfg.redbook.enable && cfg.desktop.enable) { + programs.emacs = { + extraPackages = epkgs: with epkgs; [ + slack + ]; + extraConfig = builtins.readFile ./slack.el; + }; + }) + { + programs.emacs = { + extraPackages = epkgs: with epkgs; [ + ledger-mode + ]; + }; + } + { + programs.emacs = { + extraPackages = epkgs: with epkgs; [ + age + ]; + }; + } + { + programs.claude-code = { + enable = true; + }; + + programs.emacs = { + extraPackages = epkgs: with epkgs; [ + (epkgs.trivialBuild { + pname = "claude-code"; + version = "0.4.5"; + src = pkgs.fetchFromGitHub { + owner = "stevemolitor"; + repo = "claude-code.el"; + rev = "03199df8b3a1e9cd4857f0851f7a912ba524aff3"; + sha256 = "sha256-5QJrWIu4EgnHcOqMwlrs2JBBx7aI9OaSJswesr6Apfk="; + }; + packageRequires = [ epkgs.inheritenv ]; + }) + ]; + }; + } + { + programs.bat = { + config.theme = "catppuccin-frappe"; + themes = { + catppuccin-frappe = { + src = pkgs.fetchFromGitHub { + owner = "catppuccin"; + repo = "sublime-text"; # Bat uses sublime syntax for its themes + rev = "3d8625d937d89869476e94bc100192aa220ce44a"; + sha256 = "3ABUsfJpb6RO6AiuuSL5gwDofJIwC5tlEMzBrlY9/s0="; + }; + file = "Frappe.tmTheme"; + }; + }; + }; + } + { + programs.helix.settings.theme = "catppuccin_frappe"; + } + { + programs.tmux.plugins = [ + { + plugin = pkgs.tmuxPlugins.catppuccin; + extraConfig = ''set -g @catppuccin_window_tabs_enabled on + set -g @catppuccin_window_default_text "#W" + set -g @catppuccin_window_current_text "#W"''; + } + ]; + } + { + xdg = { + enable = false + ; + # configFile."lazygit/config.yml" = { + # enable = true; + # source = pkgs.fetchFromGitHub + # { + # owner = "catppuccin"; + # repo = "lazygit"; + # rev = "b2ecb6d41b6f54a82104879573c538e8bdaeb0bf"; # Replace with the latest commit hash + # sha256 = "9BBmWRcjNaJE9T0RKVEJaSnkrbMom0CLYE8PzAT6yFw="; + # } + /themes/frappe.yml; + # }; + }; + } + { + programs.starship.settings = builtins.fromTOML (builtins.readFile + (pkgs.fetchFromGitHub + { + owner = "catppuccin"; + repo = "starship"; + rev = "3e3e54410c3189053f4da7a7043261361a1ed1bc"; # Replace with the latest commit hash + sha256 = "soEBVlq3ULeiZFAdQYMRFuswIIhI9bclIU8WXjxd7oY="; + } + /palettes/frappe.toml)); + } + { + programs.ghostty.settings.theme = "Catppuccin Frappe"; + } + { + programs.emacs = { + extraPackages = epkgs: with epkgs; [ + catppuccin-theme + ]; + extraConfig = ''(defvar my-base-font-size ${if pkgs.stdenv.isDarwin then "130" else "100"}) + (require 'catppuccin-theme) + (setopt catppuccin-flavor 'frappe) + (load-theme 'catppuccin :no-confirm) + + ;; (set-face-attribute 'default nil :font "JetBrainsMono Nerd Font" :height 130) + ;; (set-face-attribute 'fixed-pitch nil :font "JetBrainsMono Nerd Font" :height 130) + (set-face-attribute 'default nil :font "Iosevka Term SS14" :height my-base-font-size) + (set-face-attribute 'fixed-pitch nil :font "Iosevka Term SS14" :height my-base-font-size) + (set-face-attribute 'variable-pitch nil :font "Iosevka Etoile" :height my-base-font-size) + ''; + }; + } + ]); + } diff --git a/modules/home/emacs.el b/modules/home/emacs.el new file mode 100644 index 0000000..b3d03d5 --- /dev/null +++ b/modules/home/emacs.el @@ -0,0 +1,871 @@ +(setopt save-interprogram-paste-before-kill t) +(setopt enable-recursive-minibuffers t) +(setopt line-spacing 0.15) + +(setopt mouse-wheel-tilt-scroll t) +(setq-default truncate-lines t) + +(setopt tab-bar-show nil) +(tab-bar-mode) + +(setopt inhibit-startup-screen t) +(setopt initial-scratch-message nil) + +(menu-bar-mode -1) +(scroll-bar-mode -1) +(tool-bar-mode -1) + +(setopt make-backup-files nil) +(setopt create-lockfiles nil) +(setopt auto-save-default nil) + +(setopt view-read-only t) + +(setopt sentence-end-double-space nil) + +(setopt kill-whole-line t) + +(column-number-mode t) + +(which-key-mode) + +(setopt dired-auto-revert-buffer t) + +(global-hl-line-mode) + +(setopt tab-always-indent 'complete) + +(setq-default cursor-type 'bar) + +(repeat-mode) + +(savehist-mode) + +(require 'bind-key) +(unbind-key "C-x RET") +(unbind-key "C-x #") +(unbind-key "C-x 5") +(unbind-key "C-x 6") +(unbind-key "C-x \\") +(unbind-key "C-x `") +(unbind-key "C-x m") +(unbind-key "C-x l") +(unbind-key "C-x i") +(unbind-key "C-x u") +(unbind-key "C-x @") +(unbind-key "C-x C-b") +(unbind-key "C-x C-d") +(unbind-key "C-x C-l") +(unbind-key "C-x C-p") +(unbind-key "C-x C-u") +(unbind-key "C-x C-z") + +(setopt doom-modeline-irc nil) +(setopt doom-modeline-workspace-name nil) +(setopt doom-modeline-buffer-encoding nil) +(setopt doom-modeline-vcs-max-length 30) +(doom-modeline-mode) + +(ligature-set-ligatures 'prog-mode + '("--" "---" "==" "===" "!=" "!==" "=!=" + "=:=" "=/=" "<=" ">=" "&&" "&&&" "&=" "++" "+++" "***" ";;" "!!" + "??" "???" "?:" "?." "?=" "<:" ":<" ":>" ">:" "<:<" "<>" "<<<" ">>>" + "<<" ">>" "||" "-|" "_|_" "|-" "||-" "|=" "||=" "##" "###" "####" + "#{" "#[" "]#" "#(" "#?" "#_" "#_(" "#:" "#!" "#=" "^=" "<$>" "<$" + "$>" "<+>" "<+" "+>" "<*>" "<*" "*>" "" "/>" "" "->" "->>" "<<-" "<-" "<=<" "=<<" "<<=" "<==" "<=>" "<==>" + "==>" "=>" "=>>" ">=>" ">>=" ">>-" ">-" "-<" "-<<" ">->" "<-<" "<-|" + "<=|" "|=>" "|->" "<->" "<~~" "<~" "<~>" "~~" "~~>" "~>" "~-" "-~" + "~@" "[||]" "|]" "[|" "|}" "{|" "[<" ">]" "|>" "<|" "||>" "<||" + "|||>" "<|||" "<|>" "..." ".." ".=" "..<" ".?" "::" ":::" ":=" "::=" + ":?" ":?>" "//" "///" "/*" "*/" "/=" "//=" "/==" "@_" "__" "???" + "<:<" ";;;")) +(global-ligature-mode t) + +(add-hook 'text-mode-hook 'mixed-pitch-mode) + +(defun no-mixed-pitch () + (mixed-pitch-mode -1)) + +(add-hook 'yaml-ts-mode-hook 'no-mixed-pitch) +(add-hook 'mhtml-mode 'no-mixed-pitch) +(add-hook 'nxml-mode 'no-mixed-pitch) + +(bind-keys ("M-o" . ace-window) + ("s-o" . ace-window) + ("C-c o" . ace-window)) +(setopt aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)) +(setopt aw-dispatch-always t) + +(bind-keys ("s-f" . project-find-file) + ("s-F" . project-switch-project) + ;; ("s-s" . persp-switch) + ) + +(with-eval-after-load 'tab-bar + (bind-keys :map tab-bar-mode-map + ("s-s" . tab-bar-switch-to-tab))) + +(server-mode -1) + +(setopt visible-bell t) + +(transient-mark-mode -1) + +(bind-keys :repeat-map tab-bar-repeat-map + ("" . tab-bar-history-back) + ("" . tab-bar-history-forward)) + +(tab-bar-history-mode) + +(setopt compilation-scroll-output 'first-error) + +(defun jf/window-children (window) + (let ((result nil) + (child (window-child window))) + (while child + (push child result) + (setq child (window-next-sibling child))) + (reverse result))) + +(defun jf/window-main-root (&optional frame) + "Return FRAME's main (non-side) window or window subtree." + (let* ((root (frame-root-window (or frame (selected-frame)))) + (children (jf/window-children root))) + (if (seq-some (lambda (w) (window-parameter w 'window-side)) children) + (seq-find (lambda (w) (not (window-parameter w 'window-side))) children) + root))) + +(defun jf/window-leaves (window) + "Return the live leaf windows in WINDOW's subtree." + (if (window-live-p window) + (list window) + (mapcan #'jf/window-leaves (jf/window-children window)))) + +(defun jf/topright-window (windows) + "Return whichever of WINDOWS sits furthest up, then furthest right." + (car (sort (copy-sequence windows) + (lambda (a b) + (let ((top-a (nth 1 (window-edges a))) + (top-b (nth 1 (window-edges b)))) + (if (= top-a top-b) + (> (nth 0 (window-edges a)) (nth 0 (window-edges b))) + (< top-a top-b))))))) + +(defun jf/display-buffer-right (buffer alist) + "Display BUFFER on the right of the main editing area. + +Applies three rules, based on the shape of the frame's non-side windows: +1. One window -> split it and show BUFFER in the new right window. +2. Two windows side-by-side -> show BUFFER in the right one. If the + right window is selected, swap the two windows first (keeping + focus on the buffer that was active) so BUFFER doesn't evict it. +3. Any other layout -> show BUFFER in the upper-rightmost window." + (let ((main (jf/window-main-root))) + (cond + ((window-live-p main) + (let* ((new (split-window main nil 'right)) + (width (max 1 (- (frame-width) 120))) + (delta (window-resizable new (- width (window-width new)) t))) + (window-resize new delta t) + (window--display-buffer buffer new 'window alist))) + ((and (window-left-child main) + (= (length (jf/window-children main)) 2) + (seq-every-p #'window-live-p (jf/window-children main))) + (let* ((left (window-left-child main)) + (right (window-next-sibling left))) + (when (eq (selected-window) right) + (window-swap-states left right) + (select-window left)) + (window--display-buffer buffer right 'reuse alist))) + (t + (window--display-buffer buffer (jf/topright-window (jf/window-leaves main)) 'reuse alist))))) + + +(setopt popper-reference-buffers + '("\\*Flymake diagnostics .*" + "\\*Embark Export: .*" + "\\*Embark Collect .*" + "\\*xref\\*" + "\\*compilation\\*" + "\\*pnpm:.*" + "\\*sly-mrepl *" + "\\*lispy-message\\*" + "\\*occur\\*" + ".*eshell\\*" + "\\*SQL: .*" + "magit-revision: .*" + "\\*Help\\*" + "\\*rustfmt\\*" + "\\*slack: .*" + "\\*forge: .*" + "new-pullreq" + "\\*claude:.*" + "\\*Man .*" + "\\*List .*" + "\\*eldoc\\*")) +(setopt popper-display-control nil) + +(popper-mode) +(popper-echo-mode) + +(bind-keys ("C-q" . popper-toggle) + ("C-;" . popper-cycle)) + +(defvar jf/fullscreen-popup--param 'jf/fullscreen-saved-configuration + "Key used to stash the window configuration a fullscreen popup replaced. +Stored per-tab when `tab-bar-mode' is active, otherwise as a plain frame +parameter; see `jf/tab-or-frame-parameter'.") + +(defvar-local jf/fullscreen-popup-p nil + "Non-nil in a buffer currently being displayed as a fullscreen popup.") + +(defun jf/current-tab-cell (&optional frame) + "Return FRAME's current tab's raw data cell, or nil without `tab-bar-mode'." + (and (bound-and-true-p tab-bar-mode) + (assq 'current-tab (tab-bar-tabs frame)))) + +(defun jf/tab-or-frame-parameter (parameter &optional frame) + "Like `frame-parameter', but scoped to FRAME's selected tab when tabs are in use. + +A plain frame parameter is shared by every tab on that frame, which is wrong +for state like `jf/fullscreen-popup--param': switching tabs swaps in an +entirely different window layout, so \"the layout to restore\" has to travel +with the tab, not the frame." + (if-let ((tab (jf/current-tab-cell frame))) + (alist-get parameter tab) + (frame-parameter frame parameter))) + +(defun jf/set-tab-or-frame-parameter (parameter value &optional frame) + "Set PARAMETER to VALUE, scoped like `jf/tab-or-frame-parameter'." + (if-let ((tab (jf/current-tab-cell frame))) + (let ((cell (assq parameter tab))) + (if cell + (setcdr cell value) + (setcdr tab (cons (cons parameter value) (cdr tab))))) + (set-frame-parameter frame parameter value))) + +(defun jf/display-buffer-fullscreen (buffer alist) + "Display BUFFER, deleting other windows, saving the prior layout. + +Intended for use in `display-buffer-alist' for popups (e.g. Magit +status/log) that should take over the frame while displayed, but +restore the previous window configuration once closed via either +`q' or `popper-toggle' -- both end up calling `quit-window', which +is where the restore actually happens; see +`jf/quit-window-restore-fullscreen'." + (unless (jf/tab-or-frame-parameter jf/fullscreen-popup--param) + (jf/set-tab-or-frame-parameter jf/fullscreen-popup--param + (current-window-configuration))) + (when-let ((window (or (display-buffer-reuse-window buffer alist) + (display-buffer-same-window buffer alist) + (display-buffer-pop-up-window buffer alist) + (display-buffer-use-some-window buffer alist)))) + (let ((ignore-window-parameters t)) + (delete-other-windows window)) + (with-current-buffer buffer + (setq jf/fullscreen-popup-p t)) + window)) + +(defun jf/quit-window-restore-fullscreen (orig-fn &optional kill window) + "Restore the saved window configuration after quitting a fullscreen popup. + +Advice around `quit-window' so both `q' (which Magit and other +special-mode buffers bind to buffer-quitting commands that call +`quit-window') and Popper's close/toggle commands (which also call +`quit-window' directly) trigger the restore. Only restores once the +last fullscreen popup on the tab (or frame, without `tab-bar-mode') +has closed, so opening one fullscreen popup from within another +falls back to the outer one first." + (let* ((window (or window (selected-window))) + (was-fullscreen (buffer-local-value 'jf/fullscreen-popup-p + (window-buffer window))) + (frame (window-frame window))) + (funcall orig-fn kill window) + (when (and was-fullscreen + (not (and (window-live-p window) + (buffer-local-value 'jf/fullscreen-popup-p + (window-buffer window)))) + (jf/tab-or-frame-parameter jf/fullscreen-popup--param frame)) + (let ((conf (jf/tab-or-frame-parameter jf/fullscreen-popup--param frame))) + (jf/set-tab-or-frame-parameter jf/fullscreen-popup--param nil frame) + (set-window-configuration conf))))) + +(advice-add 'quit-window :around #'jf/quit-window-restore-fullscreen) + +(setopt split-width-threshold 200) +(setopt split-height-threshold nil) +(setopt display-buffer-base-action + '((display-buffer-reuse-window + display-buffer-reuse-mode-window + display-buffer-use-some-window))) + +(setq display-buffer-alist + '(((or "\\*Flymake diagnostics .*" + "\\*Embark Export: .*" + "\\*Embark Collect .*" + "\\*xref\\*" + "\\*compilation\\*" + "\\*pnpm:.*" + "\\*sly-mrepl *" + "\\*lispy-message\\*" + "\\*occur\\*" + ".*eshell\\*" + "\\*SQL: .*") + (display-buffer-in-side-window) + (side . bottom) + (slot . 0) + (window-height . 0.33)) + ((or "magit: *" + "magit-log*" + "\\*Messages\\*" + "\\*trace-output\\*" + "\\*EGLOT connections\\*" + "magit-revision: .*" + "\\*Help\\*" + "\\*rustfmt\\*" + "\\*slack: .*" + "\\*forge: .*" + "new-pullreq" + "\\*claude:.*" + "\\*Man .*" + "\\*List .*" + "\\*Embark Actions\\*" + "\\*eldoc\\*") + (display-buffer-reuse-window + display-buffer-reuse-mode-window + jf/display-buffer-right)))) + +(setopt window-persistent-parameters + (cl-list* '(no-delete-other-windows . writable) + '(no-other-window . writable) + window-persistent-parameters)) + +;; (setopt evil-want-keybinding nil) +;; (require 'evil) +;; (evil-mode 1) +;; (require 'evil-collection) +;; (evil-collection-init) + +;; (require 'evil-org) +;; (add-hook 'org-mode-hook 'evil-org-mode) +;; (evil-org-set-key-theme '(navigation insert textobjects additional calendar)) +;; (require 'evil-org-agenda) +;; (evil-org-agenda-set-keys) +(require 'god-mode) +(require 'which-key) + +(bind-keys :map god-local-mode-map + ("i" . (lambda () (interactive) (god-mode-all -1))) + ("z" . repeat)) +(bind-keys ("" . (lambda () (interactive) (god-mode-all 1)))) +(defun my-god-mode-update-cursor-type () + (setq cursor-type (cond + ((and (featurep 'org-present) org-present-mode) nil) + ((or (and (featurep 'god-mode) god-local-mode) buffer-read-only) 'box) + (t 'bar)))) + +(add-hook 'post-command-hook 'my-god-mode-update-cursor-type) +(which-key-enable-god-mode-support) + +(defun avy-goto-char-timer-embark () + (interactive) + (require 'avy) + (let ((avy-action-oneshot + (lambda (pt) + (unwind-protect + (save-excursion + (goto-char pt) + (embark-act)) + (select-window + (cdr (ring-ref avy-ring 0))))))) + (avy-goto-char-timer))) + +(bind-keys + ("C-j" . avy-goto-char-timer) + ("M-j" . avy-goto-char-timer-embark) + ("C-." . embark-act)) + +(with-eval-after-load 'lispy + (bind-keys :map lispy-mode-map + ("C-j" . avy-goto-char-timer))) + +(with-eval-after-load 'org + (bind-keys :map org-mode-map + ("C-j" . avy-goto-char-timer))) + +(setopt isearch-wrap-pause 'no-ding) + +(defun string-inflection-rust-style-cycle-function (str) + (cond + ((string-inflection-snake-case-p str) + (string-inflection-upcase-function str)) + ((string-inflection-upcase-p str) + (string-inflection-pascal-case-function str)) + (t + (string-inflection-snake-case-function str)))) + +(defun string-inflection-rust-style-cycle () + (interactive) + (require 'string-inflection) + (string-inflection--symbol-or-region #'string-inflection-rust-style-cycle-function)) + +(bind-keys :repeat-map string-inflection-java-map + ("u" . string-inflection-java-style-cycle)) + +(bind-keys :repeat-map string-inflection-rust-map + ("u" . string-inflection-rust-style-cycle)) + +(bind-keys ("C-x 4 n" . edit-indirect-region)) + +(with-eval-after-load 'apheleia + (push '(prisma-format "prisma" "format" "--schema" filepath) apheleia-formatters)) + +(buffer-terminator-mode 1) + +(bind-keys + ("s-b" . consult-project-buffer)) + +(electric-pair-mode 1) + +(setq-default indent-tabs-mode nil) +(setq-default tab-width 4) +(global-tree-sitter-mode 1) + +(setopt ediff-split-window-function 'split-window-horizontally) + +(defun project-nix-flake () + (interactive) + (nix-flake (project-root (project-current t)))) +(bind-keys ("C-x p n" . project-nix-flake)) + +(setq-default fill-column 120) + +(defun jf-init-fill-column () + (display-fill-column-indicator-mode 1)) +(add-hook 'prog-mode-hook 'jf-init-fill-column) + +(with-eval-after-load 'prog-mode + (defun my-prog-fill-reindent-defun (&optional argument) + "Refill or reindent the paragraph or defun that contains point. + +If the point is in a string or a comment, fill the paragraph that +contains point or follows point. + +Otherwise, reindent the function definition that contains point +or follows point." + (interactive "P") + (save-excursion + (let ((treesit-text-node + (and (treesit-available-p) + (treesit-parser-list) + (treesit-node-match-p + (treesit-node-at (point)) 'text t)))) + (if (or treesit-text-node + (and (not (and (treesit-available-p) (treesit-parser-list))) (nth 8 (syntax-ppss))) + (re-search-forward "\\s-*\\s<" (line-end-position) t)) + (fill-paragraph argument (region-active-p)) + (beginning-of-defun) + (let ((start (point))) + (end-of-defun) + (indent-region start (point) nil)))))) + + (advice-add 'prog-fill-reindent-defun :override #'my-prog-fill-reindent-defun)) + +(add-hook 'compilation-filter-hook 'ansi-color-compilation-filter) + +(autoload 'flymake-show-project-diagnostics "flymake") +(autoload 'flymake-goto-next-error "flymake") +(autoload 'flymake-goto-prev-error "flymake") + +(with-eval-after-load 'prog-mode + (bind-keys :map prog-mode-map + ("C-c D" . flymake-show-project-diagnostics) + ("M-n" . flymake-goto-next-error) + ("M-p" . flymake-goto-prev-error))) + +(with-eval-after-load 'flymake + (bind-keys :map flymake-project-diagnostics-mode-map + ("q" . quit-window))) + +(setopt eglot-ignored-server-capabilities '(:inlayHintProvider :semanticTokensProvider)) +(add-hook 'eglot-managed-mode-hook (lambda () (eglot-inlay-hints-mode -1)) nil t) +(setopt eglot-code-action-indications nil) +(setopt eglot-events-buffer-config '(:size 300000 :format full)) + +(with-eval-after-load 'eglot + (bind-keys :map eglot-mode-map + ("C-c a" . eglot-code-actions) + ("C-c r" . eglot-rename) + ("C-c i" . eglot-find-implementation))) + +(with-eval-after-load 'eglot + (add-to-list 'eglot-stay-out-of 'yasnippet)) + +(setq major-mode-remap-alist '((rust-mode . rust-mode))) + +(setopt rust-mode-treesitter-derive t) + +(with-eval-after-load 'rust-prog-mode + (bind-keys :map rust-mode-map + ("C-c u" . string-inflection-rust-style-cycle))) + +(defun yaml-validate () + (interactive) + (compile (concat "yamllint -d '{extends: default, rules: {indentation: {indent-sequences: false}, braces: {max-spaces-inside: 1}}}' " (buffer-file-name)) t)) + +(add-hook 'yaml-ts-mode-hook 'yaml-pro-ts-mode) + +(setopt inferior-lisp-program "sbcl") + +(setopt lispy-compat '(god-mode edebug)) +(add-hook 'emacs-lisp-mode-hook (lambda () (lispy-mode 1))) +(add-hook 'lisp-mode-hook (lambda () (lispy-mode 1))) +(add-hook 'lisp-data-mode-hook (lambda () (lispy-mode 1))) +(with-eval-after-load 'lispy + ;; (require 'lispyville) + ;; (add-hook 'lispy-mode-hook #'lispyville-mode) + (bind-keys :map lispy-mode-map + ("i" . (lambda () + (interactive) + (if (and (featurep 'god-mode) god-global-mode) + (god-mode-all -1) + (special-lispy-tab)))))) + +(add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-mode)) +(add-to-list 'major-mode-remap-alist '(typescript-mode . typescript-ts-mode)) +(add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-mode)) +(add-to-list 'major-mode-remap-alist '(tsx-mode . tsx-ts-mode)) +(setopt typescript-ts-mode-indent-offset 2) +(with-eval-after-load 'typescript-ts-mode + (defun my-tsx-ts-mode--indent-compatibility-b893426 () + "Indent rules helper, to handle different releases of tree-sitter-tsx. +Check if a node type is available, then return the right indent rules." + ;; handle https://github.com/tree-sitter/tree-sitter-typescript/commit/b893426b82492e59388a326b824a346d829487e8 + (condition-case nil + (progn (treesit-query-capture 'tsx '((jsx_fragment) @capture)) + `(((match "<" "jsx_fragment") parent 0) + ((parent-is "jsx_fragment") parent typescript-ts-mode-indent-offset))) + (treesit-query-error + `(((match "<" "jsx_text") parent 0) + ((parent-is "jsx_text") grand-parent typescript-ts-mode-indent-offset))))) + (advice-add 'tsx-ts-mode--indent-compatibility-b893426 :override #'my-tsx-ts-mode--indent-compatibility-b893426)) + +(defvar my/edit-indirect-tag-modes + '(("graphql" . graphql-ts-mode) + ("gql" . graphql-ts-mode) + ("sql" . sql-mode) + ("css" . css-mode)) + "Map of tagged-template tag names to major modes.") + +(defun my/edit-indirect--node-bounds (node) + "Return (BEG . END) for NODE's contents, excluding delimiters." + (cons (1+ (treesit-node-start node)) + (1- (treesit-node-end node)))) + +(defun my/edit-indirect-tagged-template-at-point () + (interactive) + (let* ((node (treesit-node-at (point))) + (tmpl-node (treesit-parent-until + node + (lambda (n) (equal (treesit-node-type n) "template_string")))) + (str-node (unless tmpl-node + (treesit-parent-until + node + (lambda (n) (equal (treesit-node-type n) "string"))))) + (target-node (or tmpl-node str-node))) + (unless target-node + (user-error "Point is not inside a string or template literal")) + (let* ((tagged-node (and tmpl-node (treesit-node-parent tmpl-node))) + (tag-node (and tagged-node + (equal (treesit-node-type tagged-node) "call_expression") + (treesit-node-child-by-field-name tagged-node "function"))) + (tag-name (and tag-node (treesit-node-text tag-node t))) + (mode (cond + (tag-name (or (cdr (assoc tag-name my/edit-indirect-tag-modes)) + major-mode)) + (t 'text-mode))) + (bounds (my/edit-indirect--node-bounds target-node)) + (buf (edit-indirect-region (car bounds) (cdr bounds) t))) + (with-current-buffer buf + (funcall mode))))) + +(with-eval-after-load 'typescript-ts-mode + (define-key typescript-ts-mode-map (kbd "C-c '") #'my/edit-indirect-tagged-template-at-point) + (define-key tsx-ts-mode-map (kbd "C-c '") #'my/edit-indirect-tagged-template-at-point)) + +(with-eval-after-load 'eglot + (add-to-list 'eglot-server-programs '((prisma-ts-mode) "prisma-language-server" "--stdio"))) + +(push '(java-mode . java-ts-mode) major-mode-remap-alist) +(with-eval-after-load 'java-ts-mode + (bind-keys :map java-ts-mode-map + ("C-c u" . string-inflection-java-style-cycle)) + (push `((n-p-gp nil "block" "lambda_expression") parent-bol java-ts-mode-indent-offset) + (cdar java-ts-mode--indent-rules)) + (push `((n-p-gp "}" "block" "lambda_expression") parent-bol 0) + (cdar java-ts-mode--indent-rules)) + + (add-hook 'java-mode-hook (lambda () (c-set-offset 'arglist-intro '+))) + + (defvar jf/class-name-to-file-search-path (list "src/main/java" "src/test/java")) + + (defun jf/class-name-to-file (class-name) + (let ((root (project-root (project-current t))) + (fragment (format "%s.java" (replace-regexp-in-string "\\." "/" class-name))) + (result)) + (dolist (p jf/class-name-to-file-search-path result) + (unless result + (let ((path (format "%s/%s" p fragment))) + (when (file-exists-p (concat root path)) + (setq result path))))))) + + (defun jf/compile-class-to-file () + (jf/class-name-to-file (concat (match-string 1) (match-string 2))))) + + ;(add-to-list 'compilation-error-regexp-alist-alist + ; '(java-stack-trace . + ; ("^[[:space:]]*at \\(\\(?:[[:lower:]]+\\.\\)+\\)[^(]+(\\([[:alnum:]]+\\)\\.java:\\([[:digit:]]+\\))" + ; jf/compile-class-to-file 3))) + ;(add-to-list 'compilation-error-regexp-alist 'java-stack-trace) + +(add-hook 'sql-mode-hook 'sqlind-minor-mode) + +(setopt haskell-ts-use-indent t) + +(add-hook 'after-init-hook 'envrc-global-mode) +(setopt envrc-show-summary-in-minibuffer nil) + +(setopt magit-define-global-key-bindings 'recommended) +(bind-keys ("s-g" . magit)) + +(setopt magit-save-repository-buffers 'dontask) +(setopt magit-commit-show-diff nil) +(with-eval-after-load 'magit + (set-face-attribute 'git-commit-summary nil :inherit nil) + (defun magit-fetch-all-on-load () + (magit-run-git-async "fetch" "--all")) + (add-hook 'magit-status-mode-hook 'magit-fetch-all-on-load) + (remove-hook 'magit-status-headers-hook 'magit-insert-tags-header) + (require 'forge) + (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)) + +(with-eval-after-load 'forge + (add-hook 'forge-edit-post-hook 'forge-create-pullreq-insert-single-commit-message) + (setopt forge-status-buffer-default-topic-filters + (forge--topics-spec :type 'topic :active nil :state 'open :order 'newest))) + +(global-diff-hl-mode) + +(bind-keys + ;; ("C-c M-x" . consult-mode-command) + ("C-c h" . consult-history) + ;; ("C-c k" . consult-kmacro) + ;; ("C-c m" . consult-man) + ;; ([remap Info-search] . consult-info) + + ;; ("C-x M-:" . consult-complex-command) + ("C-x b" . consult-buffer) + ("s-B" . consult-buffer) + ("C-x 4 b" . consult-buffer-other-window) + ("C-x 5 b" . consult-buffer-other-frame) + ;; ("C-x t b" . consult-buffer-other-tab) + ("C-x r b" . consult-bookmark) + ("C-x p b" . consult-project-buffer) + + ("M-#" . consult-register-load) + ("M-'" . consult-register-store) + ("C-M-#" . consult-register) + ("M-y" . consult-yank-pop) + ("M-g e" . consult-compile-error) + ("M-g f" . consult-flymake) + ("M-g g" . consult-goto-line) + ("M-g M-g" . consult-goto-line) + ("M-g o" . consult-org-heading) + ("M-g m" . consult-mark) + ("M-g k" . consult-global-mark) + ("M-g i" . consult-imenu) + ("M-g I" . consult-imenu-multi) + ("M-s r" . consult-ripgrep) + ("M-s l" . consult-line) + ("M-s L" . consult-line-multi) + ("M-s e" . consult-isearch-history) + :map isearch-mode-map + ("M-e" . consult-isearch-history) + ("M-s e" . consult-isearch-history) + ("M-s l" . consult-line) + ("M-s L" . consult-line-multi) + :map minibuffer-local-map + ("M-s" . consult-history) + ("M-r" . consult-history)) + +(advice-add 'register-preview :override #'consult-register-window) +(setopt register-preview-delay 0.5) + +(setopt xref-show-xrefs-function 'consult-xref) +(setopt xref-show-definitions-function 'consult-xref) + +(eval-when-compile + (require 'consult)) + +(with-eval-after-load 'consult + (consult-customize + consult-theme :preview-key '(:debounce 0.2 any) + consult-ripgrep consult-git-grep consult-grep consult-man + consult-bookmark consult-recent-file consult-xref + consult-source-bookmark consult-source-file-register + consult-source-recent-file + consult-source-buffer + ;; :preview-key "M-." + consult-source-project-recent-file + :preview-key '(:debounce 0.4 any))) + +(setopt consult-narrow-key "<") ;; "C-+" + +(setopt vertico-count 25) +(vertico-mode) + +(setopt completion-styles '(orderless basic)) + +(marginalia-mode) + +(setopt corfu-cycle t) +(setopt corfu-popupinfo-mode nil) +(setopt corfu-quit-at-boundary nil) +(setopt corfu-on-exact-match 'show) +(global-corfu-mode) +;; (setq completion-in-region-function #'consult-completion-in-region) + +(add-hook 'completion-at-point-functions 'cape-file) + +(use-package org + :bind (("C-c A" . org-agenda) + ("C-c C" . org-capture) + ("C-c l" . org-id-store-link)) + :custom + (org-babel-load-languages '((emacs-lisp . t) (sql . t))) + (org-confirm-babel-evaluate nil) + (org-link-keep-stored-after-insertion t) + (org-capture-templates + '(("t" "Todo" entry + (here) + "** TODO %?\n:LOGBOOK:\n- State \"TODO\" %U\n:END:\n") + ("w" "Work stuff") + ("wp" "Work Project" entry + (file+headline "~/org/todo/work.org.age" "Next Actions") + "*** PROJ %?\n- State \"PROJ\" %U\n") + ("wa" "Work Next Action" entry + (file+headline "~/org/todo/work.org.age" "Next Actions") + "*** TODO %?\n- State \"TODO\" %U\n") + ("p" "Personal stuff") + ("pp" "Personal Project" entry + (file+headline "~/org/todo/personal.org.age" "Next Actions") + "*** PROJ %?\n- State \"PROJ\" %U\n") + ("pa" "Personal Next Action" entry + (file+headline "~/org/todo/personal.org.age" "Next Actions") + "*** TODO %?\n- State \"TODO\" %U\n"))) + (org-agenda-files '("~/org/todo/work.org" "~/org/todo/personal.org.age")) + (org-agenda-todo-ignore-scheduled 'future) + (org-agenda-tags-todo-honor-ignore-options t) + (org-enforce-todo-dependencies t) + (org-agenda-dim-blocked-tasks 'invisible) + :config + (add-hook 'org-mode-hook 'visual-line-mode) + (set-face-attribute 'org-level-1 nil :font "Iosevka Aile") + (set-face-attribute 'org-level-2 nil :font "Iosevka Aile") + (set-face-attribute 'org-level-3 nil :font "Iosevka Aile") + (set-face-attribute 'org-level-4 nil :font "Iosevka Aile") + (set-face-attribute 'org-level-5 nil :font "Iosevka Aile") + (set-face-attribute 'org-level-6 nil :font "Iosevka Aile") + (set-face-attribute 'org-level-7 nil :font "Iosevka Aile") + (set-face-attribute 'org-level-8 nil :font "Iosevka Aile")) + +(defvar-local my/org-present-face-remapping-cookies nil) +(with-eval-after-load 'org-present + (require 'face-remap) + (require 'visual-fill-column) + + (defun my/org-present-start () + (setq-local my/org-present-face-remapping-cookies nil) + ;; Tweak font sizes + (dolist (face '((default :height 2.0) + (header-line :height 4.0 :background "#303446") + (org-document-title :height 1.75) + (org-code :height 2.0) + (org-verbatim :height 2.0) + (org-block :height 1.25) + (org-block-begin-line :height 0.7) + (org-level-1 :height 3.0) + (org-level-2 :height 3.0) + (org-level-3 :height 3.0) + (org-level-4 :height 3.0) + (org-level-5 :height 3.0) + (org-level-6 :height 3.0) + (org-level-7 :height 3.0) + (org-level-8 :height 3.0))) + (push (apply #'face-remap-add-relative face) my/org-present-face-remapping-cookies)) + + ;; Set a blank header line string to create blank space at the top + (setq header-line-format " ") + + ;; Display inline images automatically + (org-display-inline-images) + (org-present-hide-cursor) + + ;; Center the presentation and wrap lines + (visual-fill-column-mode 1) + (setq visual-fill-column-center-text t) + ;; (visual-line-mode 1) + ) + + (defun my/org-present-end () + ;; Reset font customizations + (dolist (cookie my/org-present-face-remapping-cookies) + (face-remap-remove-relative cookie)) + + ;; Clear the header line string so that it isn't displayed + (setq header-line-format nil) + + ;; Stop displaying inline images + (org-remove-inline-images) + (org-present-show-cursor) + + ;; Stop centering the document + (visual-fill-column-mode 0) + ;; (visual-line-mode 0) + ) + + (add-hook 'org-present-mode-hook 'my/org-present-start) + (add-hook 'org-present-mode-quit-hook 'my/org-present-end)) + +;; (require 'org-roam) +;; (bind-keys ("C-c n l" . org-roam-buffer-toggle) +;; ("C-c n f" . org-roam-node-find) +;; ("C-c n g" . org-roam-graph) +;; ("C-c n i" . org-roam-node-insert) +;; ("C-c n c" . org-roam-capture)) + +;; (setq org-roam-node-display-template (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag))) +;; (org-roam-db-autosync-mode) + +(use-package org-modern + :after org + :hook org-mode-hook) + +;; (define-key org-mode-map (kbd "C-c C-r") verb-command-map) + +(use-package eat + :commands (eat) + :hook (eshell-load-hook . eat-eshell-mode)) + +(use-package age + :custom + (age-default-identity "~/source/dotfiles/keys/yubi1age.txt") + (age-default-recipient "~/source/dotfiles/keys/myself.txt") + :config + (setenv "PINENTRY_PROGRAM" "pinentry-mac") + (age-file-enable)) + +(use-package claude-code + :bind-keymap ("C-c c" . claude-code-command-map) + :custom (claude-code-optimize-window-resize nil)) diff --git a/modules/home/home/slack.el b/modules/home/home/slack.el new file mode 100644 index 0000000..b5b5838 --- /dev/null +++ b/modules/home/home/slack.el @@ -0,0 +1,10 @@ +(use-package lui + :defer t + :custom + (lui-time-stamp-format "[%Y-%m-%d %H:%M]")) + +(use-package slack + :commands slack-start + :custom + (slack-render-profile-images-p nil) + (slack-buffer-function #'switch-to-buffer)) diff --git a/modules/home/slack.el b/modules/home/slack.el new file mode 100644 index 0000000..b5b5838 --- /dev/null +++ b/modules/home/slack.el @@ -0,0 +1,10 @@ +(use-package lui + :defer t + :custom + (lui-time-stamp-format "[%Y-%m-%d %H:%M]")) + +(use-package slack + :commands slack-start + :custom + (slack-render-profile-images-p nil) + (slack-buffer-function #'switch-to-buffer)) diff --git a/modules/home/wezterm.lua b/modules/home/wezterm.lua new file mode 100644 index 0000000..4de0145 --- /dev/null +++ b/modules/home/wezterm.lua @@ -0,0 +1,154 @@ +local config = {} + +config.font = wezterm.font 'Iosevka Term SS14' +config.font_size = 13.0 +-- config.font = wezterm.font("JetBrains Mono") +config.inactive_pane_hsb = { + saturation = 0.5, + brightness = 0.5, +} +config.pane_focus_follows_mouse = true +config.switch_to_last_active_tab_when_closing_tab = true +config.tab_bar_at_bottom = true +config.use_fancy_tab_bar = false +config.window_decorations = "RESIZE" +local workspace_switcher = wezterm.plugin.require("https://github.com/MLFlexer/smart_workspace_switcher.wezterm") +workspace_switcher.zoxide_path='~/.nix-profile/bin/zoxide' +wezterm.on('smart_workspace_switcher.workspace_switcher.created', function(window, path, workspace) + local editor = window:active_tab() + editor:set_title("edit") + editor:panes()[1]:send_text("hx\n") + local run = window:spawn_tab {} + run:set_title "run" + local term = window:spawn_tab {} + term:set_title "term" + editor:activate() + end) + +local function segments_for_right_status(window) + return { + window:active_workspace(), + } +end + +wezterm.on('update-status', + function(window, _) + local SOLID_LEFT_ARROW = utf8.char(0xe0b2) + local segments = segments_for_right_status(window) + + local color_scheme = window:effective_config().resolved_palette + -- Note the use of wezterm.color.parse here, this returns + -- a Color object, which comes with functionality for lightening + -- or darkening the colour (amongst other things). + local bg = wezterm.color.parse(color_scheme.background) + local fg = color_scheme.foreground + + -- Each powerline segment is going to be coloured progressively + -- darker/lighter depending on whether we're on a dark/light colour + -- scheme. Let's establish the "from" and "to" bounds of our gradient. + local gradient_to, gradient_from = bg + gradient_from = gradient_to:lighten(0.2) + + -- Yes, WezTerm supports creating gradients, because why not?! Although + -- they'd usually be used for setting high fidelity gradients on your terminal's + -- background, we'll use them here to give us a sample of the powerline segment + -- colours we need. + local gradient = wezterm.color.gradient( + { + orientation = 'Horizontal', + colors = { gradient_from, gradient_to }, + }, + #segments -- only gives us as many colours as we have segments. + ) + + -- We'll build up the elements to send to wezterm.format in this table. + local elements = {} + + for i, seg in ipairs(segments) do + local is_first = i == 1 + + if is_first then + table.insert(elements, { Background = { Color = 'none' } }) + end + table.insert(elements, { Foreground = { Color = gradient[i] } }) + table.insert(elements, { Text = SOLID_LEFT_ARROW }) + + table.insert(elements, { Foreground = { Color = fg } }) + table.insert(elements, { Background = { Color = gradient[i] } }) + table.insert(elements, { Text = ' ' .. seg .. ' ' }) + end + + window:set_right_status(wezterm.format(elements)) +end) +config.leader = { key = ' ', mods = 'CTRL', timeout_milliseconds = 10000 } +config.keys = { + { +key = "s", +mods = "CMD", +action = workspace_switcher.switch_workspace(), + }, + { +key = "-", +mods = "CMD", +action = wezterm.action.SplitVertical {}, + }, + { +key = "\\", +mods = "CMD", +action = wezterm.action.SplitHorizontal {}, + }, + { +key = "h", +mods = "CMD", +action = wezterm.action.ActivatePaneDirection "Left", + }, + { +key = "j", +mods = "CMD", +action = wezterm.action.ActivatePaneDirection "Down", + }, + { +key = "k", +mods = "CMD", +action = wezterm.action.ActivatePaneDirection "Up", + }, + { +key = "l", +mods = "CMD", +action = wezterm.action.ActivatePaneDirection "Right", + }, + { +key = "g", +mods = "CMD", +action = wezterm.action.SpawnCommandInNewTab { + args = { wezterm.home_dir .. "/.nix-profile/bin/zsh", "-l", "-i", "-c", "lazygit" } +} + }, + { +key = "t", +mods = "CMD", +action = wezterm.action.SpawnCommandInNewTab {} + }, + { +key = 'z', +mods = 'CMD', +action = wezterm.action.TogglePaneZoomState, + }, + { +key = 'd', +mods = 'CMD', +action = wezterm.action.CloseCurrentTab { confirm = true }, + }, +} + +for i = 1, 9 do + -- CTRL+ALT + number to activate that tab + table.insert(config.keys, { +key = tostring(i), +mods = 'LEADER', +action = wezterm.action.ActivateTab(i - 1), + }) +end +config.color_scheme = 'Catppuccin Frappe' + +return config