Configuration,
documented by itself.

Generated from the repository source.

Emacs Open documented config →

early-init.el

.emacs.d/early-init.el · 43 lines

;;; early-init.el --- early init code -*- lexical-binding: t -*-
;;; Commentary:

;; only show menu bar when running Emacs with GUI on macOS
(unless (and (string-equal system-type "darwin") (display-graphic-p))
  (menu-bar-mode -1))

;;; Code:
;; Hide vertical scroll bar
(scroll-bar-mode -1)

;; Disable package.el in favor of elpaca.el
(setq package-enable-at-startup nil)

;; Hide toolbar
(tool-bar-mode -1)

;; Set transparency
(set-frame-parameter nil 'alpha 95)

;; Set fullscreen via default-frame-alist to avoid a post-init frame
;; resize.
(add-to-list 'default-frame-alist '(fullscreen . maximized))

;; Bump eval depth — yaml.el's giant pcase grammar exceeds default
;; 1600 during eager macro expansion
(setopt max-lisp-eval-depth 5000)

;; Change GC config to speed up startup time
;; Copied from https://github.com/hlissner/doom-emacs/issues/310#issuecomment-354424413
(defvar last-file-name-handler-alist file-name-handler-alist)
(setq gc-cons-threshold most-positive-fixnum
      gc-cons-percentage 0.6
      file-name-handler-alist nil)

;; Restore GC previous config
(add-hook 'emacs-startup-hook (lambda ()
                                (setq gc-cons-threshold (* 100 1024 1024)
                                      gc-cons-percentage 0.1
                                      file-name-handler-alist last-file-name-handler-alist)))

(provide 'early-init)
;;; early-init.el ends here

elpaca-lockfile.eld

.emacs.d/elpaca-lockfile.eld · 1,673 lines

((annalist :source "elpaca-menu-lock-file" :recipe
	   (:package "annalist" :fetcher github :repo
		     "noctuid/annalist.el" :files
		     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		      "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		      "doc/*.texinfo" "lisp/*.el" "docs/dir"
		      "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		      (:exclude ".dir-locals.el" "test.el" "tests.el"
				"*-test.el" "*-tests.el" "LICENSE"
				"README*" "*-pkg.el"))
		     :source "elpaca-menu-lock-file" :protocol https
		     :inherit t :depth treeless :ref
		     "e1ef5dad75fa502d761f70d9ddf1aeb1c423f41d" :id
		     annalist :type git))
 (cider :source "elpaca-menu-lock-file" :recipe
	(:package "cider" :fetcher github :repo "clojure-emacs/cider"
		  :files
		  ("lisp/*.el" "bin/*.sh" "*.el" "clojure.sh"
		   "lein.sh" (:exclude ".dir-locals.el"))
		  :old-names (nrepl) :source "elpaca-menu-lock-file"
		  :protocol https :inherit t :depth treeless :ref
		  "eb78711b7fb0cb89a9eb1b854aabb174b705f7b2" :id cider
		  :type git))
 (clj-refactor :source "elpaca-menu-lock-file" :recipe
	       (:package "clj-refactor" :fetcher github :repo
			 "clojure-emacs/clj-refactor.el" :files
			 (:defaults "CHANGELOG.md") :source
			 "elpaca-menu-lock-file" :protocol https
			 :inherit t :depth treeless :ref
			 "dc1bbc8cdaa723bdbb6669ea7d280625c370755d"
			 :id clj-refactor :type git))
 (cloak-mode :source "elpaca-menu-lock-file" :recipe
	     (:package "cloak-mode" :fetcher github :repo
		       "erickgnavar/cloak-mode" :files
		       ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			"*.texinfo" "doc/dir" "doc/*.info"
			"doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			"docs/dir" "docs/*.info" "docs/*.texi"
			"docs/*.texinfo"
			(:exclude ".dir-locals.el" "test.el"
				  "tests.el" "*-test.el" "*-tests.el"
				  "LICENSE" "README*" "*-pkg.el"))
		       :source "elpaca-menu-lock-file" :protocol https
		       :inherit t :depth treeless :ref
		       "8a8d06a280c340a7e1639f60c8c44cd4b4127e6b" :id
		       cloak-mode :type git))
 (clojure-mode :source "elpaca-menu-lock-file" :recipe
	       (:package "clojure-mode" :repo
			 "clojure-emacs/clojure-mode" :fetcher github
			 :files ("clojure-mode.el") :source
			 "elpaca-menu-lock-file" :protocol https
			 :inherit t :depth treeless :ref
			 "eabe29b076fff19db552d36d4babaa36ecf0b704"
			 :id clojure-mode :type git))
 (color-theme-sanityinc-tomorrow :source "elpaca-menu-lock-file"
				 :recipe
				 (:package
				  "color-theme-sanityinc-tomorrow"
				  :repo
				  "purcell/color-theme-sanityinc-tomorrow"
				  :fetcher github :files
				  ("*.el" "*.el.in" "dir" "*.info"
				   "*.texi" "*.texinfo" "doc/dir"
				   "doc/*.info" "doc/*.texi"
				   "doc/*.texinfo" "lisp/*.el"
				   "docs/dir" "docs/*.info"
				   "docs/*.texi" "docs/*.texinfo"
				   (:exclude ".dir-locals.el"
					     "test.el" "tests.el"
					     "*-test.el" "*-tests.el"
					     "LICENSE" "README*"
					     "*-pkg.el"))
				  :source "elpaca-menu-lock-file"
				  :protocol https :inherit t :depth
				  treeless :ref
				  "9ce4bc783acc4ebebed1b13427873cdffc1b7960"
				  :id color-theme-sanityinc-tomorrow
				  :type git))
 (company :source "elpaca-menu-lock-file" :recipe
	  (:package "company" :fetcher github :repo
		    "company-mode/company-mode" :files
		    (:defaults "icons"
			       ("images/small"
				"doc/images/small/*.png"))
		    :source "elpaca-menu-lock-file" :protocol https
		    :inherit t :depth treeless :ref
		    "1bf7dcd3ce1c8e192a402360930ca18790fec5d4" :id
		    company :type git))
 (compat :source "elpaca-menu-lock-file" :recipe
	 (:package "compat" :fetcher github :repo
		   "emacs-compat/compat" :files
		   ("compat.el" "compat-*.el" "compat.texi") :source
		   "elpaca-menu-lock-file" :protocol https :inherit t
		   :depth treeless :ref
		   "ad1806f7f677c4e295ed1e603ae6a6a5d9f9d399" :id
		   compat :type git))
 (cond-let
   :source "elpaca-menu-lock-file" :recipe
   (:package "cond-let" :fetcher github :repo "tarsius/cond-let"
	     :files
	     ("*.el" "*.el.in" "dir" "*.info" "*.texi" "*.texinfo"
	      "doc/dir" "doc/*.info" "doc/*.texi" "doc/*.texinfo"
	      "lisp/*.el" "docs/dir" "docs/*.info" "docs/*.texi"
	      "docs/*.texinfo"
	      (:exclude ".dir-locals.el" "test.el" "tests.el"
			"*-test.el" "*-tests.el" "LICENSE" "README*"
			"*-pkg.el"))
	     :source "elpaca-menu-lock-file" :id cond-let :type git
	     :protocol https :inherit t :depth treeless :ref
	     "21b9e9835756ff5cd1acb971cf9eb56fff671c8b"))
 (consult :source "elpaca-menu-lock-file" :recipe
	  (:package "consult" :repo "minad/consult" :fetcher github
		    :files
		    ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		     "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		     "doc/*.texinfo" "lisp/*.el" "docs/dir"
		     "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		     (:exclude ".dir-locals.el" "test.el" "tests.el"
			       "*-test.el" "*-tests.el" "LICENSE"
			       "README*" "*-pkg.el"))
		    :source "elpaca-menu-lock-file" :protocol https
		    :inherit t :depth treeless :ref
		    "74e6cc77609cb36cd61aa2a23848659fbb66f05a" :id
		    consult :type git))
 (consult-yasnippet :source "elpaca-menu-lock-file" :recipe
		    (:package "consult-yasnippet" :fetcher github
			      :repo "mohkale/consult-yasnippet" :files
			      ("*.el" "*.el.in" "dir" "*.info"
			       "*.texi" "*.texinfo" "doc/dir"
			       "doc/*.info" "doc/*.texi"
			       "doc/*.texinfo" "lisp/*.el" "docs/dir"
			       "docs/*.info" "docs/*.texi"
			       "docs/*.texinfo"
			       (:exclude ".dir-locals.el" "test.el"
					 "tests.el" "*-test.el"
					 "*-tests.el" "LICENSE"
					 "README*" "*-pkg.el"))
			      :source "elpaca-menu-lock-file"
			      :protocol https :inherit t :depth
			      treeless :ref
			      "834d39acfe8a7d2c304afbe4d649b9372118c756"
			      :id consult-yasnippet :type git))
 (csv-mode :source "elpaca-menu-lock-file" :recipe
	   (:package "csv-mode" :repo
		     ("https://git.savannah.gnu.org/git/emacs/elpa.git"
		      . "csv-mode")
		     :branch "externals/csv-mode" :files
		     ("*" (:exclude ".git")) :source
		     "elpaca-menu-lock-file" :protocol https :inherit
		     t :depth treeless :ref
		     "849ce3e754f291c3643bc36ed802226606955c3f" :id
		     csv-mode :type git))
 (d2-mode :source "elpaca-menu-lock-file" :recipe
	  (:package "d2-mode" :repo "andorsk/d2-mode" :fetcher github
		    :files
		    ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		     "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		     "doc/*.texinfo" "lisp/*.el" "docs/dir"
		     "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		     (:exclude ".dir-locals.el" "test.el" "tests.el"
			       "*-test.el" "*-tests.el" "LICENSE"
			       "README*" "*-pkg.el"))
		    :source "elpaca-menu-lock-file" :protocol https
		    :inherit t :depth treeless :ref
		    "e1fc7d6c1915acaf476060c0f79b8bdef6bd1952" :id
		    d2-mode :type git))
 (dart-mode :source "elpaca-menu-lock-file" :recipe
	    (:package "dart-mode" :fetcher github :repo
		      "emacsorphanage/dart-mode" :files
		      ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		       "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		       "doc/*.texinfo" "lisp/*.el" "docs/dir"
		       "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		       (:exclude ".dir-locals.el" "test.el" "tests.el"
				 "*-test.el" "*-tests.el" "LICENSE"
				 "README*" "*-pkg.el"))
		      :source "elpaca-menu-lock-file" :protocol https
		      :inherit t :depth treeless :ref
		      "02e919c1cf200b4938139c18068577faff1fb364" :id
		      dart-mode :type git))
 (dash :source "elpaca-menu-lock-file" :recipe
       (:package "dash" :fetcher github :repo "magnars/dash.el" :files
		 ("dash.el" "dash.texi") :source
		 "elpaca-menu-lock-file" :protocol https :inherit t
		 :depth treeless :ref
		 "1de9dcb83eacfb162b6d9a118a4770b1281bcd84" :id dash
		 :type git))
 (denote :source "elpaca-menu-lock-file" :recipe
	 (:package "denote" :repo
		   ("https://github.com/protesilaos/denote" . "denote")
		   :files
		   ("*" (:exclude ".git" "COPYING" "doclicense.texi"))
		   :source "elpaca-menu-lock-file" :protocol https
		   :inherit t :depth treeless :ref
		   "212a1b3c17ad99986e593901b3385c9c581b70e4" :id
		   denote :type git))
 (diff-hl :source "elpaca-menu-lock-file" :recipe
	  (:package "diff-hl" :fetcher github :repo "dgutov/diff-hl"
		    :files
		    ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		     "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		     "doc/*.texinfo" "lisp/*.el" "docs/dir"
		     "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		     (:exclude ".dir-locals.el" "test.el" "tests.el"
			       "*-test.el" "*-tests.el" "LICENSE"
			       "README*" "*-pkg.el"))
		    :source "elpaca-menu-lock-file" :protocol https
		    :inherit t :depth treeless :ref
		    "685ea2d50607a9902f1c6c141ee6268d759b9b25" :id
		    diff-hl :type git))
 (dired-hacks-utils :source "elpaca-menu-lock-file" :recipe
		    (:package "dired-hacks-utils" :fetcher github
			      :repo "Fuco1/dired-hacks" :files
			      ("dired-hacks-utils.el") :source
			      "elpaca-menu-lock-file" :protocol https
			      :inherit t :depth treeless :ref
			      "e9e408e8571aee5574ca0a431ef15cac5a3585d4"
			      :id dired-hacks-utils :type git))
 (dired-preview :source "elpaca-menu-lock-file" :recipe
		(:package "dired-preview" :repo
			  ("https://github.com/protesilaos/dired-preview"
			   . "dired-preview")
			  :files
			  ("*"
			   (:exclude ".git" "COPYING"
				     "doclicense.texi"))
			  :source "elpaca-menu-lock-file" :protocol
			  https :inherit t :depth treeless :ref
			  "76e2d2a6675807f41c18c2f000dfaec35ed59100"
			  :id dired-preview :type git))
 (dired-subtree :source "elpaca-menu-lock-file" :recipe
		(:package "dired-subtree" :fetcher github :repo
			  "Fuco1/dired-hacks" :files
			  ("dired-subtree.el") :source
			  "elpaca-menu-lock-file" :protocol https
			  :inherit t :depth treeless :ref
			  "e9e408e8571aee5574ca0a431ef15cac5a3585d4"
			  :id dired-subtree :type git))
 (doom-modeline :source "elpaca-menu-lock-file" :recipe
		(:package "doom-modeline" :repo
			  "seagle0128/doom-modeline" :fetcher github
			  :files
			  ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			   "*.texinfo" "doc/dir" "doc/*.info"
			   "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			   "docs/dir" "docs/*.info" "docs/*.texi"
			   "docs/*.texinfo"
			   (:exclude ".dir-locals.el" "test.el"
				     "tests.el" "*-test.el"
				     "*-tests.el" "LICENSE" "README*"
				     "*-pkg.el"))
			  :source "elpaca-menu-lock-file" :protocol
			  https :inherit t :depth treeless :ref
			  "33edc978783a311479ca8edf3f7437d7e930eb10"
			  :id doom-modeline :type git))
 (dumb-jump :source "elpaca-menu-lock-file" :recipe
	    (:package "dumb-jump" :repo "jacktasia/dumb-jump" :fetcher
		      github :files
		      ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		       "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		       "doc/*.texinfo" "lisp/*.el" "docs/dir"
		       "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		       (:exclude ".dir-locals.el" "test.el" "tests.el"
				 "*-test.el" "*-tests.el" "LICENSE"
				 "README*" "*-pkg.el"))
		      :source "elpaca-menu-lock-file" :protocol https
		      :inherit t :depth treeless :ref
		      "9f3967b0bea038c37d4a6c6bb96144574bb706ee" :id
		      dumb-jump :type git))
 (edit-indirect :source "elpaca-menu-lock-file" :recipe
		(:package "edit-indirect" :fetcher github :repo
			  "Fanael/edit-indirect" :files
			  ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			   "*.texinfo" "doc/dir" "doc/*.info"
			   "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			   "docs/dir" "docs/*.info" "docs/*.texi"
			   "docs/*.texinfo"
			   (:exclude ".dir-locals.el" "test.el"
				     "tests.el" "*-test.el"
				     "*-tests.el" "LICENSE" "README*"
				     "*-pkg.el"))
			  :source "elpaca-menu-lock-file" :protocol
			  https :inherit t :depth treeless :ref
			  "82a28d8a85277cfe453af464603ea330eae41c05"
			  :id edit-indirect :type git))
 (eglot-java :source "elpaca-menu-lock-file" :recipe
	     (:package "eglot-java" :repo "yveszoundi/eglot-java"
		       :fetcher github :files
		       ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			"*.texinfo" "doc/dir" "doc/*.info"
			"doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			"docs/dir" "docs/*.info" "docs/*.texi"
			"docs/*.texinfo"
			(:exclude ".dir-locals.el" "test.el"
				  "tests.el" "*-test.el" "*-tests.el"
				  "LICENSE" "README*" "*-pkg.el"))
		       :source "elpaca-menu-lock-file" :protocol https
		       :inherit t :depth treeless :ref
		       "b42b5190f3f59976d330fcec5fd27fc8e2701336" :id
		       eglot-java :type git))
 (elisp-refs :source "elpaca-menu-lock-file" :recipe
	     (:package "elisp-refs" :repo "Wilfred/elisp-refs"
		       :fetcher github :files
		       (:defaults (:exclude "elisp-refs-bench.el"))
		       :source "elpaca-menu-lock-file" :protocol https
		       :inherit t :depth treeless :ref
		       "541a064c3ce27867872cf708354a65d83baf2a6d" :id
		       elisp-refs :type git))
 (elm-mode :source "elpaca-menu-lock-file" :recipe
	   (:package "elm-mode" :fetcher github :repo
		     "jcollard/elm-mode" :files (:defaults "elm.tags")
		     :source "elpaca-menu-lock-file" :protocol https
		     :inherit t :depth treeless :ref
		     "699841865e1bd5b7f2077baa7121510b6bcad3c7" :id
		     elm-mode :type git))
 (elpaca :source
   "elpaca-menu-lock-file" :recipe
   (:source nil :package "elpaca" :id elpaca :repo
	    "https://github.com/progfolio/elpaca.git" :ref
	    "e974e6f4b5ea2e974f4d7686c5d956aff455594d" :depth nil
	    :inherit ignore :files
	    (:defaults "elpaca-test.el" (:exclude "extensions"))
	    :build (:not elpaca-activate) :type git :protocol https))
 (elpaca-use-package :source "elpaca-menu-lock-file" :recipe
		     (:package "elpaca-use-package" :wait t :repo
			       "https://github.com/progfolio/elpaca.git"
			       :files
			       ("extensions/elpaca-use-package.el")
			       :main
			       "extensions/elpaca-use-package.el"
			       :build (:not elpaca--compile-info)
			       :source "elpaca-menu-lock-file"
			       :protocol https :inherit t :depth
			       treeless :ref
			       "e974e6f4b5ea2e974f4d7686c5d956aff455594d"
			       :id elpaca-use-package :type git))
 (embark :source "elpaca-menu-lock-file" :recipe
	 (:package "embark" :repo "oantolin/embark" :fetcher github
		   :files ("embark.el" "embark-org.el" "embark.texi")
		   :source "elpaca-menu-lock-file" :protocol https
		   :inherit t :depth treeless :ref
		   "195add1f1ccd1059472c9df7334c97c4d155425e" :id
		   embark :type git))
 (embark-consult :source "elpaca-menu-lock-file" :recipe
		 (:package "embark-consult" :repo "oantolin/embark"
			   :fetcher github :files
			   ("embark-consult.el") :source
			   "elpaca-menu-lock-file" :protocol https
			   :inherit t :depth treeless :ref
			   "195add1f1ccd1059472c9df7334c97c4d155425e"
			   :id embark-consult :type git))
 (emmet-mode :source "elpaca-menu-lock-file" :recipe
	     (:package "emmet-mode" :fetcher github :repo
		       "smihica/emmet-mode" :files
		       ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			"*.texinfo" "doc/dir" "doc/*.info"
			"doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			"docs/dir" "docs/*.info" "docs/*.texi"
			"docs/*.texinfo"
			(:exclude ".dir-locals.el" "test.el"
				  "tests.el" "*-test.el" "*-tests.el"
				  "LICENSE" "README*" "*-pkg.el"))
		       :source "elpaca-menu-lock-file" :protocol https
		       :inherit t :depth treeless :ref
		       "322d3bb112fced57d63b44863357f7a0b7eee1e3" :id
		       emmet-mode :type git))
 (erlang-ts-mode :source "elpaca-menu-lock-file" :recipe
		 (:source "elpaca-menu-lock-file" :protocol https
			  :inherit t :depth treeless :host github
			  :repo "sebastiw/erlang-ts-mode" :package
			  "erlang-ts-mode" :ref
			  "ba5a01a2d7731a6fbf6129c6c8556fbceeb7d8a9"
			  :id erlang-ts-mode :type git))
 (evil :source "elpaca-menu-lock-file" :recipe
       (:package "evil" :repo "emacs-evil/evil" :fetcher github :files
		 (:defaults "doc/build/texinfo/evil.texi"
			    (:exclude "evil-test-helpers.el"))
		 :source "elpaca-menu-lock-file" :protocol https
		 :inherit t :depth treeless :ref
		 "729d9a58b387704011a115c9200614e32da3cefc" :id evil
		 :type git))
 (evil-collection :source "elpaca-menu-lock-file" :recipe
		  (:package "evil-collection" :fetcher github :repo
			    "emacs-evil/evil-collection" :files
			    (:defaults "modes") :source
			    "elpaca-menu-lock-file" :protocol https
			    :inherit t :depth treeless :ref
			    "7ab9179591bc2f9474f480f52ec8dc44cf0a05f1"
			    :id evil-collection :type git))
 (evil-commentary :source "elpaca-menu-lock-file" :recipe
		  (:package "evil-commentary" :repo
			    "linktohack/evil-commentary" :fetcher
			    github :files
			    ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			     "*.texinfo" "doc/dir" "doc/*.info"
			     "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			     "docs/dir" "docs/*.info" "docs/*.texi"
			     "docs/*.texinfo"
			     (:exclude ".dir-locals.el" "test.el"
				       "tests.el" "*-test.el"
				       "*-tests.el" "LICENSE"
				       "README*" "*-pkg.el"))
			    :source "elpaca-menu-lock-file" :protocol
			    https :inherit t :depth treeless :ref
			    "c5945f28ce47644c828aac1f5f6ec335478d17fb"
			    :id evil-commentary :type git))
 (evil-matchit :source "elpaca-menu-lock-file" :recipe
	       (:package "evil-matchit" :fetcher github :repo
			 "redguardtoo/evil-matchit" :files
			 ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			  "*.texinfo" "doc/dir" "doc/*.info"
			  "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			  "docs/dir" "docs/*.info" "docs/*.texi"
			  "docs/*.texinfo"
			  (:exclude ".dir-locals.el" "test.el"
				    "tests.el" "*-test.el"
				    "*-tests.el" "LICENSE" "README*"
				    "*-pkg.el"))
			 :source "elpaca-menu-lock-file" :protocol
			 https :inherit t :depth treeless :ref
			 "8a94e56bd2987c6d0c1a85fbf189f9a86e623cde"
			 :id evil-matchit :type git))
 (evil-surround :source "elpaca-menu-lock-file" :recipe
		(:package "evil-surround" :repo
			  "emacs-evil/evil-surround" :fetcher github
			  :old-names (surround) :files
			  ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			   "*.texinfo" "doc/dir" "doc/*.info"
			   "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			   "docs/dir" "docs/*.info" "docs/*.texi"
			   "docs/*.texinfo"
			   (:exclude ".dir-locals.el" "test.el"
				     "tests.el" "*-test.el"
				     "*-tests.el" "LICENSE" "README*"
				     "*-pkg.el"))
			  :source "elpaca-menu-lock-file" :protocol
			  https :inherit t :depth treeless :ref
			  "da05c60b0621cf33161bb4335153f75ff5c29d91"
			  :id evil-surround :type git))
 (exec-path-from-shell :source "elpaca-menu-lock-file" :recipe
		       (:package "exec-path-from-shell" :fetcher
				 github :repo
				 "purcell/exec-path-from-shell" :files
				 ("*.el" "*.el.in" "dir" "*.info"
				  "*.texi" "*.texinfo" "doc/dir"
				  "doc/*.info" "doc/*.texi"
				  "doc/*.texinfo" "lisp/*.el"
				  "docs/dir" "docs/*.info"
				  "docs/*.texi" "docs/*.texinfo"
				  (:exclude ".dir-locals.el" "test.el"
					    "tests.el" "*-test.el"
					    "*-tests.el" "LICENSE"
					    "README*" "*-pkg.el"))
				 :source "elpaca-menu-lock-file"
				 :protocol https :inherit t :depth
				 treeless :ref
				 "4896a797252fbfdac32fb77508500ac7d220f717"
				 :id exec-path-from-shell :type git))
 (f :source "elpaca-menu-lock-file" :recipe
    (:package "f" :fetcher github :repo "rejeep/f.el" :files
	      ("*.el" "*.el.in" "dir" "*.info" "*.texi" "*.texinfo"
	       "doc/dir" "doc/*.info" "doc/*.texi" "doc/*.texinfo"
	       "lisp/*.el" "docs/dir" "docs/*.info" "docs/*.texi"
	       "docs/*.texinfo"
	       (:exclude ".dir-locals.el" "test.el" "tests.el"
			 "*-test.el" "*-tests.el" "LICENSE" "README*"
			 "*-pkg.el"))
	      :source "elpaca-menu-lock-file" :protocol https :inherit
	      t :depth treeless :ref
	      "931b6d0667fe03e7bf1c6c282d6d8d7006143c52" :id f :type
	      git))
 (flutter :source "elpaca-menu-lock-file" :recipe
	  (:package "flutter" :repo "amake/flutter.el" :fetcher github
		    :files
		    ("flutter.el" "flutter-project.el"
		     "flutter-l10n.el")
		    :source "elpaca-menu-lock-file" :protocol https
		    :inherit t :depth treeless :ref
		    "e71235d400787d977da7ed792709437899c2a03c" :id
		    flutter :type git))
 (flymake-biome :source "elpaca-menu-lock-file" :recipe
		(:package "flymake-biome" :fetcher github :repo
			  "erickgnavar/flymake-biome" :files
			  ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			   "*.texinfo" "doc/dir" "doc/*.info"
			   "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			   "docs/dir" "docs/*.info" "docs/*.texi"
			   "docs/*.texinfo"
			   (:exclude ".dir-locals.el" "test.el"
				     "tests.el" "*-test.el"
				     "*-tests.el" "LICENSE" "README*"
				     "*-pkg.el"))
			  :source "elpaca-menu-lock-file" :protocol
			  https :inherit t :depth treeless :ref
			  "03fa55d23fdc80fb4bc963cd144da460e7da0220"
			  :id flymake-biome :type git))
 (flymake-markdownlint :source "elpaca-menu-lock-file" :recipe
		       (:package "flymake-markdownlint" :fetcher
				 codeberg :repo
				 "erickgnavar/flymake-markdownlint"
				 :files
				 ("*.el" "*.el.in" "dir" "*.info"
				  "*.texi" "*.texinfo" "doc/dir"
				  "doc/*.info" "doc/*.texi"
				  "doc/*.texinfo" "lisp/*.el"
				  "docs/dir" "docs/*.info"
				  "docs/*.texi" "docs/*.texinfo"
				  (:exclude ".dir-locals.el" "test.el"
					    "tests.el" "*-test.el"
					    "*-tests.el" "LICENSE"
					    "README*" "*-pkg.el"))
				 :source "elpaca-menu-lock-file"
				 :protocol https :inherit t :depth
				 treeless :host github :ref
				 "5167625b9ab958992ca68e225a0763c5c26b4eeb"
				 :id flymake-markdownlint :type git))
 (flymake-ruff :source "elpaca-menu-lock-file" :recipe
	       (:package "flymake-ruff" :fetcher github :repo
			 "erickgnavar/flymake-ruff" :files
			 ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			  "*.texinfo" "doc/dir" "doc/*.info"
			  "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			  "docs/dir" "docs/*.info" "docs/*.texi"
			  "docs/*.texinfo"
			  (:exclude ".dir-locals.el" "test.el"
				    "tests.el" "*-test.el"
				    "*-tests.el" "LICENSE" "README*"
				    "*-pkg.el"))
			 :source "elpaca-menu-lock-file" :protocol
			 https :inherit t :depth treeless :ref
			 "970acdac865d5b6ee8541b82a413f3a107b9332e"
			 :id flymake-ruff :type git))
 (flymake-sqlfluff :source "elpaca-menu-lock-file" :recipe
		   (:package "flymake-sqlfluff" :fetcher github :repo
			     "erickgnavar/flymake-sqlfluff" :files
			     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			      "*.texinfo" "doc/dir" "doc/*.info"
			      "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			      "docs/dir" "docs/*.info" "docs/*.texi"
			      "docs/*.texinfo"
			      (:exclude ".dir-locals.el" "test.el"
					"tests.el" "*-test.el"
					"*-tests.el" "LICENSE"
					"README*" "*-pkg.el"))
			     :source "elpaca-menu-lock-file" :protocol
			     https :inherit t :depth treeless :ref
			     "0a836d7a919723ae5897fce01c3c7d651a30e8c6"
			     :id flymake-sqlfluff :type git))
 (git-backup :source "elpaca-menu-lock-file" :recipe
	     (:package "git-backup" :fetcher github :repo
		       "antham/git-backup" :files
		       ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			"*.texinfo" "doc/dir" "doc/*.info"
			"doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			"docs/dir" "docs/*.info" "docs/*.texi"
			"docs/*.texinfo"
			(:exclude ".dir-locals.el" "test.el"
				  "tests.el" "*-test.el" "*-tests.el"
				  "LICENSE" "README*" "*-pkg.el"))
		       :source "elpaca-menu-lock-file" :protocol https
		       :inherit t :depth treeless :ref
		       "67e38c659c918e98642171ba3f385a15182347f4" :id
		       git-backup :type git))
 (git-link :source "elpaca-menu-lock-file" :recipe
	   (:package "git-link" :fetcher github :repo "sshaw/git-link"
		     :files
		     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		      "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		      "doc/*.texinfo" "lisp/*.el" "docs/dir"
		      "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		      (:exclude ".dir-locals.el" "test.el" "tests.el"
				"*-test.el" "*-tests.el" "LICENSE"
				"README*" "*-pkg.el"))
		     :source "elpaca-menu-lock-file" :protocol https
		     :inherit t :depth treeless :ref
		     "40f7b1674d2c703199ff2f82b464f17aa6f61b4b" :id
		     git-link :type git))
 (git-modes :source "elpaca-menu-lock-file" :recipe
	    (:package "git-modes" :fetcher github :repo
		      "magit/git-modes" :old-names
		      (gitattributes-mode gitconfig-mode
					  gitignore-mode)
		      :files
		      ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		       "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		       "doc/*.texinfo" "lisp/*.el" "docs/dir"
		       "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		       (:exclude ".dir-locals.el" "test.el" "tests.el"
				 "*-test.el" "*-tests.el" "LICENSE"
				 "README*" "*-pkg.el"))
		      :source "elpaca-menu-lock-file" :protocol https
		      :inherit t :depth treeless :ref
		      "f99010bbeb8b6d8a0819fac0195a2ef0159d08f0" :id
		      git-modes :type git))
 (git-timemachine :source "elpaca-menu-lock-file" :recipe
		  (:package "git-timemachine" :fetcher codeberg :repo
			    "pidu/git-timemachine" :files
			    ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			     "*.texinfo" "doc/dir" "doc/*.info"
			     "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			     "docs/dir" "docs/*.info" "docs/*.texi"
			     "docs/*.texinfo"
			     (:exclude ".dir-locals.el" "test.el"
				       "tests.el" "*-test.el"
				       "*-tests.el" "LICENSE"
				       "README*" "*-pkg.el"))
			    :source "elpaca-menu-lock-file" :protocol
			    https :inherit t :depth treeless :ref
			    "d1346a76122595aeeb7ebb292765841c6cfd417b"
			    :id git-timemachine :type git))
 (goto-chg :source "elpaca-menu-lock-file" :recipe
	   (:package "goto-chg" :repo "emacs-evil/goto-chg" :fetcher
		     github :files
		     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		      "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		      "doc/*.texinfo" "lisp/*.el" "docs/dir"
		      "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		      (:exclude ".dir-locals.el" "test.el" "tests.el"
				"*-test.el" "*-tests.el" "LICENSE"
				"README*" "*-pkg.el"))
		     :source "elpaca-menu-lock-file" :protocol https
		     :inherit t :depth treeless :ref
		     "72f556524b88e9d30dc7fc5b0dc32078c166fda7" :id
		     goto-chg :type git))
 (graphql-mode :source "elpaca-menu-lock-file" :recipe
	       (:package "graphql-mode" :repo "davazp/graphql-mode"
			 :fetcher github :files
			 ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			  "*.texinfo" "doc/dir" "doc/*.info"
			  "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			  "docs/dir" "docs/*.info" "docs/*.texi"
			  "docs/*.texinfo"
			  (:exclude ".dir-locals.el" "test.el"
				    "tests.el" "*-test.el"
				    "*-tests.el" "LICENSE" "README*"
				    "*-pkg.el"))
			 :source "elpaca-menu-lock-file" :protocol
			 https :inherit t :depth treeless :ref
			 "ee49531935ede7a2c9597713e13a4c9d33ef2220"
			 :id graphql-mode :type git))
 (haskell-mode :source "elpaca-menu-lock-file" :recipe
	       (:package "haskell-mode" :repo "haskell/haskell-mode"
			 :fetcher github :files
			 (:defaults "NEWS" "logo.svg") :source
			 "elpaca-menu-lock-file" :protocol https
			 :inherit t :depth treeless :ref
			 "495fb9688e12ed1bf945b4876f124ee38b84c039"
			 :id haskell-mode :type git))
 (hcl-mode :source "elpaca-menu-lock-file" :recipe
	   (:package "hcl-mode" :repo "hcl-emacs/hcl-mode" :fetcher
		     github :files
		     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		      "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		      "doc/*.texinfo" "lisp/*.el" "docs/dir"
		      "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		      (:exclude ".dir-locals.el" "test.el" "tests.el"
				"*-test.el" "*-tests.el" "LICENSE"
				"README*" "*-pkg.el"))
		     :source "elpaca-menu-lock-file" :protocol https
		     :inherit t :depth treeless :ref
		     "1da895ed75d28d9f87cbf9b74f075d90ba31c0ed" :id
		     hcl-mode :type git))
 (helpful :source "elpaca-menu-lock-file" :recipe
	  (:package "helpful" :repo "Wilfred/helpful" :fetcher github
		    :files
		    ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		     "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		     "doc/*.texinfo" "lisp/*.el" "docs/dir"
		     "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		     (:exclude ".dir-locals.el" "test.el" "tests.el"
			       "*-test.el" "*-tests.el" "LICENSE"
			       "README*" "*-pkg.el"))
		    :source "elpaca-menu-lock-file" :protocol https
		    :inherit t :depth treeless :ref
		    "34328c639ed7aad371a3f57209acad2a5bb66401" :id
		    helpful :type git))
 (hide-mode-line :source "elpaca-menu-lock-file" :recipe
		 (:package "hide-mode-line" :repo
			   "hlissner/emacs-hide-mode-line" :fetcher
			   github :files
			   ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			    "*.texinfo" "doc/dir" "doc/*.info"
			    "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			    "docs/dir" "docs/*.info" "docs/*.texi"
			    "docs/*.texinfo"
			    (:exclude ".dir-locals.el" "test.el"
				      "tests.el" "*-test.el"
				      "*-tests.el" "LICENSE" "README*"
				      "*-pkg.el"))
			   :source "elpaca-menu-lock-file" :protocol
			   https :inherit t :depth treeless :ref
			   "ddd154f1e04d666cd004bf8212ead8684429350d"
			   :id hide-mode-line :type git))
 (highlight-thing :source "elpaca-menu-lock-file" :recipe
		  (:package "highlight-thing" :fetcher github :repo
			    "fgeller/highlight-thing.el" :files
			    ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			     "*.texinfo" "doc/dir" "doc/*.info"
			     "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			     "docs/dir" "docs/*.info" "docs/*.texi"
			     "docs/*.texinfo"
			     (:exclude ".dir-locals.el" "test.el"
				       "tests.el" "*-test.el"
				       "*-tests.el" "LICENSE"
				       "README*" "*-pkg.el"))
			    :source "elpaca-menu-lock-file" :protocol
			    https :inherit t :depth treeless :ref
			    "ad788d7a7ee9eb287a8cca3adb21510b89270dca"
			    :id highlight-thing :type git))
 (htmlize :source "elpaca-menu-lock-file" :recipe
	  (:package "htmlize" :fetcher github :repo
		    "hniksic/emacs-htmlize" :files
		    ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		     "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		     "doc/*.texinfo" "lisp/*.el" "docs/dir"
		     "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		     (:exclude ".dir-locals.el" "test.el" "tests.el"
			       "*-test.el" "*-tests.el" "LICENSE"
			       "README*" "*-pkg.el"))
		    :source "elpaca-menu-lock-file" :protocol https
		    :inherit t :depth treeless :ref
		    "8e3841c837b4b78bd72ad7f0436e919f39315a46" :id
		    htmlize :type git))
 (hydra :source "elpaca-menu-lock-file" :recipe
	(:package "hydra" :repo "abo-abo/hydra" :fetcher github :files
		  (:defaults (:exclude "lv.el")) :source
		  "elpaca-menu-lock-file" :protocol https :inherit t
		  :depth treeless :ref
		  "317e1de33086637579a7aeb60f77ed0405bf359b" :id hydra
		  :type git))
 (hydra-posframe :source "elpaca-menu-lock-file" :recipe
		 (:source "elpaca-menu-lock-file" :protocol https
			  :inherit t :depth treeless :host github
			  :repo "Ladicle/hydra-posframe" :package
			  "hydra-posframe" :ref
			  "142a04dd588af6c725e331863c3ca7bd5dda13ec"
			  :id hydra-posframe :type git))
 (inflections :source "elpaca-menu-lock-file" :recipe
	      (:package "inflections" :repo "eschulte/jump.el"
			:fetcher github :files ("inflections.el")
			:source "elpaca-menu-lock-file" :protocol
			https :inherit t :depth treeless :ref
			"55caa66a7cc6e0b1a76143fd40eff38416928941" :id
			inflections :type git))
 (inheritenv :source "elpaca-menu-lock-file" :recipe
	     (:package "inheritenv" :fetcher github :repo
		       "purcell/inheritenv" :files
		       ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			"*.texinfo" "doc/dir" "doc/*.info"
			"doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			"docs/dir" "docs/*.info" "docs/*.texi"
			"docs/*.texinfo"
			(:exclude ".dir-locals.el" "test.el"
				  "tests.el" "*-test.el" "*-tests.el"
				  "LICENSE" "README*" "*-pkg.el"))
		       :source "elpaca-menu-lock-file" :protocol https
		       :inherit t :depth treeless :ref
		       "b9e67cc20c069539698a9ac54d0e6cc11e616c6f" :id
		       inheritenv :type git))
 (jit-spell :source "elpaca-menu-lock-file" :recipe
	    (:package "jit-spell" :repo
		      ("https://github.com/astoff/jit-spell"
		       . "jit-spell")
		      :files ("*" (:exclude ".git")) :source
		      "elpaca-menu-lock-file" :protocol https :inherit
		      t :depth treeless :ref
		      "54f6f69401cbaf3b103e5a2ff4bec34498098ef6" :id
		      jit-spell :type git))
 (jj-mode :source "elpaca-menu-lock-file" :recipe
	  (:source "elpaca-menu-lock-file" :package "jj-mode" :id
		   jj-mode :host github :repo "bolivier/jj-mode.el"
		   :type git :protocol https :inherit t :depth
		   treeless :ref
		   "64a59eda9022080dcd7e86c128bb1bc3c92964df"))
 (just-ts-mode :source "elpaca-menu-lock-file" :recipe
	       (:package "just-ts-mode" :fetcher github :repo
			 "leon-barrett/just-ts-mode.el" :files
			 ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			  "*.texinfo" "doc/dir" "doc/*.info"
			  "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			  "docs/dir" "docs/*.info" "docs/*.texi"
			  "docs/*.texinfo"
			  (:exclude ".dir-locals.el" "test.el"
				    "tests.el" "*-test.el"
				    "*-tests.el" "LICENSE" "README*"
				    "*-pkg.el"))
			 :source "elpaca-menu-lock-file" :protocol
			 https :inherit t :depth treeless :ref
			 "f4fb841f93222fcefccabd641281553b4c8d9d6e"
			 :id just-ts-mode :type git))
 (lfe-mode :source "elpaca-menu-lock-file" :recipe
	   (:package "lfe-mode" :fetcher github :repo "rvirding/lfe"
		     :files
		     ("emacs/inferior-lfe.el" "emacs/lfe-mode.el"
		      "emacs/lfe-indent.el")
		     :source "elpaca-menu-lock-file" :protocol https
		     :inherit t :depth treeless :ref
		     "2fb4de84f81ac822923ddc60eaccc4a938c9dfdc" :id
		     lfe-mode :type git))
 (ligature :source "elpaca-menu-lock-file" :recipe
	   (:package "ligature" :fetcher github :repo
		     "mickeynp/ligature.el" :files
		     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		      "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		      "doc/*.texinfo" "lisp/*.el" "docs/dir"
		      "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		      (:exclude ".dir-locals.el" "test.el" "tests.el"
				"*-test.el" "*-tests.el" "LICENSE"
				"README*" "*-pkg.el"))
		     :source "elpaca-menu-lock-file" :protocol https
		     :inherit t :depth treeless :ref
		     "6ac1634612dbd42f7eb81ecaf022bd239aabb954" :id
		     ligature :type git))
 (linkode :source "elpaca-menu-lock-file" :recipe
	  (:package "linkode" :fetcher github :repo
		    "erickgnavar/linkode.el" :files
		    ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		     "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		     "doc/*.texinfo" "lisp/*.el" "docs/dir"
		     "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		     (:exclude ".dir-locals.el" "test.el" "tests.el"
			       "*-test.el" "*-tests.el" "LICENSE"
			       "README*" "*-pkg.el"))
		    :source "elpaca-menu-lock-file" :protocol https
		    :inherit t :depth treeless :ref
		    "8bfb80ca046bc0c942a14effef354722ad8a8a6c" :id
		    linkode :type git))
 (llama :source "elpaca-menu-lock-file" :recipe
	(:package "llama" :fetcher github :repo "tarsius/llama" :files
		  ("llama.el" ".dir-locals.el") :source
		  "elpaca-menu-lock-file" :protocol https :inherit t
		  :depth treeless :ref
		  "d430d48e0b5afd2a34b5531f103dcb110c3539c4" :id llama
		  :type git))
 (lv :source "elpaca-menu-lock-file" :recipe
     (:package "lv" :repo "abo-abo/hydra" :fetcher github :files
	       ("lv.el") :source "elpaca-menu-lock-file" :protocol
	       https :inherit t :depth treeless :ref
	       "317e1de33086637579a7aeb60f77ed0405bf359b" :id lv :type
	       git))
 (magit :source "elpaca-menu-lock-file" :recipe
	(:package "magit" :fetcher github :repo "magit/magit" :files
		  ("lisp/magit*.el" "lisp/git-*.el" "docs/magit.texi"
		   "docs/AUTHORS.md" "LICENSE" ".dir-locals.el"
		   (:exclude "lisp/magit-section.el"))
		  :source "elpaca-menu-lock-file" :protocol https
		  :inherit t :depth treeless :ref
		  "c800f79c2061621fde847f6a53129eca0e8da728" :id magit
		  :type git))
 (magit-delta :source "elpaca-menu-lock-file" :recipe
	      (:package "magit-delta" :fetcher github :repo
			"dandavison/magit-delta" :files
			("*.el" "*.el.in" "dir" "*.info" "*.texi"
			 "*.texinfo" "doc/dir" "doc/*.info"
			 "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			 "docs/dir" "docs/*.info" "docs/*.texi"
			 "docs/*.texinfo"
			 (:exclude ".dir-locals.el" "test.el"
				   "tests.el" "*-test.el" "*-tests.el"
				   "LICENSE" "README*" "*-pkg.el"))
			:source "elpaca-menu-lock-file" :protocol
			https :inherit t :depth treeless :ref
			"5fc7dbddcfacfe46d3fd876172ad02a9ab6ac616" :id
			magit-delta :type git))
 (magit-section :source "elpaca-menu-lock-file" :recipe
		(:package "magit-section" :fetcher github :repo
			  "magit/magit" :files
			  ("lisp/magit-section.el"
			   "docs/magit-section.texi"
			   "magit-section-pkg.el")
			  :source "elpaca-menu-lock-file" :protocol
			  https :inherit t :depth treeless :ref
			  "c800f79c2061621fde847f6a53129eca0e8da728"
			  :id magit-section :type git))
 (major-mode-hydra :source "elpaca-menu-lock-file" :recipe
		   (:package "major-mode-hydra" :repo
			     "jerrypnz/major-mode-hydra.el" :fetcher
			     github :files ("major-mode-hydra.el")
			     :source "elpaca-menu-lock-file" :protocol
			     https :inherit t :depth treeless :wait t
			     :ref
			     "d0a5dadee97c3752fcdef113cf2ba1923972a480"
			     :id major-mode-hydra :type git))
 (marginalia :source "elpaca-menu-lock-file" :recipe
	     (:package "marginalia" :repo "minad/marginalia" :fetcher
		       github :files
		       ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			"*.texinfo" "doc/dir" "doc/*.info"
			"doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			"docs/dir" "docs/*.info" "docs/*.texi"
			"docs/*.texinfo"
			(:exclude ".dir-locals.el" "test.el"
				  "tests.el" "*-test.el" "*-tests.el"
				  "LICENSE" "README*" "*-pkg.el"))
		       :source "elpaca-menu-lock-file" :protocol https
		       :inherit t :depth treeless :ref
		       "a527fb03b76a2bce1e360c6e73a095e06922c3f3" :id
		       marginalia :type git))
 (mermaid-mode :source "elpaca-menu-lock-file" :recipe
	       (:package "mermaid-mode" :repo "abrochard/mermaid-mode"
			 :fetcher github :files
			 ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			  "*.texinfo" "doc/dir" "doc/*.info"
			  "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			  "docs/dir" "docs/*.info" "docs/*.texi"
			  "docs/*.texinfo"
			  (:exclude ".dir-locals.el" "test.el"
				    "tests.el" "*-test.el"
				    "*-tests.el" "LICENSE" "README*"
				    "*-pkg.el"))
			 :source "elpaca-menu-lock-file" :protocol
			 https :inherit t :depth treeless :ref
			 "e74d4da7612c7a88e07f9dd3369e3b9fd36f396c"
			 :id mermaid-mode :type git))
 (mini-meme :source "elpaca-menu-lock-file" :recipe
	    (:source "elpaca-menu-lock-file" :package "mini-meme" :id
		     mini-meme :host github :repo
		     "erickgnavar/mini-meme.el" :type git :protocol
		     https :inherit t :depth treeless :ref
		     "079e82c4c85b0fd3dfafb69b6778d4e2a158acad"))
 (mise :source "elpaca-menu-lock-file" :recipe
       (:package "mise" :fetcher github :repo "eki3z/mise.el" :files
		 ("*.el" "*.el.in" "dir" "*.info" "*.texi" "*.texinfo"
		  "doc/dir" "doc/*.info" "doc/*.texi" "doc/*.texinfo"
		  "lisp/*.el" "docs/dir" "docs/*.info" "docs/*.texi"
		  "docs/*.texinfo"
		  (:exclude ".dir-locals.el" "test.el" "tests.el"
			    "*-test.el" "*-tests.el" "LICENSE"
			    "README*" "*-pkg.el"))
		 :source "elpaca-menu-lock-file" :protocol https
		 :inherit t :depth treeless :ref
		 "60ef63466d07417a9ef956af363baae39c9ddf34" :id mise
		 :type git))
 (multiple-cursors :source "elpaca-menu-lock-file" :recipe
		   (:package "multiple-cursors" :fetcher github :repo
			     "magnars/multiple-cursors.el" :files
			     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			      "*.texinfo" "doc/dir" "doc/*.info"
			      "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			      "docs/dir" "docs/*.info" "docs/*.texi"
			      "docs/*.texinfo"
			      (:exclude ".dir-locals.el" "test.el"
					"tests.el" "*-test.el"
					"*-tests.el" "LICENSE"
					"README*" "*-pkg.el"))
			     :source "elpaca-menu-lock-file" :protocol
			     https :inherit t :depth treeless :ref
			     "dd10cf2334333838e4550f091a75695448e26765"
			     :id multiple-cursors :type git))
 (nerd-icons :source "elpaca-menu-lock-file" :recipe
	     (:package "nerd-icons" :repo
		       "rainstormstudio/nerd-icons.el" :fetcher github
		       :files (:defaults "data") :source
		       "elpaca-menu-lock-file" :protocol https
		       :inherit t :depth treeless :ref
		       "f3e7ba37642455e5627968b1031faeefbcac1245" :id
		       nerd-icons :type git))
 (nerd-icons-completion :source "elpaca-menu-lock-file" :recipe
			(:package "nerd-icons-completion" :repo
				  "rainstormstudio/nerd-icons-completion"
				  :fetcher github :files
				  ("*.el" "*.el.in" "dir" "*.info"
				   "*.texi" "*.texinfo" "doc/dir"
				   "doc/*.info" "doc/*.texi"
				   "doc/*.texinfo" "lisp/*.el"
				   "docs/dir" "docs/*.info"
				   "docs/*.texi" "docs/*.texinfo"
				   (:exclude ".dir-locals.el"
					     "test.el" "tests.el"
					     "*-test.el" "*-tests.el"
					     "LICENSE" "README*"
					     "*-pkg.el"))
				  :source "elpaca-menu-lock-file"
				  :protocol https :inherit t :depth
				  treeless :ref
				  "8e5b995eb2439850ab21ba6062d9e6942c82ab9c"
				  :id nerd-icons-completion :type git))
 (nerd-icons-dired :source "elpaca-menu-lock-file" :recipe
		   (:package "nerd-icons-dired" :repo
			     "rainstormstudio/nerd-icons-dired"
			     :fetcher github :files
			     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			      "*.texinfo" "doc/dir" "doc/*.info"
			      "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			      "docs/dir" "docs/*.info" "docs/*.texi"
			      "docs/*.texinfo"
			      (:exclude ".dir-locals.el" "test.el"
					"tests.el" "*-test.el"
					"*-tests.el" "LICENSE"
					"README*" "*-pkg.el"))
			     :source "elpaca-menu-lock-file" :protocol
			     https :inherit t :depth treeless :ref
			     "c0b0cda2b92f831d0f764a7e8c0c6728d6a27774"
			     :id nerd-icons-dired :type git))
 (nix-ts-mode :source "elpaca-menu-lock-file" :recipe
	      (:package "nix-ts-mode" :fetcher github :repo
			"nix-community/nix-ts-mode" :files
			("*.el" "*.el.in" "dir" "*.info" "*.texi"
			 "*.texinfo" "doc/dir" "doc/*.info"
			 "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			 "docs/dir" "docs/*.info" "docs/*.texi"
			 "docs/*.texinfo"
			 (:exclude ".dir-locals.el" "test.el"
				   "tests.el" "*-test.el" "*-tests.el"
				   "LICENSE" "README*" "*-pkg.el"))
			:source "elpaca-menu-lock-file" :protocol
			https :inherit t :depth treeless :ref
			"706bbfaf7f344d7c5914bf7aa5beb2d904eb53b3" :id
			nix-ts-mode :type git))
 (ob-d2 :source "elpaca-menu-lock-file" :recipe
	(:package "ob-d2" :repo "xcapaldi/ob-d2" :fetcher github
		  :files
		  ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		   "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		   "doc/*.texinfo" "lisp/*.el" "docs/dir"
		   "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		   (:exclude ".dir-locals.el" "test.el" "tests.el"
			     "*-test.el" "*-tests.el" "LICENSE"
			     "README*" "*-pkg.el"))
		  :source "elpaca-menu-lock-file" :protocol https
		  :inherit t :depth treeless :ref
		  "5d197f8225a9fb4da997235b231abe30049c6825" :id ob-d2
		  :type git))
 (odin-ts-mode :source "elpaca-menu-lock-file" :recipe
	       (:source "elpaca-menu-lock-file" :protocol https
			:inherit t :depth treeless :host github :repo
			"Sampie159/odin-ts-mode" :package
			"odin-ts-mode" :ref
			"800134c4f104ab48b28ed33c8ebce1c8b8707add" :id
			odin-ts-mode :type git))
 (orderless :source "elpaca-menu-lock-file" :recipe
	    (:package "orderless" :repo "oantolin/orderless" :fetcher
		      github :files
		      ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		       "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		       "doc/*.texinfo" "lisp/*.el" "docs/dir"
		       "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		       (:exclude ".dir-locals.el" "test.el" "tests.el"
				 "*-test.el" "*-tests.el" "LICENSE"
				 "README*" "*-pkg.el"))
		      :source "elpaca-menu-lock-file" :protocol https
		      :inherit t :depth treeless :ref
		      "c7cb04499d94ee1c17affb29b1cfcd2a45116c97" :id
		      orderless :type git))
 (org-modern :source "elpaca-menu-lock-file" :recipe
	     (:package "org-modern" :repo "minad/org-modern" :fetcher
		       github :files
		       ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			"*.texinfo" "doc/dir" "doc/*.info"
			"doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			"docs/dir" "docs/*.info" "docs/*.texi"
			"docs/*.texinfo"
			(:exclude ".dir-locals.el" "test.el"
				  "tests.el" "*-test.el" "*-tests.el"
				  "LICENSE" "README*" "*-pkg.el"))
		       :source "elpaca-menu-lock-file" :protocol https
		       :inherit t :depth treeless :ref
		       "f514a2570da0f7a8ff0d72641458dbcf96ccf702" :id
		       org-modern :type git))
 (org-tree-slide :source "elpaca-menu-lock-file" :recipe
		 (:package "org-tree-slide" :fetcher github :repo
			   "takaxp/org-tree-slide" :files
			   ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			    "*.texinfo" "doc/dir" "doc/*.info"
			    "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			    "docs/dir" "docs/*.info" "docs/*.texi"
			    "docs/*.texinfo"
			    (:exclude ".dir-locals.el" "test.el"
				      "tests.el" "*-test.el"
				      "*-tests.el" "LICENSE" "README*"
				      "*-pkg.el"))
			   :source "elpaca-menu-lock-file" :protocol
			   https :inherit t :depth treeless :ref
			   "e2599a106a26ce5511095e23df4ea04be6687a8a"
			   :id org-tree-slide :type git))
 (ox-typst :source "elpaca-menu-lock-file" :recipe
	   (:package "ox-typst" :fetcher github :repo
		     "jmpunkt/ox-typst" :files
		     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		      "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		      "doc/*.texinfo" "lisp/*.el" "docs/dir"
		      "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		      (:exclude ".dir-locals.el" "test.el" "tests.el"
				"*-test.el" "*-tests.el" "LICENSE"
				"README*" "*-pkg.el"))
		     :source "elpaca-menu-lock-file" :protocol https
		     :inherit t :depth treeless :ref
		     "cc7e9665d4eabe0a8e0251408392f9578c7b1dac" :id
		     ox-typst :type git))
 (package-lint :source "elpaca-menu-lock-file" :recipe
	       (:package "package-lint" :fetcher github :repo
			 "purcell/package-lint" :files
			 (:defaults "data" (:exclude "*flymake.el"))
			 :source "elpaca-menu-lock-file" :protocol
			 https :inherit t :depth treeless :ref
			 "6f05a369e0718e93c5dce0951cad5e6646296612"
			 :id package-lint :type git))
 (paredit :source "elpaca-menu-lock-file" :recipe
	  (:package "paredit" :fetcher git :url
		    "https://mumble.net/~campbell/git/paredit.git"
		    :files
		    ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		     "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		     "doc/*.texinfo" "lisp/*.el" "docs/dir"
		     "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		     (:exclude ".dir-locals.el" "test.el" "tests.el"
			       "*-test.el" "*-tests.el" "LICENSE"
			       "README*" "*-pkg.el"))
		    :source "elpaca-menu-lock-file" :protocol https
		    :inherit t :depth treeless :ref
		    "af075775af91f2dbc63b915d762b4aec092946c4" :id
		    paredit :type git))
 (parrot :source "elpaca-menu-lock-file" :recipe
	 (:package "parrot" :fetcher github :repo "dp12/parrot" :files
		   (:defaults "img") :source "elpaca-menu-lock-file"
		   :protocol https :inherit t :depth treeless :ref
		   "1d381f24d74242018e306d1a0c891bed9a465ac3" :id
		   parrot :type git))
 (parseclj :source "elpaca-menu-lock-file" :recipe
	   (:package "parseclj" :repo "clojure-emacs/parseclj"
		     :fetcher github :files
		     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		      "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		      "doc/*.texinfo" "lisp/*.el" "docs/dir"
		      "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		      (:exclude ".dir-locals.el" "test.el" "tests.el"
				"*-test.el" "*-tests.el" "LICENSE"
				"README*" "*-pkg.el"))
		     :source "elpaca-menu-lock-file" :protocol https
		     :inherit t :depth treeless :ref
		     "6af22372e0fe14df882dd300b22b12ba2d7e00b0" :id
		     parseclj :type git))
 (parseedn :source "elpaca-menu-lock-file" :recipe
	   (:package "parseedn" :repo "clojure-emacs/parseedn"
		     :fetcher github :files
		     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		      "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		      "doc/*.texinfo" "lisp/*.el" "docs/dir"
		      "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		      (:exclude ".dir-locals.el" "test.el" "tests.el"
				"*-test.el" "*-tests.el" "LICENSE"
				"README*" "*-pkg.el"))
		     :source "elpaca-menu-lock-file" :protocol https
		     :inherit t :depth treeless :ref
		     "3407e4530a367b6c2b857dae261cdbb67a440aaa" :id
		     parseedn :type git))
 (perspective :source "elpaca-menu-lock-file" :recipe
	      (:package "perspective" :fetcher github :repo
			"nex3/perspective-el" :files
			("*.el" "*.el.in" "dir" "*.info" "*.texi"
			 "*.texinfo" "doc/dir" "doc/*.info"
			 "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			 "docs/dir" "docs/*.info" "docs/*.texi"
			 "docs/*.texinfo"
			 (:exclude ".dir-locals.el" "test.el"
				   "tests.el" "*-test.el" "*-tests.el"
				   "LICENSE" "README*" "*-pkg.el"))
			:source "elpaca-menu-lock-file" :protocol
			https :inherit t :depth treeless :ref
			"e32d3ea731f6bc551ce196527b3cb0dc19d71151" :id
			perspective :type git))
 (popper :source "elpaca-menu-lock-file" :recipe
	 (:package "popper" :fetcher github :repo "karthink/popper"
		   :files
		   ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		    "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		    "doc/*.texinfo" "lisp/*.el" "docs/dir"
		    "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		    (:exclude ".dir-locals.el" "test.el" "tests.el"
			      "*-test.el" "*-tests.el" "LICENSE"
			      "README*" "*-pkg.el"))
		   :source "elpaca-menu-lock-file" :protocol https
		   :inherit t :depth treeless :ref
		   "faf155059e519fb036324af579c342365795dbbb" :id
		   popper :type git))
 (popup :source "elpaca-menu-lock-file" :recipe
	(:package "popup" :fetcher github :repo
		  "auto-complete/popup-el" :files
		  ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		   "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		   "doc/*.texinfo" "lisp/*.el" "docs/dir"
		   "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		   (:exclude ".dir-locals.el" "test.el" "tests.el"
			     "*-test.el" "*-tests.el" "LICENSE"
			     "README*" "*-pkg.el"))
		  :source "elpaca-menu-lock-file" :protocol https
		  :inherit t :depth treeless :ref
		  "7a05700a37aae66d2b24f0cd8851f65383a5cf96" :id popup
		  :type git))
 (posframe :source "elpaca-menu-lock-file" :recipe
	   (:package "posframe" :fetcher github :repo
		     "tumashu/posframe" :files
		     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		      "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		      "doc/*.texinfo" "lisp/*.el" "docs/dir"
		      "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		      (:exclude ".dir-locals.el" "test.el" "tests.el"
				"*-test.el" "*-tests.el" "LICENSE"
				"README*" "*-pkg.el"))
		     :source "elpaca-menu-lock-file" :protocol https
		     :inherit t :depth treeless :ref
		     "fcf1757baee481f617fbf2dc39f8c561207df263" :id
		     posframe :type git))
 (pretty-hydra :source "elpaca-menu-lock-file" :recipe
	       (:package "pretty-hydra" :repo
			 "jerrypnz/major-mode-hydra.el" :fetcher
			 github :files ("pretty-hydra.el") :source
			 "elpaca-menu-lock-file" :protocol https
			 :inherit t :depth treeless :ref
			 "d0a5dadee97c3752fcdef113cf2ba1923972a480"
			 :id pretty-hydra :type git))
 (pythontest :source "elpaca-menu-lock-file" :recipe
	     (:package "pythontest" :fetcher github :repo
		       "erickgnavar/pythontest.el" :files
		       ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			"*.texinfo" "doc/dir" "doc/*.info"
			"doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			"docs/dir" "docs/*.info" "docs/*.texi"
			"docs/*.texinfo"
			(:exclude ".dir-locals.el" "test.el"
				  "tests.el" "*-test.el" "*-tests.el"
				  "LICENSE" "README*" "*-pkg.el"))
		       :source "elpaca-menu-lock-file" :protocol https
		       :inherit t :depth treeless :ref
		       "4bb4f330c13ef82bb6e4a4b15c47cb3fede83523" :id
		       pythontest :type git))
 (queue :source "elpaca-menu-lock-file" :recipe
	(:package "queue" :repo
		  ("https://git.savannah.gnu.org/git/emacs/elpa.git"
		   . "queue")
		  :branch "externals/queue" :files
		  ("*" (:exclude ".git")) :source
		  "elpaca-menu-lock-file" :protocol https :inherit t
		  :depth treeless :ref
		  "f986fb68e75bdae951efb9e11a3012ab6bd408ee" :id queue
		  :type git))
 (rainbow-delimiters :source "elpaca-menu-lock-file" :recipe
		     (:package "rainbow-delimiters" :fetcher github
			       :repo "Fanael/rainbow-delimiters"
			       :files
			       ("*.el" "*.el.in" "dir" "*.info"
				"*.texi" "*.texinfo" "doc/dir"
				"doc/*.info" "doc/*.texi"
				"doc/*.texinfo" "lisp/*.el" "docs/dir"
				"docs/*.info" "docs/*.texi"
				"docs/*.texinfo"
				(:exclude ".dir-locals.el" "test.el"
					  "tests.el" "*-test.el"
					  "*-tests.el" "LICENSE"
					  "README*" "*-pkg.el"))
			       :source "elpaca-menu-lock-file"
			       :protocol https :inherit t :depth
			       treeless :ref
			       "f40ece58df8b2f0fb6c8576b527755a552a5e763"
			       :id rainbow-delimiters :type git))
 (rainbow-mode :source "elpaca-menu-lock-file" :recipe
	       (:package "rainbow-mode" :repo
			 ("https://git.savannah.gnu.org/git/emacs/elpa.git"
			  . "rainbow-mode")
			 :branch "externals/rainbow-mode" :files
			 ("*" (:exclude ".git")) :source
			 "elpaca-menu-lock-file" :protocol https
			 :inherit t :depth treeless :ref
			 "f7db3b5919f70420a91eb199f8663468de3033f3"
			 :id rainbow-mode :type git))
 (reformatter :source "elpaca-menu-lock-file" :recipe
	      (:package "reformatter" :repo
			"purcell/emacs-reformatter" :fetcher github
			:files
			("*.el" "*.el.in" "dir" "*.info" "*.texi"
			 "*.texinfo" "doc/dir" "doc/*.info"
			 "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			 "docs/dir" "docs/*.info" "docs/*.texi"
			 "docs/*.texinfo"
			 (:exclude ".dir-locals.el" "test.el"
				   "tests.el" "*-test.el" "*-tests.el"
				   "LICENSE" "README*" "*-pkg.el"))
			:source "elpaca-menu-lock-file" :protocol
			https :inherit t :depth treeless :ref
			"f2cb59466b1c3f85a8c960f7d4b7b7ead015bedc" :id
			reformatter :type git))
 (request :source "elpaca-menu-lock-file"
   :recipe
   (:package "request" :repo "tkf/emacs-request" :fetcher github
	     :files ("request.el") :source "elpaca-menu-lock-file"
	     :protocol https :inherit t :depth treeless :ref
	     "01e338c335c07e4407239619e57361944a82cb8a" :id request
	     :type git))
 (restclient :source "elpaca-menu-lock-file" :recipe
	     (:package "restclient" :repo "emacsorphanage/restclient"
		       :fetcher github :files ("restclient.el")
		       :source "elpaca-menu-lock-file" :protocol https
		       :inherit t :depth treeless :ref
		       "757d82eacb997f82c84c4e6eea08a338b0b6afa8" :id
		       restclient :type git))
 (s :source "elpaca-menu-lock-file" :recipe
    (:package "s" :fetcher github :repo "magnars/s.el" :files
	      ("*.el" "*.el.in" "dir" "*.info" "*.texi" "*.texinfo"
	       "doc/dir" "doc/*.info" "doc/*.texi" "doc/*.texinfo"
	       "lisp/*.el" "docs/dir" "docs/*.info" "docs/*.texi"
	       "docs/*.texinfo"
	       (:exclude ".dir-locals.el" "test.el" "tests.el"
			 "*-test.el" "*-tests.el" "LICENSE" "README*"
			 "*-pkg.el"))
	      :source "elpaca-menu-lock-file" :protocol https :inherit
	      t :depth treeless :ref
	      "dda84d38fffdaf0c9b12837b504b402af910d01d" :id s :type
	      git))
 (sesman :source "elpaca-menu-lock-file" :recipe
	 (:package "sesman" :repo "vspinu/sesman" :fetcher github
		   :files
		   ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		    "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		    "doc/*.texinfo" "lisp/*.el" "docs/dir"
		    "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		    (:exclude ".dir-locals.el" "test.el" "tests.el"
			      "*-test.el" "*-tests.el" "LICENSE"
			      "README*" "*-pkg.el"))
		   :source "elpaca-menu-lock-file" :protocol https
		   :inherit t :depth treeless :ref
		   "7bca68dbbab0af26a6a23be1ff5fa97f9a18e022" :id
		   sesman :type git))
 (shrink-path :source "elpaca-menu-lock-file" :recipe
	      (:package "shrink-path" :fetcher gitlab :repo
			"bennya/shrink-path.el" :files
			("*.el" "*.el.in" "dir" "*.info" "*.texi"
			 "*.texinfo" "doc/dir" "doc/*.info"
			 "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			 "docs/dir" "docs/*.info" "docs/*.texi"
			 "docs/*.texinfo"
			 (:exclude ".dir-locals.el" "test.el"
				   "tests.el" "*-test.el" "*-tests.el"
				   "LICENSE" "README*" "*-pkg.el"))
			:source "elpaca-menu-lock-file" :protocol
			https :inherit t :depth treeless :ref
			"c14882c8599aec79a6e8ef2d06454254bb3e1e41" :id
			shrink-path :type git))
 (slint-mode :source "elpaca-menu-lock-file" :recipe
	     (:package "slint-mode" :fetcher github :repo
		       "nilclass/slint-mode" :files
		       ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			"*.texinfo" "doc/dir" "doc/*.info"
			"doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			"docs/dir" "docs/*.info" "docs/*.texi"
			"docs/*.texinfo"
			(:exclude ".dir-locals.el" "test.el"
				  "tests.el" "*-test.el" "*-tests.el"
				  "LICENSE" "README*" "*-pkg.el"))
		       :source "elpaca-menu-lock-file" :protocol https
		       :inherit t :depth treeless :ref
		       "168a6cfb90b5e36360074c83f80d5bbac2f0287e" :id
		       slint-mode :type git))
 (sly :source "elpaca-menu-lock-file" :recipe
      (:package "sly" :repo "joaotavora/sly" :fetcher github :files
		(:defaults "lib" "slynk" "contrib" "doc/images"
			   (:exclude "sly-autoloads.el"))
		:version-regexp "%v" :source "elpaca-menu-lock-file"
		:protocol https :inherit t :depth treeless :ref
		"c48defcf58596e035d473f3a125fdd1485593146" :id sly
		:type git))
 (spinner :source "elpaca-menu-lock-file" :recipe
	  (:package "spinner" :repo
		    ("https://github.com/Malabarba/spinner.el"
		     . "spinner")
		    :files ("*" (:exclude ".git")) :source
		    "elpaca-menu-lock-file" :protocol https :inherit t
		    :depth treeless :ref
		    "d4647ae87fb0cd24bc9081a3d287c860ff061c21" :id
		    spinner :type git))
 (svelte-ts-mode :source "elpaca-menu-lock-file" :recipe
		 (:source "elpaca-menu-lock-file" :package
			  "svelte-ts-mode" :id svelte-ts-mode :host
			  github :repo "leafOfTree/svelte-ts-mode"
			  :type git :protocol https :inherit t :depth
			  treeless :ref
			  "d079050fc1ba70f8fba9e596638daa2ca96e0fdd"))
 (swift-mode :source "elpaca-menu-lock-file" :recipe
	     (:package "swift-mode" :repo "swift-emacs/swift-mode"
		       :fetcher github :files
		       ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			"*.texinfo" "doc/dir" "doc/*.info"
			"doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			"docs/dir" "docs/*.info" "docs/*.texi"
			"docs/*.texinfo"
			(:exclude ".dir-locals.el" "test.el"
				  "tests.el" "*-test.el" "*-tests.el"
				  "LICENSE" "README*" "*-pkg.el"))
		       :source "elpaca-menu-lock-file" :protocol https
		       :inherit t :depth treeless :ref
		       "bf56866b98de2fd73029da2bca1035ca02f9e879" :id
		       swift-mode :type git))
 (telega :source "elpaca-menu-lock-file" :recipe
	 (:package "telega" :fetcher github :repo "zevlg/telega.el"
		   :files
		   (:defaults "etc" "server" "contrib" "Makefile")
		   :source "elpaca-menu-lock-file" :protocol https
		   :inherit t :depth treeless :ref
		   "85ad1717de4a47ebeb293e836e5a89e901b55dbd" :id
		   telega :type git))
 (terraform-mode :source "elpaca-menu-lock-file" :recipe
		 (:package "terraform-mode" :repo
			   "hcl-emacs/terraform-mode" :fetcher github
			   :files
			   ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			    "*.texinfo" "doc/dir" "doc/*.info"
			    "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			    "docs/dir" "docs/*.info" "docs/*.texi"
			    "docs/*.texinfo"
			    (:exclude ".dir-locals.el" "test.el"
				      "tests.el" "*-test.el"
				      "*-tests.el" "LICENSE" "README*"
				      "*-pkg.el"))
			   :source "elpaca-menu-lock-file" :protocol
			   https :inherit t :depth treeless :ref
			   "5bdd734a87f67f6574664f63eb4d02a0bc74c0d0"
			   :id terraform-mode :type git))
 (treesit-auto :source "elpaca-menu-lock-file" :recipe
	       (:package "treesit-auto" :fetcher github :repo
			 "renzmann/treesit-auto" :files
			 ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			  "*.texinfo" "doc/dir" "doc/*.info"
			  "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			  "docs/dir" "docs/*.info" "docs/*.texi"
			  "docs/*.texinfo"
			  (:exclude ".dir-locals.el" "test.el"
				    "tests.el" "*-test.el"
				    "*-tests.el" "LICENSE" "README*"
				    "*-pkg.el"))
			 :source "elpaca-menu-lock-file" :protocol
			 https :inherit t :depth treeless :ref
			 "433dac791c3a5c6f2aebddc70066f72940a12489"
			 :id treesit-auto :type git))
 (treesit-ispell :source "elpaca-menu-lock-file" :recipe
		 (:package "treesit-ispell" :fetcher github :repo
			   "erickgnavar/treesit-ispell.el" :files
			   ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			    "*.texinfo" "doc/dir" "doc/*.info"
			    "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			    "docs/dir" "docs/*.info" "docs/*.texi"
			    "docs/*.texinfo"
			    (:exclude ".dir-locals.el" "test.el"
				      "tests.el" "*-test.el"
				      "*-tests.el" "LICENSE" "README*"
				      "*-pkg.el"))
			   :source "elpaca-menu-lock-file" :protocol
			   https :inherit t :depth treeless :ref
			   "fd1598fb16fe99dc8d8245974641c3e474dc31d1"
			   :id treesit-ispell :type git))
 (typst-ts-mode :source "elpaca-menu-lock-file" :recipe
		(:package "typst-ts-mode" :repo
			  "meow_king/typst-ts-mode" :files
			  ("*" (:exclude ".git")) :source
			  "elpaca-menu-lock-file" :protocol https
			  :inherit t :depth treeless :type git :host
			  codeberg :ref
			  "972dc69d6b8a3f8983f6b8000654f59c8a8d05ba"
			  :id typst-ts-mode))
 (vertico :source "elpaca-menu-lock-file" :recipe
	  (:package "vertico" :repo "minad/vertico" :files
		    (:defaults "extensions/vertico-*.el") :fetcher
		    github :source "elpaca-menu-lock-file" :protocol
		    https :inherit t :depth treeless :ref
		    "17c629087ea23466b10fd0dd4ecce53e17a810e3" :id
		    vertico :type git))
 (vertico-posframe :source "elpaca-menu-lock-file" :recipe
		   (:package "vertico-posframe" :repo
			     ("https://github.com/tumashu/vertico-posframe"
			      . "vertico-posframe")
			     :files ("*" (:exclude ".git")) :source
			     "elpaca-menu-lock-file" :protocol https
			     :inherit t :depth treeless :ref
			     "d89a70743cfd95b7fcda621679b7555bbef51acb"
			     :id vertico-posframe :type git))
 (visual-fill-column :source "elpaca-menu-lock-file" :recipe
		     (:package "visual-fill-column" :fetcher codeberg
			       :repo "joostkremers/visual-fill-column"
			       :files
			       ("*.el" "*.el.in" "dir" "*.info"
				"*.texi" "*.texinfo" "doc/dir"
				"doc/*.info" "doc/*.texi"
				"doc/*.texinfo" "lisp/*.el" "docs/dir"
				"docs/*.info" "docs/*.texi"
				"docs/*.texinfo"
				(:exclude ".dir-locals.el" "test.el"
					  "tests.el" "*-test.el"
					  "*-tests.el" "LICENSE"
					  "README*" "*-pkg.el"))
			       :source "elpaca-menu-lock-file"
			       :protocol https :inherit t :depth
			       treeless :ref
			       "11575df4e042f3263db62de0e8ee4f2331fbb1b4"
			       :id visual-fill-column :type git))
 (vterm :source "elpaca-menu-lock-file" :recipe
	(:package "vterm" :fetcher github :repo
		  "akermu/emacs-libvterm" :files
		  ("CMakeLists.txt" "elisp.c" "elisp.h"
		   "emacs-module.h" "etc" "utf8.c" "utf8.h" "vterm.el"
		   "vterm-module.c" "vterm-module.h")
		  :source "elpaca-menu-lock-file" :protocol https
		  :inherit t :depth treeless :ref
		  "056ad74653704bc353d8ec8ab52ac75267b7d373" :id vterm
		  :type git))
 (vterm-toggle :source "elpaca-menu-lock-file" :recipe
	       (:package "vterm-toggle" :fetcher github :repo
			 "jixiuf/vterm-toggle" :files
			 ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			  "*.texinfo" "doc/dir" "doc/*.info"
			  "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			  "docs/dir" "docs/*.info" "docs/*.texi"
			  "docs/*.texinfo"
			  (:exclude ".dir-locals.el" "test.el"
				    "tests.el" "*-test.el"
				    "*-tests.el" "LICENSE" "README*"
				    "*-pkg.el"))
			 :source "elpaca-menu-lock-file" :protocol
			 https :inherit t :depth treeless :ref
			 "a0051a8b8eaa85f8df54ddb032f5710c1f62779d"
			 :id vterm-toggle :type git))
 (wakatime-mode :source "elpaca-menu-lock-file" :recipe
		(:package "wakatime-mode" :fetcher github :repo
			  "wakatime/wakatime-mode" :files
			  ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			   "*.texinfo" "doc/dir" "doc/*.info"
			   "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			   "docs/dir" "docs/*.info" "docs/*.texi"
			   "docs/*.texinfo"
			   (:exclude ".dir-locals.el" "test.el"
				     "tests.el" "*-test.el"
				     "*-tests.el" "LICENSE" "README*"
				     "*-pkg.el"))
			  :source "elpaca-menu-lock-file" :protocol
			  https :inherit t :depth treeless :ref
			  "1c5b2254dd72f2ff504d6a6189a8c10be03a98d1"
			  :id wakatime-mode :type git))
 (web-mode :source "elpaca-menu-lock-file" :recipe
	   (:package "web-mode" :repo "fxbois/web-mode" :fetcher
		     github :files
		     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		      "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		      "doc/*.texinfo" "lisp/*.el" "docs/dir"
		      "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		      (:exclude ".dir-locals.el" "test.el" "tests.el"
				"*-test.el" "*-tests.el" "LICENSE"
				"README*" "*-pkg.el"))
		     :source "elpaca-menu-lock-file" :protocol https
		     :inherit t :depth treeless :ref
		     "be2d59c8fa02b1a45ae54ce4079e502e659cefe6" :id
		     web-mode :type git))
 (websocket :source "elpaca-menu-lock-file" :recipe
	    (:package "websocket" :repo "ahyatt/emacs-websocket"
		      :fetcher github :files
		      ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		       "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		       "doc/*.texinfo" "lisp/*.el" "docs/dir"
		       "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		       (:exclude ".dir-locals.el" "test.el" "tests.el"
				 "*-test.el" "*-tests.el" "LICENSE"
				 "README*" "*-pkg.el"))
		      :source "elpaca-menu-lock-file" :protocol https
		      :inherit t :depth treeless :ref
		      "40c208eaab99999d7c1e4bea883648da24c03be3" :id
		      websocket :type git))
 (wgrep :source "elpaca-menu-lock-file" :recipe
	(:package "wgrep" :fetcher github :repo
		  "mhayashi1120/Emacs-wgrep" :files ("wgrep.el")
		  :source "elpaca-menu-lock-file" :protocol https
		  :inherit t :depth treeless :ref
		  "49f09ab9b706d2312cab1199e1eeb1bcd3f27f6f" :id wgrep
		  :type git))
 (with-editor :source "elpaca-menu-lock-file"
   :recipe
   (:package "with-editor" :fetcher github :repo "magit/with-editor"
	     :files
	     ("*.el" "*.el.in" "dir" "*.info" "*.texi" "*.texinfo"
	      "doc/dir" "doc/*.info" "doc/*.texi" "doc/*.texinfo"
	      "lisp/*.el" "docs/dir" "docs/*.info" "docs/*.texi"
	      "docs/*.texinfo"
	      (:exclude ".dir-locals.el" "test.el" "tests.el"
			"*-test.el" "*-tests.el" "LICENSE" "README*"
			"*-pkg.el"))
	     :source "elpaca-menu-lock-file" :protocol https :inherit
	     t :depth treeless :ref
	     "64211dcb815f2533ac3d2a7e56ff36ae804d8338" :id
	     with-editor :type git))
 (writeroom-mode :source "elpaca-menu-lock-file" :recipe
		 (:package "writeroom-mode" :fetcher github :repo
			   "joostkremers/writeroom-mode" :files
			   ("*.el" "*.el.in" "dir" "*.info" "*.texi"
			    "*.texinfo" "doc/dir" "doc/*.info"
			    "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			    "docs/dir" "docs/*.info" "docs/*.texi"
			    "docs/*.texinfo"
			    (:exclude ".dir-locals.el" "test.el"
				      "tests.el" "*-test.el"
				      "*-tests.el" "LICENSE" "README*"
				      "*-pkg.el"))
			   :source "elpaca-menu-lock-file" :protocol
			   https :inherit t :depth treeless :ref
			   "86965a56ed712ab70eb76577dc6fc5b01fd0eda6"
			   :id writeroom-mode :type git))
 (xterm-color :source "elpaca-menu-lock-file" :recipe
	      (:package "xterm-color" :repo "atomontage/xterm-color"
			:fetcher github :files
			("*.el" "*.el.in" "dir" "*.info" "*.texi"
			 "*.texinfo" "doc/dir" "doc/*.info"
			 "doc/*.texi" "doc/*.texinfo" "lisp/*.el"
			 "docs/dir" "docs/*.info" "docs/*.texi"
			 "docs/*.texinfo"
			 (:exclude ".dir-locals.el" "test.el"
				   "tests.el" "*-test.el" "*-tests.el"
				   "LICENSE" "README*" "*-pkg.el"))
			:source "elpaca-menu-lock-file" :protocol
			https :inherit t :depth treeless :ref
			"7543ed80d0ad07f1044b4d55c9ee7893cd45362c" :id
			xterm-color :type git))
 (yaml :source "elpaca-menu-lock-file" :recipe
       (:package "yaml" :repo "zkry/yaml.el" :fetcher github :files
		 ("*.el" "*.el.in" "dir" "*.info" "*.texi" "*.texinfo"
		  "doc/dir" "doc/*.info" "doc/*.texi" "doc/*.texinfo"
		  "lisp/*.el" "docs/dir" "docs/*.info" "docs/*.texi"
		  "docs/*.texinfo"
		  (:exclude ".dir-locals.el" "test.el" "tests.el"
			    "*-test.el" "*-tests.el" "LICENSE"
			    "README*" "*-pkg.el"))
		 :source "elpaca-menu-lock-file" :protocol https
		 :inherit t :depth treeless :ref
		 "73a9f5356284716a123952ce194c14ad9d183444" :id yaml
		 :type git))
 (yaml-pro :source "elpaca-menu-lock-file" :recipe
	   (:package "yaml-pro" :repo "zkry/yaml-pro" :fetcher github
		     :files
		     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		      "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		      "doc/*.texinfo" "lisp/*.el" "docs/dir"
		      "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		      (:exclude ".dir-locals.el" "test.el" "tests.el"
				"*-test.el" "*-tests.el" "LICENSE"
				"README*" "*-pkg.el"))
		     :source "elpaca-menu-lock-file" :protocol https
		     :inherit t :depth treeless :ref
		     "f7706ea170de98d29b7cdd5a4f189bd038b1e27b" :id
		     yaml-pro :type git))
 (yasnippet :source "elpaca-menu-lock-file" :recipe
	    (:package "yasnippet" :repo "joaotavora/yasnippet"
		      :fetcher github :files
		      ("yasnippet.el" "snippets") :source
		      "elpaca-menu-lock-file" :protocol https :inherit
		      t :depth treeless :ref
		      "5b315f1753480ebe669b157b8242448b5eafcfea" :id
		      yasnippet :type git))
 (yasnippet-snippets :source "elpaca-menu-lock-file" :recipe
		     (:package "yasnippet-snippets" :repo
			       "AndreaCrotti/yasnippet-snippets"
			       :fetcher github :files
			       ("*.el" "snippets" ".nosearch") :source
			       "elpaca-menu-lock-file" :protocol https
			       :inherit t :depth treeless :ref
			       "f1907ed38acc479e78d5c113810465e4d77d8604"
			       :id yasnippet-snippets :type git))
 (zig-mode :source "elpaca-menu-lock-file" :recipe
	   (:package "zig-mode" :repo "ziglang/zig-mode" :fetcher
		     github :files
		     ("*.el" "*.el.in" "dir" "*.info" "*.texi"
		      "*.texinfo" "doc/dir" "doc/*.info" "doc/*.texi"
		      "doc/*.texinfo" "lisp/*.el" "docs/dir"
		      "docs/*.info" "docs/*.texi" "docs/*.texinfo"
		      (:exclude ".dir-locals.el" "test.el" "tests.el"
				"*-test.el" "*-tests.el" "LICENSE"
				"README*" "*-pkg.el"))
		     :source "elpaca-menu-lock-file" :protocol https
		     :inherit t :depth treeless :ref
		     "f0b4a487530146f99230f4a5ff67e8d56c8f3f80" :id
		     zig-mode :type git)))

init.el

.emacs.d/init.el · 80 lines

;;; Package --- init file -*- lexical-binding: t -*-
;;; init.el --- bootstrap file to load config from bootstrap.org
;;; Commentary:
;;; Code:

(defvar elpaca-lock-file (expand-file-name "elpaca-lockfile.eld" user-emacs-directory))

(defun my/elpaca-write-lock-file ()
  "Write lock file into a fixed path using `elpaca-lock-file'."
  (interactive)
  (minibuffer-with-setup-hook #'(lambda ()
                                  (insert elpaca-lock-file))
    (call-interactively 'elpaca-write-lock-file)))

;; Avoid to show a message about deprecation of cl package
(setq byte-compile-warnings '(cl-functions))

(defvar elpaca-installer-version 0.12)
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
(defvar elpaca-sources-directory (expand-file-name "sources/" elpaca-directory))
(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
                              :ref nil :depth nil :inherit ignore
                              :files (:defaults "elpaca-test.el" (:exclude "extensions"))
                              :build (:not elpaca-activate)))
(let* ((repo  (expand-file-name "elpaca/" elpaca-sources-directory))
       (build (expand-file-name "elpaca/" elpaca-builds-directory))
       (order (cdr elpaca-order))
       (default-directory repo))
  (add-to-list 'load-path (if (file-exists-p build) build repo))
  (unless (file-exists-p repo)
    (make-directory repo t)
    (when (<= emacs-major-version 28) (require 'subr-x))
    (condition-case-unless-debug err
        (if-let* ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
                  ((zerop (apply #'call-process `("git" nil ,buffer t "clone"
                                                  ,@(when-let* ((depth (plist-get order :depth)))
                                                      (list (format "--depth=%d" depth) "--no-single-branch"))
                                                  ,(plist-get order :repo) ,repo))))
                  ((zerop (call-process "git" nil buffer t "checkout"
                                        (or (plist-get order :ref) "--"))))
                  (emacs (concat invocation-directory invocation-name))
                  ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
                                        "--eval" "(byte-recompile-directory \".\" 0 'force)")))
                  ((require 'elpaca))
                  ((elpaca-generate-autoloads "elpaca" repo)))
            (progn (message "%s" (buffer-string)) (kill-buffer buffer))
          (error "%s" (with-current-buffer buffer (buffer-string))))
      ((error) (warn "%s" err) (delete-directory repo 'recursive))))
  (unless (require 'elpaca-autoloads nil t)
    (require 'elpaca)
    (elpaca-generate-autoloads "elpaca" repo)
    (let ((load-source-file-function nil)) (load "./elpaca-autoloads"))))

(add-hook 'after-init-hook #'elpaca-process-queues)

(elpaca `(,@elpaca-order))

;; Install use-package support
(elpaca elpaca-use-package
  ;; Enable use-package :ensure support for Elpaca.
  (elpaca-use-package-mode))

;; setup zsh as a default shell when is available
(let ((zsh-path (executable-find "zsh")))
  (when zsh-path
    (setq shell-file-name zsh-path)))

;; Always follow symlinks, used to avoid emacs ask when open org conf file
(setq vc-follow-symlinks t)

;; read bootstrap.org and load emacs-lisp code
(org-babel-load-file (expand-file-name "bootstrap.org" user-emacs-directory))

;; custom code that will be unique per machine
(defconst local-config-file (expand-file-name "local.el" user-emacs-directory))
(load local-config-file :noerror)

(provide 'init.el)
;;; init.el ends here

README.org

.emacs.d/README.org · 3,291 lines

#+TITLE: Emacs config
#+AUTHOR: Erick Navarro
#+OPTIONS: num:nil

* Vanilla Emacs setup

This is required to enable lexical binding in the resulting ~.el~ file, which will be used by emacs at startup.

#+begin_src emacs-lisp
;; -*- lexical-binding: t -*-
#+end_src

#+BEGIN_SRC emacs-lisp
(setq inhibit-startup-message t)
(setq default-directory (expand-file-name "~/"))

;; Default font-family, this will used in all the function that manipulates font settings
(defvar my/font-family "JetBrainsMono Nerd Font")

;; Change backups directory to emacs folder
;; this avoid to save backup files in the same directory of the original files
(setq backup-directory-alist `(("." . ,(concat user-emacs-directory
                                               "backups"))))

;; Revert dired and other buffers when there are changes on disk
(setq global-auto-revert-non-file-buffers t)

;; Hide the bell in the center of screen
(setq ring-bell-function 'ignore)
(column-number-mode t)
(global-hl-line-mode 1)

;; in case the font doesn't exit this will raise an error saying that
(set-face-attribute 'default nil :font (format "%s 13" my/font-family))

(global-set-key (kbd "C-x -") 'my/decrease-font-height)
(global-set-key (kbd "C-x =") 'my/increase-font-height)
(global-set-key (kbd "C-x +") 'my/increase-font-height)

;; Navigate through buffers
(global-set-key (kbd "M-[") 'previous-buffer)
(global-set-key (kbd "M-]") 'next-buffer)

;; Fix unicode errors
(setenv "LANG" "en_US.UTF-8")
(setenv "LC_ALL" "en_US.UTF-8")
(setenv "LC_CTYPE" "en_US.UTF-8")

;; Fix size of scroll
(setq scroll-step 1
      scroll-conservatively  10000)

;; Show a marker when the line has empty characters at the end
(setq-default show-trailing-whitespace t)

;; disable `show-trailing-whitespace' for modes based in comint-mode
;; make prompt readonly
(add-hook 'comint-mode-hook #'(lambda ()
                                (setq-local show-trailing-whitespace nil)
                                (setq-local comint-prompt-read-only t)))

;; Avoid close emacs by mistake
(global-unset-key (kbd "C-x C-c"))

(defalias 'yes-or-no-p 'y-or-n-p)
(defalias 'run-elisp 'ielm)

;; place custom code generated for emacs in a separate file
(defconst custom-file (expand-file-name ".customize.el" user-emacs-directory))
(load custom-file :noerror)

;; disable mouse gestures to avoid scaling text by mistake
(global-set-key (kbd "<pinch>") 'ignore)
(global-set-key (kbd "<C-wheel-up>") 'ignore)
(global-set-key (kbd "<C-wheel-down>") 'ignore)
#+END_SRC

Show a message with emacs startup time

#+begin_src emacs-lisp
(defun my/display-startup-time ()
  (message "🚀 Emacs loaded in %s with %d garbage collections."
           (float-time (time-subtract after-init-time before-init-time))
           gcs-done))

(add-hook 'emacs-startup-hook #'my/display-startup-time)
#+end_src

Setup ligatures, by default it has support for ~JetBrainsMono~.

#+BEGIN_SRC emacs-lisp
(use-package ligature
  :ensure t
  :config
  ;; Enable ligatures in programming modes
  (ligature-set-ligatures 'prog-mode '("www" "**" "***" "**/" "*>" "*/" "\\\\" "\\\\\\" "{-" "::"
                                       ":::" ":=" "!!" "!=" "!==" "-}" "----" "-->" "->" "->>"
                                       "-<" "-<<" "-~" "#{" "#[" "##" "###" "####" "#(" "#?" "#_"
                                       "#_(" ".-" ".=" ".." "..<" "..." "?=" "??" ";;" "/*" "/**"
                                       "/=" "/==" "/>" "//" "///" "&&" "||" "||=" "|=" "|>" "^=" "$>"
                                       "++" "+++" "+>" "=:=" "==" "===" "==>" "=>" "=>>" "<="
                                       "=<<" "=/=" ">-" ">=" ">=>" ">>" ">>-" ">>=" ">>>" "<*"
                                       "<*>" "<|" "<|>" "<$" "<$>" "<!--" "<-" "<--" "<->" "<+"
                                       "<+>" "<=" "<==" "<=>" "<=<" "<>" "<<" "<<-" "<<=" "<<<"
                                       "<~" "<~~" "</" "</>" "~@" "~-" "~>" "~~" "~~>" "%%"))
  (global-ligature-mode 't))
#+END_SRC

Adjust font size to screen resolution, increase font size for 4K screens

#+BEGIN_SRC emacs-lisp
(when (and (display-graphic-p) (>= (x-display-pixel-width) 3840))
  (set-face-attribute 'default nil :font (format "%s 14" my/font-family)))
#+END_SRC

#+begin_src emacs-lisp
(define-minor-mode big-font-mode
  "Switch between a regular font size and a presentation font size."
  :init-value nil
  :global t
  (if big-font-mode
      (progn
        ;; save current font size in a temp variable to be able to restore it
        ;; after this minor mode is disabled
        (setq big-font-mode--tmp (/ (face-attribute 'default :height) 10))
        (set-face-attribute 'default nil :font (format "%s 24" my/font-family)))
    (set-face-attribute 'default nil :font (format "%s %d" my/font-family big-font-mode--tmp))))
#+end_src

Parrot mode FTW!

#+BEGIN_SRC emacs-lisp
(defun my/parrot-animate-when-compile-success (buffer result)
  (if (string-match "^finished" result)
      (parrot-start-animation)))

(use-package parrot
  :ensure t
  :hook
  (elpaca-after-init . parrot-mode)
  (before-save . parrot-start-animation)
  :config
  (add-to-list 'compilation-finish-functions 'my/parrot-animate-when-compile-success))
#+END_SRC

Open scratch buffer

#+BEGIN_SRC emacs-lisp
(defun my/scratch-buffer()
  "Switch to scratch buffer."
  (interactive)
  (let ((buffer (get-buffer "*scratch*")))
    (if buffer
        (switch-to-buffer buffer)
      (switch-to-buffer (get-buffer-create "*scratch*"))
      (insert (substitute-command-keys initial-scratch-message)))
    (lisp-interaction-mode)))
#+END_SRC

** Ansi term

For some reason ansi-term doesn't respect the global keybinding for =M-]= so this have to be setup in =term-raw-map= as well.

#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'term
  (define-key term-raw-map (kbd "M-]") 'next-buffer))
#+END_SRC

** xref

Enable evil emacs state when entering a xref buffer

#+begin_src emacs-lisp
(with-eval-after-load 'xref
  (add-hook 'xref-after-update-hook #'(lambda () (evil-emacs-state))))
#+end_src

** Occur

#+begin_src emacs-lisp
;; occur is part of replace.el file
(with-eval-after-load 'replace
  (define-key occur-mode-map (kbd "C-c C-e") 'occur-edit-mode)
  ;; this will run every time search results are shown
  (setq occur-hook #'(lambda ()
                       (window-buffer)
                       (select-window (get-buffer-window "*Occur*"))
                       (evil-normal-state))))
#+end_src

** Popper.el

Display some special buffers in a little window always at bottom

#+begin_src emacs-lisp
(use-package popper
  :ensure t
  :init
  (setq popper-reference-buffers
        '("\\*eldoc\\*"
          help-mode))
  (popper-mode +1))
#+end_src

** Compilation

Disable =h= key-binding, this has a conflict with evil-mode left navigation key-binding.

#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'compile
  ;; set cursor to follow compilation output
  (setq compilation-scroll-output t)
  ;; for elixir testing output test filename use black color which makes it ineligible
  (set-face-foreground 'ansi-color-bold "magenta")
  (define-key compilation-mode-map (kbd "C-n") 'compilation-next-error)
  (define-key compilation-mode-map (kbd "C-p") 'compilation-previous-error)
  (define-key compilation-mode-map (kbd "g") nil)
  (define-key compilation-mode-map (kbd "r") 'recompile)
  (define-key compilation-mode-map (kbd "h") nil))
#+END_SRC

Translate ANSI escape sequences into faces, for example to show colors.

#+BEGIN_SRC emacs-lisp
(require 'ansi-color)

(add-hook 'compilation-filter-hook 'ansi-color-compilation-filter)
#+END_SRC

Custom function to select a base directory before running compilation. ~M-x compile~ always use the base directory of the buffer from where it was called as ~default-directory~, this is awful when you want to run a project compilation command from a nested file buffer.

#+begin_src emacs-lisp
(defun my/compile ()
  "Run compilation process but ask for a `default-directory' before."
  (interactive)
  (let ((default-directory (read-directory-name "Base directory: " (my/project-root)))
        (cmd (read-string "Compile command: ")))
    ;; we need to "export" this variable to be able to re-run `compile' command
    (setq compile-command cmd)
    (compile compile-command)))
#+end_src

** Async shell commands

Helper function to run ~async-shell-command~ with some tweaks.

#+begin_src emacs-lisp
(defun my/async-shell-command ()
  "Run `async-shell-command' but at any location.
By default it will use project root but this can be changed"
  (interactive)
  (let* ((default-directory (read-directory-name "Base directory: " (my/project-root)))
         (action-name (read-string "Action name: "))
         (command (read-string "shell command: "))
         (buffer-name (format "(%s)*%s*" (my/project-name) action-name)))
    (async-shell-command command (get-buffer-create buffer-name))))
#+end_src

** Narrowing

#+begin_src emacs-lisp
(defun my/toggle-narrowing ()
  "Toggle narrow on the selected region."
  (interactive)
  (if (buffer-narrowed-p)
      (widen)
    (if (region-active-p)
        (narrow-to-region (region-beginning) (region-end))
      (user-error "No active selection"))))
#+end_src

** Ediff

#+begin_src emacs-lisp
(with-eval-after-load 'ediff
  (setq ediff-split-window-function 'split-window-horizontally)
  ;; put ediff buffer in a buffer at the bottom instead of in a new frame
  (setq ediff-window-setup-function 'ediff-setup-windows-plain))
#+end_src

* Theme and styles

** Spaceink theme

#+begin_src emacs-lisp
(use-package color-theme-sanityinc-tomorrow
  :ensure t
  :config
  (load-theme 'sanityinc-tomorrow-bright t))
#+end_src

** Doom modeline

Enable =display-battery-mode= only if a battery is present. =(battery)= reports "battery N/A" on
desktops, which we use to detect the absence of a battery.

#+BEGIN_SRC emacs-lisp
(use-package doom-modeline
  :ensure t
  :defer t
  :custom
  (doom-modeline-modal-icon nil)
  (doom-modeline-buffer-file-name-style 'relative-from-project)
  :hook
  (elpaca-after-init . doom-modeline-mode)
  (doom-modeline-mode . (lambda ()
                          ;; prevent show (battery) message in echo
                          ;; are and *Messages* buffer
                          (let* ((inhibit-message t)
                                 (message-log-max nil)
                                 (battery-p (not (string-match-p "battery N/A" (battery)))))
                            (display-battery-mode (if battery-p 1 -1))))))
#+END_SRC

* Dired

#+BEGIN_SRC emacs-lisp
(with-eval-after-load "dired"
  (add-hook 'dired-mode-hook 'evil-emacs-state)
  (define-key dired-mode-map (kbd "C-c C-e") 'wdired-change-to-wdired-mode))
#+END_SRC

** Dired preview

#+begin_src emacs-lisp
(use-package dired-preview
  :ensure t
  :after dired
  :config
  (define-key dired-mode-map (kbd "P") #'(lambda ()
                                           (interactive)
                                           (dired-preview-display-file (dired-file-name-at-point)))))
#+end_src

** Nerd icons dired

#+begin_src emacs-lisp
(use-package nerd-icons-dired
  :ensure t
  :defer t
  :hook
  (dired-mode . nerd-icons-dired-mode))
#+end_src

** Dired subtree

#+BEGIN_SRC emacs-lisp
(use-package dired-subtree
  :ensure t
  :after dired
  :config
  (define-key dired-mode-map (kbd "<tab>") 'dired-subtree-toggle))
#+END_SRC

* Editor enhancements

** Whitespace

Show special markers for tab and endline characters in ~prog-mode~

#+begin_src emacs-lisp
(use-package whitespace-mode
  :custom
  (whitespace-style '(tab-mark newline-mark))
  (whitespace-display-mappings '((newline-mark ?\n    [?¬ ?\n]  [?$ ?\n])
                                 (tab-mark     ?\t    [?» ?\t] [?\\ ?\t])))
  :hook
  (prog-mode . whitespace-mode))
#+end_src

** Deactivate extended region in visual mode

Allow to visual mode work more like vim visual highlighting.

#+begin_src emacs-lisp
(set-face-attribute 'region nil :extend nil)
#+end_src

** Dark and transparent title bar in macOS

#+BEGIN_SRC emacs-lisp
(when (memq window-system '(mac ns))
  (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
  (add-to-list 'default-frame-alist '(ns-appearance . dark)))
#+END_SRC

** Highlight TODO, FIXME, etc

#+BEGIN_SRC emacs-lisp
(defun my/highlight-todo-like-words ()
  "Highlight TODO like keywords."
  (font-lock-add-keywords
   nil `(("\\<\\(FIXME\\|TODO\\|BUG\\)" 1 font-lock-warning-face t)
         ("\\<\\(NOTE\\|HACK\\)" 1 font-lock-doc-face t))))

(add-hook 'prog-mode-hook 'my/highlight-todo-like-words)
#+END_SRC

** Auto fill mode

Use ~auto-fill-mode~ only for comments and only with programming buffers

#+begin_src emacs-lisp
(add-hook 'prog-mode-hook #'(lambda ()
                              (setq-local comment-auto-fill-only-comments t)
                              (auto-fill-mode 1)))
#+end_src

** Load PATH environment

=exec-path-from-shell= by default uses =("-l" "-i")= when starts a new shell to get the =PATH=, =-i= option was removed to open a non interactive shell so it can be faster at startup.

#+BEGIN_SRC emacs-lisp
(use-package exec-path-from-shell
  :ensure t
  :custom
  (exec-path-from-shell-arguments '("-l"))
  (exec-path-from-shell-check-startup-files nil)
  :config
  (when (memq window-system '(mac ns))
    (exec-path-from-shell-initialize)))
#+END_SRC

** Editorconfig

#+BEGIN_SRC emacs-lisp
(use-package editorconfig
  :ensure nil
  :hook
  (elpaca-after-init . editorconfig-mode))
#+END_SRC

** Snippets

#+BEGIN_SRC emacs-lisp
(use-package yasnippet
  :ensure t
  :hook ((prog-mode . yas-minor-mode)
         (conf-mode . yas-minor-mode)
         (text-mode . yas-minor-mode)
         (snippet-mode . yas-minor-mode)))

(use-package yasnippet-snippets
  :ensure t
  :after (yasnippet))
#+END_SRC

** Wakatime

#+begin_src emacs-lisp
(use-package wakatime-mode
  :ensure t
  :if (executable-find "wakatime-cli")
  :init
  (setq wakatime-cli-path (executable-find "wakatime-cli"))
  :config
  (global-wakatime-mode))
#+end_src

** Highlight thing

#+BEGIN_SRC emacs-lisp
(use-package highlight-thing
  :ensure t
  :hook
  (prog-mode . highlight-thing-mode))
#+END_SRC

** Various changes

Disable lock files

#+BEGIN_SRC emacs-lisp
(setq create-lockfiles nil)
#+END_SRC

** Reformatter

#+BEGIN_SRC emacs-lisp
(use-package reformatter
  :ensure t
  :defer t)
#+END_SRC

** Hydra for major modes

#+begin_src emacs-lisp
(use-package major-mode-hydra
  ;; we need to this to be installed before continuing because we need
  ;; it to configure other packages with :hydra option
  :ensure (:wait t)
  :config
  (with-eval-after-load 'evil
    (evil-define-key nil 'global (kbd "<leader>\\") #'(lambda ()
                                                        (interactive)
                                                        (major-mode-hydra)))))

(use-package hydra-posframe
  :ensure (:host github :repo "Ladicle/hydra-posframe")
  :if (display-graphic-p)
  :hook (elpaca-after-init . hydra-posframe-mode))
#+end_src

** Vterm

Function to search into zsh history

#+begin_src emacs-lisp
(defun my/select-from-zsh-history ()
  "Selectt one option from ~/.zsh_history file."
  (with-temp-buffer
    (insert-file-contents (expand-file-name "~/.zsh_history"))
    (let* ((raw-content (buffer-substring-no-properties (point-min) (point-max)))
           (lines (string-split raw-content "\n"))
           (choices (mapcar (lambda (line) (cl-second (string-split line ";"))) lines)))
      (completing-read "Select command: " choices))))

(defun my/insert-from-zsh-history ()
  "Search into zsh history and insert the selected choice into buffer."
  (interactive)
  (when-let* ((selected-choice (my/select-from-zsh-history)))
    (vterm-insert selected-choice)))
#+end_src

#+begin_src emacs-lisp
(use-package vterm
  :ensure t
  :defer t
  :hook
  (vterm-mode . (lambda ()
                  (setq-local show-trailing-whitespace nil)))
  :mode-hydra
  (vterm-mode
   (:title (concat (nerd-icons-icon-for-buffer) " Vterm commands"))
   ("History"
    (("i" my/insert-from-zsh-history "Insert command from history"))))
  :custom
  (vterm-module-cmake-args "-DUSE_SYSTEM_LIBVTERM=yes")
  (vterm-always-compile-module t))
#+end_src

** Toggle terminal

='project= use always the same terminal per project, this way we avoid to create a new terminal for each call to =vterm-toggle=.
='reset-window-configration= yes, it's suppose to be =configration=, for some reason it was defined like this instead of =configuration=

Also for easy access insert mode is activated right away after vterm is shown

#+begin_src emacs-lisp
(use-package vterm-toggle
  :ensure t
  :custom
  (vterm-toggle-scope 'project)
  (vterm-toggle-hide-method 'reset-window-configration)
  :hook
  (vterm-toggle-show . evil-insert-state))
#+end_src

** Spelling

#+begin_src emacs-lisp
(use-package jit-spell
  :ensure t
  :hook ((text-mode . jit-spell-mode)
         (prog-mode . jit-spell-mode))
  :config
  (define-key jit-spell-mode-map (kbd "M-\\") 'jit-spell-correct-word))
#+end_src

** iSpell

Avoid check spelling in markdown code blocks

#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'ispell
  (setq ispell-program-name "aspell")
  (add-to-list 'ispell-skip-region-alist
               '("^```" . "^```")))
#+END_SRC

When editing a commit message =ispell= should ignore lines that start with =#=, these lines are diff details about the commit.

#+begin_src emacs-lisp
(defun my/setup-ispell-for-commit-message ()
  "Setup `ispell-skip-region-alist' to avoid lines starting with #.
  This way diff code will be ignored when ispell run."
  (setq-local ispell-skip-region-alist (cons '("^#" . "$") ispell-skip-region-alist)))
#+end_src

** Tree sitter

Incremental code parsing for better syntax highlighting

#+begin_src emacs-lisp
(use-package treesit-auto
  :ensure t
  :custom (treesit-auto-install t))
  ;; current version of treesit-auto-mode has a bug which causes a lot
  ;; of calls which makes Emacs slow, this will disabled until a fix
  ;; is made
  ;; :config
  ;; (global-treesit-auto-mode))
#+end_src

Run ispell in text nodes

#+begin_src emacs-lisp
(use-package treesit-ispell
  :ensure t
  :defer t
  :bind (("C-x C-s" . treesit-ispell-run-at-point)))
#+end_src

Set maximum value for ~font-locking~ in ~treesit~ native syntax highlighting

#+begin_src emacs-lisp
(with-eval-after-load 'treesit
  (setq treesit-font-lock-level 4))
#+end_src

* Evil

#+begin_src emacs-lisp
(defun my/find-file-under-cursor ()
  "Check it the filepath under cursor is an absolute path otherwise open `project-find-file' and insert the filepath."
  (interactive)
  (let ((file-path (thing-at-point 'filename t)))
    (if (file-name-absolute-p file-path)
        (find-file-at-point file-path)
      (minibuffer-with-setup-hook #'(lambda () (insert file-path))
        (project-find-file)))))
#+end_src

#+BEGIN_SRC emacs-lisp
(use-package evil
  :ensure t
  :defer t
  :init
  (setq evil-emacs-state-cursor '("white" box)
        evil-normal-state-cursor '("green" box)
        evil-visual-state-cursor '("orange" box)
        evil-insert-state-cursor '("red" bar)
        evil-want-keybinding nil
        ;; use emacs-28 undo system
        evil-undo-system 'undo-redo)
  :config
  (evil-mode 1)
  (modify-syntax-entry ?_ "w")
  (define-key evil-normal-state-map (kbd "C-p") 'diff-hl-previous-hunk)
  (define-key evil-normal-state-map (kbd "C-n") 'diff-hl-next-hunk)
  (define-key evil-normal-state-map "gf" 'my/find-file-under-cursor)
  (define-key evil-motion-state-map "gd" 'my/goto-definition-dumb-jump-fallback)
  (define-key evil-motion-state-map "gr" 'xref-find-references)
  (define-key evil-motion-state-map "K" 'eldoc-doc-buffer)
  (add-hook 'prog-mode-hook #'(lambda ()
                                (modify-syntax-entry ?_ "w")))

  ;; Setup leader key only for `normal', `visual' and `motion' modes
  (evil-set-leader '(normal visual motion) (kbd "\\"))

  (evil-define-key nil 'global (kbd "<leader>SPC") #'(lambda ()
                                                       (interactive)
                                                       (call-interactively #'execute-extended-command)))
  (evil-define-key nil 'global (kbd "<leader>a") #'(lambda ()
                                                     (interactive)
                                                     (if (region-active-p)
                                                         (my/grep-in-project (buffer-substring-no-properties (region-beginning) (region-end)))
                                                       (my/grep-in-project (thing-at-point 'symbol)))))
  (evil-define-key nil 'global (kbd "<leader>A") 'my/grep-in-project)
  (evil-define-key nil 'global (kbd "<leader>ba") 'my/add-bookmark)
  (evil-define-key nil 'global (kbd "<leader>bb") 'my/bookmark-switch)
  (evil-define-key nil 'global (kbd "<leader>B") #'(lambda ()
                                                     (interactive)
                                                     (call-interactively #'switch-to-buffer)))
  (evil-define-key nil 'global (kbd "<leader>c") 'vterm-toggle)
  (evil-define-key nil 'global (kbd "<leader>e") 'my/find-file-in-project)
  (evil-define-key nil 'global (kbd "<leader>E") 'my/find-file-in-project-local)
  (evil-define-key nil 'global (kbd "<leader>f") 'find-file)
  (evil-define-key nil 'global (kbd "<leader>g") 'magit-status)
  (evil-define-key nil 'global (kbd "<leader>G") 'magit-file-dispatch)
  (evil-define-key nil 'global (kbd "<leader>i") 'consult-imenu)
  (evil-define-key nil 'global (kbd "<leader>hs") 'diff-hl-stage-current-hunk)
  (evil-define-key nil 'global (kbd "<leader>hk") 'diff-hl-revert-hunk)
  (evil-define-key nil 'global (kbd "<leader>j") 'jj-log)
  (evil-define-key nil 'global (kbd "<leader>k") 'kill-buffer)
  (evil-define-key nil 'global (kbd "<leader>l") 'display-line-numbers-mode)
  (evil-define-key nil 'global (kbd "<leader>n") 'evil-buffer-new)
  (evil-define-key nil 'global (kbd "<leader>N") 'my/toggle-narrowing)
  (evil-define-key nil 'global (kbd "<leader>pa") 'my/copy-abs-path)
  (evil-define-key nil 'global (kbd "<leader>pr") 'my/copy-relative-path)
  (evil-define-key nil 'global (kbd "<leader>q") 'my/consult-line)
  (evil-define-key nil 'global (kbd "<leader>r") 'my/replace-at-point-or-region)
  (evil-define-key nil 'global (kbd "<leader>R") '(lambda ()
                                                    (interactive)
                                                    (save-excursion)
                                                    (with-current-buffer "*compilation*"
                                                      (recompile))))
  (evil-define-key nil 'global (kbd "<leader>s") 'my/toggle-spanish-characters)
  (evil-define-key nil 'global (kbd "<leader>t") 'persp-switch)
  (evil-define-key nil 'global (kbd "<leader>w") 'my/toggle-maximize)
  (evil-define-key nil 'global (kbd "<leader>x") 'my/resize-window)
  (evil-define-key nil 'global (kbd "<leader>y") 'consult-yank-from-kill-ring)

  (face-spec-set
   'evil-ex-substitute-matches
   '((t :foreground "red"
        :strike-through t
        :weight bold)))

  (face-spec-set
   'evil-ex-substitute-replacement
   '((t
      :foreground "green"
      :weight bold))))

(use-package evil-commentary
  :ensure t
  :after (evil)
  :config
  (evil-commentary-mode))

(use-package evil-surround
  :ensure t
  :after (evil)
  :config
  (global-evil-surround-mode 1))

(defun my/replace-at-point-or-region ()
  "Setup buffer replace string for word at point or active region using evil ex mode."
  (interactive)
  (let ((text (if (region-active-p)
                  (buffer-substring-no-properties (region-beginning) (region-end))
                (word-at-point))))
    (evil-ex (concat "%s/" text "/"))))

(use-package evil-matchit
  :ensure t
  :config (global-evil-matchit-mode 1))
#+END_SRC

** Evil collection

Allow to use default terminal keybinding in ~vterm~ without losing evil features

#+begin_src emacs-lisp
(use-package evil-collection
  :ensure t
  :config
  (evil-collection-init '(vterm)))
#+end_src

* Utils

** Which-key

#+BEGIN_SRC emacs-lisp
(use-package which-key
  :ensure nil
  :config
  (which-key-mode)
  (which-key-setup-minibuffer))
#+END_SRC

** Auto pair

Complete parenthesis, square brackets, etc

#+BEGIN_SRC emacs-lisp
(use-package elec-pair
  :ensure nil
  :config
  (electric-pair-mode))
#+END_SRC

** Restclient

#+BEGIN_SRC emacs-lisp
(defun my/toggle-restclient-follow-redirect ()
  "Toggle follow redirect for the current session."
  (interactive)
  (setopt restclient-follow-redirects (not restclient-follow-redirects))
  (if restclient-follow-redirects
      (message "Follow redirect enabled")
    (message "Follow redirect disabled")))

(use-package restclient
  :ensure t
  :defer t
  :mode (("\\.http\\'" . restclient-mode))
  :mode-hydra
  (restclient-mode
   (:title (concat (nerd-icons-icon-for-buffer) " restclient commands"))
   ("Format"
    ;; TODO: change to only apply json formatting when the content-type is application/json
    (("f" json-format-region))
    "Customization"
    (("SPC" my/toggle-restclient-follow-redirect "Toggle follow redirect"))
    "Secrets"
    (("h" cloak-mode "Toggle cloak" :toggle t))
    "Request"
    (("r" restclient-http-send-current-stay-in-window "Execute request")
     ("c" restclient-copy-curl-command "Copy CURL command")))))
#+END_SRC

** Rainbow delimiters

#+BEGIN_SRC emacs-lisp
(use-package rainbow-delimiters
  :ensure t
  :hook
  (prog-mode . rainbow-delimiters-mode))
#+END_SRC

** XML formatter

#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define xml-format
    :program "xmlformat"
    :group 'xml))

(use-package nxml-mode
  :ensure nil
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " XML Commands"))
   ("Format"
    (("f" xml-format-buffer)))))
#+END_SRC

** SQL formatter

#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define sql-format
    :program "sqlfluff"
    :args '("format" "--dialect" "postgres" "--nocolor" "-")))

(defun my/format-sql ()
  "Format active region otherwise format the entire buffer."
  (interactive)
  (if (region-active-p)
      (sql-format-region (region-beginning) (region-end))
    (sql-format-buffer)))

(use-package sql
  :ensure nil
  :hook ((sql-mode . flymake-sqlfluff-load)
         (sql-mode . flymake-mode))
  :mode-hydra
  (sql-mode
   (:title (concat (nerd-icons-icon-for-buffer) " SQL Commands"))
   ("Format"
    (("f" my/format-sql)))))
#+END_SRC

** SQL linter using [[https://www.sqlfluff.com][sqlfluff]]

#+begin_src emacs-lisp
(use-package flymake-sqlfluff
  :ensure t)
#+end_src

* Common packages

Used in every major mode

** Company

#+BEGIN_SRC emacs-lisp
(use-package company
  :ensure t
  :init
  (setq company-idle-delay 0.1
        company-tooltip-limit 10
        company-minimum-prefix-length 3)
  :hook (elpaca-after-init . global-company-mode)
  :config
  (define-key company-active-map (kbd "C-n") 'company-select-next)
  (define-key company-active-map (kbd "C-p") 'company-select-previous))
#+END_SRC

** Flymake

Only activate =flymake= for actual projects and for =prog-mode=

#+begin_src emacs-lisp
(defun my/setup-flymake ()
  "Activate flymake only if we are inside a project."
  (when (functionp 'my/project-p)
    (flymake-mode 1)))

(add-hook 'prog-mode-hook 'my/setup-flymake)

(with-eval-after-load "flymake"
  (setq flymake-show-diagnostics-at-end-of-line nil)
  (define-key flymake-mode-map (kbd "C-S-n") 'flymake-goto-next-error)
  (define-key flymake-mode-map (kbd "C-S-p") 'flymake-goto-prev-error))
#+end_src

** Mise

Handle environment variables per buffer using a ~mise.toml~ file.

#+BEGIN_SRC emacs-lisp
(use-package mise
  :ensure t
  :hook
  (elpaca-after-init . global-mise-mode))
#+END_SRC

Hydra commands, this have to be defined this way instead of using ~:mode-hydra~ inside ~use-package~ because
for some reason it mess up with environment variables loading

#+begin_src emacs-lisp
;TODO: migrate to use a specific major mode for mise.toml files
;; (with-eval-after-load 'major-mode-hydra
;;   (major-mode-hydra-define envrc-file-mode
;;     (:title (concat (nerd-icons-icon-for-buffer) " envrc commands"))
;;     ("Secrets"
;;      (("h" cloak-mode "Toggle cloak" :toggle t)))))
#+end_src

** Cloak mode

Hide values that match regex patterns for ~restclient~ files

#+begin_src emacs-lisp
(use-package cloak-mode
  :ensure t
  :custom
  (cloak-mode-patterns '((restclient-mode . "^:[^: ]+[ \t]*=[ \t]*\\(.+?\\)$")
                         ("mise.local.toml" . "[a-zA-Z_-]+[ \t]*=[ \t]*\"\\([^\"]+\\)\"")))
  (cloak-mode-mask "🙈🙈🙈")
  :hook
  (elpaca-after-init . global-cloak-mode))
#+end_src

** perspective.el

#+begin_src emacs-lisp
(use-package perspective
  :ensure t
  :custom
  ;; disable warnings
  (persp-suppress-no-prefix-key-warning t)
  (persp-modestring-short t)
  :config
  (persp-mode)
  ;; setup vim tab like key-bindings
  (define-key evil-normal-state-map (kbd "gt") 'persp-next)
  (define-key evil-normal-state-map (kbd "gT") 'persp-prev))
#+end_src

** Project.el

~project.el~ default prefix is ~C-x~

#+begin_src emacs-lisp
(defun my/project-edit-dir-locals ()
  "Edit .dir-locals.el file in project root."
  (interactive)
  (find-file (expand-file-name ".dir-locals.el" (my/project-root))))

(defun my/project-edit-mise ()
  "Edit mise.toml file in project root."
  (interactive)
  (find-file (expand-file-name "mise.toml" (my/project-root))))

(use-package project
  :ensure nil
  :bind (:map project-prefix-map
              ("m" . 'my/project-edit-mise)
              ("d" . 'project-dired)
              ("e" . 'my/project-edit-dir-locals)
              ("k" . 'my/project-kill-buffers)
              ("n" . 'my/project-open-new-project)
              ("p" . 'my/project-switch)))
#+end_src

Define helper functions to be used by other packages

#+begin_src emacs-lisp
(defun my/project-root ()
  "Return project root path."
  (project-root (project-current)))

(defun my/project-p ()
  (project-current))

(defun my/project-name ()
  "Get project name extracting latest part of project path."
  (if (my/project-p)
      (cl-second (reverse (split-string (my/project-root) "/")))
    nil))
#+end_src

~perspective.el~ integration, a new perspective should be "attached" to a project so it's easy to switch between them.

#+BEGIN_SRC emacs-lisp
(defun my/project-switch ()
  "Switch to a project and trigger switch action."
  (interactive)
  ;; make sure all the projects list is available to be used
  (project--ensure-read-project-list)
  (let* ((projects (mapcar 'car project--list))
         (choice (completing-read "Switch to project: " projects))
         (default-directory choice))
    ;; `default-directory' must be defined so `project.el' can know is in a new project
    (my/project-switch-action)))

(defun my/project-switch-action ()
  "Switch to a new perspective which name is project's name and open `project-find-file'."
  (interactive)
  (persp-switch (my/project-name))
  (project-find-file))

(defun my/project-kill-buffers ()
  "Kill all the related buffers to the current project and delete its perspective as well."
  (interactive)
  (let* ((project-name (my/project-name))
         (project (project-current))
         (buffers-to-kill (project--buffers-to-kill project)))
    (when (yes-or-no-p (format "Kill %d buffers in %s?" (length buffers-to-kill) (my/project-root)))
      ;; in case we're using eglot we shutdown its server
      (if (and (featurep 'eglot) (eglot-managed-p))
          (eglot-shutdown (eglot-current-server)))
      (mapc #'kill-buffer buffers-to-kill)
      (persp-kill project-name))))

(defun my/project-open-new-project ()
  "Open a project for the first time and add it to `project.el' projects list."
  (interactive)
  (let* ((project-path-abs (read-directory-name "Enter project root: "))
         ;; we need to define `default-directory' to be able to get the new project when `project-current' is called
         (default-directory (replace-regexp-in-string (expand-file-name "~") "~" project-path-abs)))
    (project-remember-project (project-current))
    (my/project-switch-action)))
#+END_SRC

** Completion

UI for completion

#+begin_src emacs-lisp
(use-package vertico
  :ensure t
  :hook
  (elpaca-after-init . vertico-mode)
  :custom
  ;; fixed height
  (vertico-resize nil)
  ;; show max 15 elements
  (vertico-count 15)
  :config
  ;; delete word like a regular buffer in emacs state
  (define-key vertico-map (kbd "C-w") #'vertico-directory-delete-word)
  ;; `C-;' will open embark and `o' with execute `find-file-other-window'
  (define-key vertico-map (kbd "C-<return>") (kbd "C-; o")))
#+end_src

Vertico posframe, show all the candidates in a child-frame, it will activated only for GUI version.

#+begin_src emacs-lisp
(use-package vertico-posframe
  :ensure t
  :if (display-graphic-p)
  :custom
  (vertico-posframe-width 100)
  (vertico-posframe-poshandler (lambda (info)
                                 (cons (/ (- (plist-get info :parent-frame-width)
                                             (plist-get info :posframe-width))
                                          2)
                                       40)))
  :hook
  (vertico-mode . vertico-posframe-mode))
#+end_src

Add annotations to results shown by vertico

#+begin_src emacs-lisp
(use-package marginalia
  :ensure
  :custom
  (marginalia-align 'right)
  :hook
  (elpaca-after-init . marginalia-mode))
#+end_src

Icons support

#+begin_src emacs-lisp
(use-package nerd-icons-completion
  :ensure t
  :after marginalia
  :config
  (nerd-icons-completion-mode)
  (add-hook 'marginalia-mode-hook #'nerd-icons-completion-marginalia-setup))
#+end_src

Enable better completion styles

#+begin_src emacs-lisp
(use-package orderless
  :ensure t
  :config
  (setq completion-styles '(orderless basic)
        completion-category-overrides '((file (styles basic partial-completion)))))
#+end_src

Disable orderless completion style in company to keep previous behaviour which I like, this was copied from ~orderless~ documentation.

#+begin_src emacs-lisp
;; We follow a suggestion by company maintainer u/hvis:
;; https://www.reddit.com/r/emacs/comments/nichkl/comment/gz1jr3s/
(defun company-completion-styles (capf-fn &rest args)
  (let ((completion-styles '(basic partial-completion)))
    (apply capf-fn args)))

(advice-add 'company-capf :around #'company-completion-styles)
#+end_src

Search into project source

#+begin_src emacs-lisp
(use-package consult
  :ensure t
  :config
  ;; without --hidden ripgrep avoid hidden files like yaml files
  ;; inside a .github folder or this file inside .emacs.d
  (setopt consult-ripgrep-args (concat consult-ripgrep-args " --hidden "))
  ;; Use consult for completion inside minibuffer, for example when
  ;; searching for a file.
  (setq completion-in-region-function #'consult-completion-in-region))
#+end_src

Custom ~consult-line~

#+begin_src emacs-lisp
(defun my/consult-line ()
  "Call `consult-line' with current selection if possible."
  (interactive)
  (if (region-active-p)
      (let ((text (buffer-substring-no-properties (region-beginning) (region-end))))
        (minibuffer-with-setup-hook #'(lambda () (insert text)) (consult-line)))
    (consult-line)))
#+end_src

Integration with yasnippets

#+begin_src emacs-lisp
(use-package consult-yasnippet
  :ensure t
  :defer t)
#+end_src

Helpers to search term at point and general search into project

#+begin_src emacs-lisp
(defun my/grep-in-project (&optional term)
  "Run grep in current project.
If TERM is not nil it will be used as initial value."
  (interactive)
  (let* ((pattern (read-string "Pattern: " (or term "")))
         ;; add an extra space to be able to start typing more filters
         (pattern (concat pattern " ")))
    (call-interactively #'(lambda ()
                            (interactive)
                            (consult-ripgrep (my/project-root) pattern)))))
#+end_src

Switch fonts like ~consult-theme~

#+begin_src emacs-lisp
(defun my/consult-font (font)
  "Replace current font with FONT from `font-family-list'."
  (interactive
   (list
    (let ((saved-font (symbol-name (font-get (face-attribute 'default :font) :family))))
      (consult--read
       (font-family-list)
       :prompt "Font: "
       :require-match t
       :state (lambda (action font)
                (pcase action
                  ('return (my/consult-font (or font saved-font)))
                  ((and 'preview (guard font)) (my/consult-font font))))
       ))))
  (when font
    ;; size doesn't change during scrolling so we can reuse that to
    ;; configure new selected font
    (set-face-attribute 'default nil :font (format "%s %d" font (font-get (face-attribute 'default :font) :size)))))
#+end_src

Integration with ~embark~

#+begin_src emacs-lisp
(use-package embark
  :ensure t
  :bind
  ("C-;" . embark-act)
  :config
  ;; grep exported data can have a lot of white spaces so we don't want
  ;; them to be shown while editing their content
  (setq-mode-local embark-collect-mode show-trailing-whitespace nil))

(use-package embark-consult
  :ensure t
  :defer t)

(defun my/edit-completing-results ()
  "Use results origin to execute an action after export them with `embark-export'."
  (interactive)
  ;; call of `project-find-file'
  (when (cl-search "Find file in" (buffer-string))
    (run-at-time 0 nil #'embark-export)
    (run-at-time 0 nil #'wdired-change-to-wdired-mode)
    (run-at-time 0 nil #'evil-normal-state))
  ;; call of `consult-ripgrep'
  (when (cl-search "Ripgrep" (buffer-string))
    ;; we use `run-at-time' to ensure all of these steps
    ;; will be executed in order
    (run-at-time 0 nil #'embark-export)
    (run-at-time 0 nil #'wgrep-change-to-wgrep-mode)
    (run-at-time 0 nil #'evil-normal-state)))

(define-key minibuffer-mode-map (kbd "C-c C-e") #'my/edit-completing-results)
#+end_src

Edit grep buffer

#+begin_src emacs-lisp
(use-package wgrep
  :ensure t
  :custom
  (wgrep-auto-save-buffer t)
  :bind
  ("C-c C-c" . 'wgrep-finish-edit)
  ("C-c C-k" . 'wgrep-abort-changes))
#+end_src


* Emacs process list

#+begin_src emacs-lisp
(defun my/kill-emacs-process ()
  "Show a list of current Emacs processes and kill the selected one."
  (interactive)
  (let* ((names (mapcar #'process-name (process-list)))
         (process-name (completing-read "Choose process: " names)))
    (delete-process (get-process process-name))
    (message "%s process killed" (propertize process-name 'face '(:foreground "magenta")))))

(global-set-key (kbd "C-x c p") 'my/kill-emacs-process)
#+end_src

* Git backup

Save a backup on every save, also allow to recover any version of a file

#+begin_src emacs-lisp :lexical t
(defvar my/backup-dir (expand-file-name "~/.git-backup"))

(defun my/git-backup-versioning ()
  "Save a version of the current file."
  (unless (featurep 'git-backup)
    (require 'git-backup))
  (git-backup-version-file (executable-find "git") my/backup-dir '() (buffer-file-name)))

(defun my/git-backup-run-action (command commit-hash)
  "Execute COMMAND with COMMIT-HASH using another defaults arguments."
  (apply command `(,(executable-find "git") ,my/backup-dir ,commit-hash ,(buffer-file-name))))

(defun my/git-backup-sort (completions)
  "Given COMPLETIONS define a custom sort function."
  (lambda (string pred action)
    (if (eq action 'metadata)
        '(metadata (display-sort-function . identity))
      (complete-with-action action completions string pred))))

(defun my/git-backup ()
  "Navigate in versions of the current file."
  (interactive)
  (unless (featurep 'git-backup)
    (require 'git-backup))
  ;; for some reason an extra space after `%h|' is required to avoid an error when
  ;; the shell command is executed
  (let* ((candidates (git-backup-list-file-change-time (executable-find "git") my/backup-dir "%cI|%h| %ar" (buffer-file-name)))
         (selection (completing-read "Pick revision: " (my/git-backup-sort candidates)))
         (commit-hash (nth 1 (string-split selection "|")))
         (action (completing-read "Choose action: " '("diff" "new buffer" "replace current buffer"))))
    (cond ((string-equal action "diff") (my/git-backup-run-action 'git-backup-create-ediff commit-hash))
          ((string-equal action "new buffer") (my/git-backup-run-action 'git-backup-open-in-new-buffer commit-hash))
          ((string-equal action "replace current buffer") (my/git-backup-run-action 'git-backup-replace-current-buffer commit-hash))
          (t (message "Not valid option")))))

(use-package git-backup
  :ensure t
  :hook (after-save . my/git-backup-versioning))
#+end_src

* Meme

Create memes from a file or the macOS clipboard in a graphical editor with a live
preview. ImageMagick is only required when saving the result to a format other than SVG.

#+begin_src emacs-lisp
(use-package mini-meme
  :ensure (:host github :repo "erickgnavar/mini-meme.el")
  :commands mini-meme)

(defun my/meme-from-clipboard ()
  "Create a meme using an image from clipboard."
  (interactive)
  (unless (executable-find "pngpaste")
    (user-error "please install pngpaste"))

  (let* ((filepath (make-temp-file "clipboard" nil ".png"))
         (command (format "pngpaste %s" filepath))
         (command-stdout (shell-command-to-string command)))
    ;; pngpaste returns "" when found a valid image in the clipboard
    (unless (string-equal command-stdout "")
      (user-error (string-trim command-stdout)))

    (mini-meme filepath)
    ;; Mini Meme embeds the image when rendering, so keep the temporary
    ;; file until its editor buffer is closed.
    (add-hook 'kill-buffer-hook
              (lambda ()
                (when (file-exists-p filepath)
                  (delete-file filepath)))
              nil t)))
#+end_src

* Orgmode

Configured variables:

- =org-latex-caption-above= puts table captions at the bottom
- =org-src-fontify-natively= enables syntax highlighting for code blocks
- =org-log-done= saves the timestamp when a task is done
- =org-src-preserve-indentation= when is =t= avoid to insert a left indentation in source blocks

#+begin_src emacs-lisp

(defun my/org-insert-image-from-clipboard ()
  "Insert image from clipboard using an org tag"
  (interactive)
  (let* ((image-name (read-string "Filename: " "image.png"))
         (images-folder "./images")
         (image-path (format "%s/%s" images-folder image-name)))
    (unless (file-directory-p images-folder)
      (shell-command (format "mkdir -p %s" images-folder)))
    (shell-command (format "pngpaste %s" image-path))
    (insert (format "[[file:%s]]" image-path))))

(with-eval-after-load 'evil
  (evil-define-key nil org-mode-map (kbd "<leader>mii") 'my/org-insert-image-from-clipboard))

#+end_src

When I read books on Apple Books and I want to insert some quote Apple Books insert some text I don't want in my notes, this function delete that and just insert the meaning part using org quote syntax.

#+begin_src emacs-lisp
(defun my/org-insert-quote-from-apple-books ()
  "Take quote from clipboard and remove all the unnecesary text and insert
    an org quote in the current position"
  (interactive)
  (let* ((raw-value (current-kill 0 t))
         (tmp (cl-second (split-string raw-value "“")))
         (quote-value (car (split-string tmp "”"))))
    (insert "#+begin_quote\n")
    (insert (concat quote-value "\n"))
    (insert "#+end_quote\n")))
#+end_src

#+BEGIN_SRC emacs-lisp
(defvar my/org-src-block-tmp-window-configuration nil)

(defun my/org-edit-special (&optional arg)
  "Save current window configuration before a org-edit buffer is open."
  (setq my/org-src-block-tmp-window-configuration (current-window-configuration)))

(defun my/org-edit-src-exit ()
  "Restore the window configuration that was saved before org-edit-special was called."
  (set-window-configuration my/org-src-block-tmp-window-configuration))

(with-eval-after-load 'org
  ;; do not show trailing spaces in calendar buffer
  (add-hook 'calendar-mode-hook #'(lambda ()
                                    (setq-local show-trailing-whitespace nil)))

  (setq org-latex-caption-above nil
        org-src-fontify-natively t
        org-src-preserve-indentation t
        ;; use tectonic for export to PDF
        org-latex-pdf-process '("tectonic -X compile --outdir=%o -Z shell-escape %f")
        org-log-done t)
  (org-clock-persistence-insinuate)

  (add-hook 'org-mode-hook #'(lambda ()
                               (setq-local fill-column 100)
                               (auto-fill-mode 1)
                               ;; Since emacs 27 this is needed to use shortcuts like <s to create source blocks
                               (unless (featurep 'org-tempo)
                                 (require 'org-tempo))
                               (org-indent-mode -1)
                               ;; disable electric-pair to avoid conflict with ~<s~
                               (electric-pair-local-mode -1)))

  (advice-add 'org-edit-special :before 'my/org-edit-special)
  (advice-add 'org-edit-src-exit :after 'my/org-edit-src-exit)

  ;; setup python executable, it can be installed from homebrew, nix
  ;; or in another way
  (setq org-babel-python-command (executable-find "python3"))

  (org-babel-do-load-languages 'org-babel-load-languages
                               '((python . t)
                                 (shell . t)
                                 (lisp . t)
                                 (sql . t)
                                 (dot . t)
                                 (plantuml . t)
                                 (emacs-lisp . t))))

(use-package htmlize
  :ensure t
  :after (org))
#+END_SRC

Fix error with ~TAB~ in ~evil-mode~ in ~org-mode~ with org elements.

#+begin_src emacs-lisp
(defun my/org-tab ()
  "Run `org-cycle' only at point of an org element."
  (interactive)
  (if (org-element-at-point)
      (org-cycle)
    (evil-jump-forward)))

(with-eval-after-load 'org
  (define-key org-mode-map (kbd "<tab>") 'my/org-tab))
#+end_src

** Org typst

Allow to export to PDF using ~typst~ instead of ~latex~

#+begin_src emacs-lisp
(use-package ox-typst
  :ensure t
  :after org)
#+end_src

** Org modern

#+begin_src emacs-lisp
(use-package org-modern
  :ensure t
  :custom
  (org-modern-fold-stars '(("▶" . "▼") ("▷" . "▽") ("▹" . "▿") ("▸" . "▾")))
  :hook
  (org-mode . org-modern-mode))
#+end_src

** Org tree slide

A tool to show org file as an slideshow

=hide-mode-line= hide the modeline to allow to have a clean screen while using =org-tree-slide-mode=

#+begin_src emacs-lisp
(use-package hide-mode-line
  :ensure t
  :defer t)
#+end_src

Some tweaks to have a better looking while presenting slides

#+begin_src emacs-lisp
(defun my/org-tree-slide-setup ()
  (org-display-inline-images)
  (hide-mode-line-mode 1))

(defun my/org-tree-slide-end ()
  (org-display-inline-images)
  (hide-mode-line-mode 0))

(use-package org-tree-slide
  :ensure t
  :defer t
  :custom
  (org-image-actual-width nil)
  (org-tree-slide-activate-message "Presentation started!")
  (org-tree-slide-deactivate-message "Presentation finished!")
  :hook ((org-tree-slide-play . my/org-tree-slide-setup)
         (org-tree-slide-stop . my/org-tree-slide-end))
  :bind (:map org-tree-slide-mode-map
              ("C-<" . org-tree-slide-move-previous-tree)
              ("C->" . org-tree-slide-move-next-tree)))
#+end_src

* Denote

Note taking using ~denote~

#+begin_src emacs-lisp
(use-package denote
  :ensure t
  :custom (denote-directory "~/Documents/wiki")
  :hook ((dired-mode . denote-dired-mode)))

(defun my/wiki ()
  "Open personal wiki and launch Dired."
  (interactive)
  (dired (expand-file-name "~/Documents/wiki"))
  (dired-hide-details-mode t))
#+end_src

* Typst

Define formatter helpers using ~typstyle~

#+begin_src emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define typst-format
    :program "typstyle"))
#+end_src

#+begin_src emacs-lisp
(defun my/typst-compile-and-preview ()
  "Compile and preview PDF for the current buffer."
  (interactive)
  (typst-ts-compile t))

(use-package typst-ts-mode
  :ensure (:type git :host codeberg :repo "meow_king/typst-ts-mode")
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Typst Commands"))
   ("Build"
    (("b" typst-ts-compile "Compile")
     ("p" my/typst-compile-and-preview "Compile and preview"))
    "Format"
    (("f" typst-format-buffer "Format")))))
#+end_src

Set up LSP

#+begin_src emacs-lisp
(with-eval-after-load 'eglot
  (with-eval-after-load 'typst-ts-mode
    (add-to-list 'eglot-server-programs
                 `((typst-ts-mode) .
                   ,(eglot-alternatives '("tinymist"
                                          "typst-lsp"))))))
#+end_src

* Git

** Git-link

Open selected region in remote repo page

#+BEGIN_SRC emacs-lisp
(use-package git-link
  :ensure t
  :defer t
  :commands (git-link))
#+END_SRC

** Git modes

This pacakge includes ~gitignore-mode~, ~gitconfig-mode~ and ~gitattributes-mode~

#+BEGIN_SRC emacs-lisp
(use-package git-modes
  :defer t
  :ensure t)
#+END_SRC

** Transient

#+begin_src emacs-lisp
(use-package transient
  :ensure nil
  :custom
  (transient-save-history nil))
#+end_src

** JJ

#+begin_src emacs-lisp
(use-package jj-mode
  :ensure (:host github :repo "bolivier/jj-mode.el")
  :defer t
  :config
  (with-eval-after-load 'evil
    (evil-make-overriding-map jj-mode-map 'normal)))
#+end_src

** Magit

#+BEGIN_SRC emacs-lisp
(defun my/magit-blame-quit ()
  "Restore evil state after magit blame mode is closed."
  (evil-exit-emacs-state))

(use-package magit
  :ensure t
  :defer t
  :custom
  ;; restore previous window configuration after a buffer is closed
  (magit-bury-buffer-function 'magit-restore-window-configuration)
  ;; open magit status buffer in the whole frame
  (magit-display-buffer-function 'magit-display-buffer-fullframe-status-v1)
  :config
  (advice-add 'magit-blame-quit :after 'my/magit-blame-quit)
  (add-hook 'git-commit-mode-hook 'my/setup-ispell-for-commit-message)
  (add-hook 'magit-blame-mode-hook #'(lambda () (evil-emacs-state))))
#+END_SRC

** Magit delta

Use ~delta~ tool to show diffs in magit

#+begin_src emacs-lisp
(use-package magit-delta
  :ensure t
  :after (magit))

(defun my/toggle-magit-delta ()
  "Toggle magit-delta mode."
  (interactive)
  (magit-delta-mode
   (if magit-delta-mode -1 1))
  (magit-refresh))

(with-eval-after-load 'magit
  (transient-append-suffix 'magit-diff '(-1 -1 -1)
    '("D" "Toggle magit-delta" my/toggle-magit-delta)))
#+end_src

** Git diff-hl

#+BEGIN_SRC emacs-lisp
(use-package diff-hl
  :ensure t
  :custom
  (diff-hl-show-staged-changes nil)
  ;; disable highlight reference to avoid confusion with status
  ;; indicators in fringe area
  (diff-hl-highlight-reference-function nil)
  ;; for some reason the :hook form doesn't work so we have to use :init
  :init
  (add-hook 'magit-pre-refresh-hook 'diff-hl-magit-pre-refresh)
  (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)
  (add-hook 'dired-mode-hook 'diff-hl-dired-mode)
  :config
  (global-diff-hl-mode))
#+END_SRC

** Timemachine

#+BEGIN_SRC emacs-lisp
(use-package git-timemachine
  :ensure t
  :config
  (add-hook 'git-timemachine-mode-hook #'(lambda () (evil-emacs-state))))
#+END_SRC

** Gist

#+BEGIN_SRC emacs-lisp
(defun my/gist ()
  "Create a public gist."
  (interactive)
  (my/create-gist t))

(defun my/private-gist ()
  "Create a private gist."
  (interactive)
  (my/create-gist :false))

(defun my/create-gist (public)
  "Create a gist, given PUBLIC value it will set as private or public."
  (let* ((oauth-token (my/gist-get-oauth-token))
         (content (my/gist-get-content))
         (payload (make-hash-table))
         (filename (or (and (buffer-file-name) (file-name-nondirectory (buffer-file-name))) (buffer-name)))
         (files (make-hash-table))
         (file (make-hash-table)))
    (puthash "description" "" payload)
    (puthash "public" public payload)
    (puthash "content" content file)
    (puthash filename file files)
    (puthash "files" files payload)
    (let ((url-request-method "POST")
          (url-request-extra-headers `(("Content-Type" . "application/json") ("User-Agent" . "gist.el") ("Authorization" . ,(concat "Bearer " oauth-token))))
          (url-request-data (json-encode payload)))
      (url-retrieve "https://api.github.com/gists" 'my/gist-handle-response))))

(defun my/gist-handle-response (response)
  "Process content of RESPONSE and extract link."
  (let* ((status-line (buffer-substring-no-properties (line-beginning-position) (line-end-position)))
         (status-code (nth 1 (split-string status-line " "))))

    (unless (string-equal status-code "201")
      (user-error "There was a problem with GitHub API, try again later"))

    (search-forward "\n\n")
    (let* ((raw-response (buffer-substring-no-properties (point) (point-max)))
           (data (json-parse-string raw-response))
           (gist-link (gethash "html_url" data)))
      (kill-new gist-link)
      (message "Paste created: %s" gist-link))))

(defun my/gist-get-oauth-token ()
  "Fetch oauth token from ~/.authinfo."
  (let ((entry (car (auth-source-search :host "gist"))))
    (unless entry
      (user-error "GitHub oauth token must be in ~/.authinfo using 'gist' as host"))
    (funcall (plist-get entry :secret))))

(defun my/gist-get-content ()
  "Create payload using current region or the whole buffer."
  (if (region-active-p)
      (buffer-substring-no-properties (region-beginning) (region-end))
    (buffer-substring-no-properties (point-min) (point-max))))
#+END_SRC

** Linkode

#+BEGIN_SRC emacs-lisp
(use-package linkode
  :ensure t
  :defer t)
#+END_SRC

* Web

** Web mode

#+BEGIN_SRC emacs-lisp
(defun my/web-mode-hook ()
  (emmet-mode)
  (rainbow-delimiters-mode-disable))

(use-package web-mode
  :ensure t
  :custom
  (web-mode-enable-current-element-highlight t)
  (web-mode-enable-current-column-highlight t)
  :mode (("\\.html\\'" . web-mode)
         ("\\.html.eex\\'" . web-mode)
         ("\\.html.leex\\'" . web-mode)
         ("\\.hbs\\'" . web-mode))
  :config
  (add-hook 'web-mode-hook 'my/web-mode-hook))
#+END_SRC

** Emmet

#+BEGIN_SRC emacs-lisp
(use-package emmet-mode
  :ensure t
  :defer t)
#+END_SRC

** CSS mode

#+BEGIN_SRC emacs-lisp
(use-package css-ts-mode
  :ensure nil
  :mode "\\.css\\'"
  :defer t
  :hook (css-ts-mode . flymake-biome-load)
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " CSS Commands"))
   ("Format"
    (("f" css-format-buffer)))))
#+END_SRC

#+begin_src emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define css-format
    :program "biome"
    :args '("format" "--stdin-file-path" "a.css" "--css-formatter-indent-style" "space")))
#+end_src

** Rainbow

#+BEGIN_SRC emacs-lisp
(use-package rainbow-mode
  :ensure t
  :hook
  ((css-ts-mode . rainbow-mode)
   (sass-mode . rainbow-mode)
   (heex-ts-mode . rainbow-mode)
   (scss-mode . rainbow-mode)))
#+END_SRC

* Telegram

In case on error when compiling ~tdlib~ the first time we need to specify where it's installed, for ~macOS~ we can specify:

~(telega-server-libs-prefix "/opt/homebrew/Cellar/tdlib/HEAD-fd3154b")~

The last part can change depending of the version installed

#+begin_src emacs-lisp
(use-package telega
  :ensure t
  :defer t
  :custom
  ;; enable markdown for code snippets
  (telega-chat-input-markups '("markdown2" "org" nil))
  ;; use vertico for completion
  (telega-completing-read-function 'completing-read)
  :config
  ;; use shift enter to make multi line messages and enter to send it
  (define-key telega-chat-mode-map (kbd "S-<return>") #'newline)
  ;; disable copy message link when moving over text with evil-mode
  (define-key telega-msg-button-map (kbd "l") nil)
  ;; avoid showing blank spaces highlighted
  (add-hook 'telega-chat-mode-hook #'(lambda ()
                                       (setq-local show-trailing-whitespace nil))))

(defun my/start-telega ()
  "Start `telega' inside a new perspective and activate 'telega-mode-line-mode'"
  (interactive)
  (persp-switch "*telega*")
  (telega)
  (telega-mode-line-mode))
#+end_src

* Miscellaneous

#+BEGIN_SRC emacs-lisp
(use-package writeroom-mode
  :ensure t
  :defer t)

(use-package csv-mode
  :ensure t
  :defer t)

(with-eval-after-load 'reformatter
  (reformatter-define json-format
    :program "jq"
    :args '(".")
    :group 'json))

(use-package json-ts-mode
  :ensure nil
  :mode "\\.hujson\\'"
  :mode "\\.jsonc?\\'"
  :mode-hydra
  (json-ts-mode
   (:title (concat (nerd-icons-icon-for-buffer) " JSON commands"))
   ("Format"
    (("f" json-format-buffer)))))

(use-package request
  :ensure t
  :defer t)

(use-package graphql-mode
  :ensure t
  :defer t)

(with-eval-after-load 'reformatter
  (reformatter-define terraform-format
    :program "terraform"
    :args '("fmt" "-")
    :group 'terraform))

(use-package terraform-mode
  :ensure t
  :defer t
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Terraform Commands"))
   ("Format"
    (("f" terraform-format-buffer)))))

(defun my/k8s-apply ()
  "Apply current yaml file to the current kubernetes context."
  (interactive)
  (let ((default-directory (file-name-directory buffer-file-name)))
    (compile (format "kubectl apply -f %s" buffer-file-name))))

(defun my/k8s-delete ()
  "Delete current yaml file to the current kubernetes context."
  (interactive)
  (let ((default-directory (file-name-directory buffer-file-name)))
    (compile (format "kubectl delete -f %s" buffer-file-name))))

(use-package yaml-ts-mode
  :ensure nil
  :mode "\\.ya?ml\\'"
  :hook (yaml-ts-mode . flymake-mode)
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " YAML Commands"))
   ("Format"
    (("f" yaml-pro-format))
    "Kubernetes"
    (("a" my/k8s-apply)
     ("k" my/k8s-delete)))))

(use-package yaml-pro
  :ensure t
  :defer t
  :hook
  (yaml-ts-mode . yaml-pro-ts-mode)
  :config
  ;; this binding conflicts with org indirect mode
  (define-key yaml-pro-ts-mode-map (kbd "C-c '") nil))

(use-package hcl-mode
  :ensure t
  :defer t)

(use-package just-ts-mode
  :ensure t
  :defer t
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Justfile Commands"))
   ("Format"
    (("f" just-ts-format-buffer)))))

(use-package toml-ts-mode
  :ensure nil
  :mode "\\.toml\\'"
  :defer t)

(use-package slint-mode
  :ensure t
  :hook (slint-mode . eglot-ensure)
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Slint Commands"))
   ("Format"
    (("f" eglot-format-buffer)))))

(with-eval-after-load 'reformatter
  (reformatter-define nix-format
    :program "nixpkgs-fmt"
    :group 'nix))

(use-package nix-ts-mode
  :ensure t
  :defer t
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Nix commands"))
   ("Format"
    (("f" nix-format-buffer))))
  :mode "\\.nix\\'")

(use-package d2-mode
  :ensure t
  :defer t
  :mode "\\.d2\\'")

(use-package ob-d2
  :defer t
  :ensure t
  :config
  (with-eval-after-load 'org
    (org-babel-do-load-languages
     'org-babel-load-languages
     '((d2 . t)))))

(use-package mermaid-mode
  :ensure t
  :defer t)

(defun my/preview-mermaid ()
  "Render region inside a webit embebed browser."
  (interactive)
  (unless (region-active-p)
    (user-error "Select a region first"))
  (let* ((path (concat (make-temp-file (temporary-file-directory)) ".html"))
         (mermaid-code (buffer-substring-no-properties (region-beginning) (region-end))))
    (save-excursion
      (with-temp-buffer
        (insert "<body>
  <pre class=\"mermaid\">")
        (insert mermaid-code)
        ;; js script copied from mermaid documentation
        (insert "</pre>
  <script type=\"module\">
    import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
    mermaid.initialize({ startOnLoad: true });
  </script>
</body>")
        (write-file path)))
    (xwidget-webkit-browse-url (format "file://%s" path))))

(defun my/markdown-ts-edit-code-block ()
  "Edit the fenced code block at point in an indirect buffer via `edit-indirect'."
  (interactive)
  (let* ((beg (get-char-property (point) 'markdown-ts-code-beg-marker))
         (end (get-char-property (point) 'markdown-ts-code-end-marker))
         (mode (markdown-ts-code-block-mode-at))
         (indirect-buf (edit-indirect-region (marker-position beg)
                                             (marker-position end)
                                             'display-buffer)))
    (unless (and beg end)
      (user-error "Not in a fenced code block"))
    (with-current-buffer indirect-buf
      (funcall mode))))

(use-package markdown-ts-mode
  :ensure nil
  :mode "\\.md\\'"
  :bind (:map markdown-ts-mode-map
              ("C-c '" . 'my/markdown-ts-edit-code-block))
  :custom
  (markdown-ts-fontify-code-blocks-natively t)
  :config
  (add-hook 'markdown-ts-mode-hook #'(lambda ()
                                       (setq-local fill-column 100)
                                       (auto-fill-mode 1))))

(use-package flymake-markdownlint
  :ensure (:host github :repo "erickgnavar/flymake-markdownlint")
  :hook ((markdown-ts-mode . flymake-markdownlint-load)
         (markdown-ts-mode . flymake-mode)))

(use-package edit-indirect
  :ensure t
  :defer t)

(use-package dockerfile-ts-mode
  :ensure nil
  :defer t
  :mode (("\\Dockerfile\\'" . dockerfile-ts-mode)
         ("\\.dockerignore\\'" . dockerfile-ts-mode)))

(use-package dumb-jump
  :ensure t
  :defer t
  :custom
  (dumb-jump-force-searcher 'rg)
  (dumb-jump-selector 'completing-read))
#+END_SRC

helpful, enhance help functions

#+begin_src emacs-lisp
(use-package helpful
  :ensure t
  :defer t
  :bind (("C-h f" . helpful-callable)
         ("C-h v" . helpful-variable)
         ("C-h k" . helpful-key)))
#+end_src

Use ESC key instead C-g to close and abort

Copied from somewhere

#+BEGIN_SRC emacs-lisp
(defun minibuffer-keyboard-quit ()
  "Abort recursive edit.
  In Delete Selection mode, if the mark is active, just deactivate it;
  then it takes a second \\[keyboard-quit] to abort the minibuffer."
  (interactive)
  (if (and delete-selection-mode transient-mark-mode mark-active)
      (setq deactivate-mark  t)
    (when (get-buffer "*Completions*") (delete-windows-on "*Completions*"))
    (abort-recursive-edit)))

(with-eval-after-load 'evil
  (define-key evil-normal-state-map [escape] 'keyboard-quit)
  (define-key evil-visual-state-map [escape] 'keyboard-quit))

(define-key minibuffer-local-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-ns-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-completion-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-must-match-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-isearch-map [escape] 'minibuffer-keyboard-quit)
(global-set-key [escape] 'evil-exit-emacs-state)
#+END_SRC

websockets client

#+begin_src emacs-lisp
(use-package websocket
  :ensure t
  :defer t)
#+end_src

* LSP

#+begin_src emacs-lisp
(use-package eglot
  :defer t
  :ensure nil
  :bind (:map eglot-mode-map
              ("<M-return>" . 'eglot-code-actions)
              ("C-c C-s" . 'xref-find-references))
  :custom
  (eglot-ignored-server-capabilities '(:inlayHintProvider))
  :config
  ;; only for new(emacs 31 or above) versions of eglot, avoid an error
  ;; where when cursor is at an error it increase its line height a
  ;; little bit
  (setq eglot-code-action-indicator "")
  ;; tailwind LSP support
  (add-to-list 'eglot-server-programs
               '(((heex-ts-mode :language-id "html")
                  (web-mode :language-id "html")) . ("tailwindcss-language-server" "--stdio")))
  ;; elixir LSP server
  (setf (alist-get '(elixir-ts-mode heex-ts-mode) eglot-server-programs) '("expert")))
#+end_src

In case we don't have eglot running we can relay on dumb-jump

#+begin_src emacs-lisp
(defun my/goto-definition-dumb-jump-fallback ()
  "Go to definition using eglot when is active otherwise use dumb-jump."
  (interactive)
  (if (and (featurep 'eglot) (eglot-managed-p))
      (xref-find-definitions (thing-at-point 'symbol))
    (if (member major-mode '(emacs-lisp-mode lisp-interaction-mode))
        (xref-find-definitions (thing-at-point 'symbol))
      (dumb-jump-go))))
#+end_src

Start a ~expert~ process and listen on a random port

#+begin_src emacs-lisp
(defun my/start-elixir-expert-server ()
  "Start expert LSP server."
  (interactive)
  (let* ((port (+ 1024 (random (- 65536 1024))))
         (default-directory (my/project-root))
         (command (format "expert --port %s" port))
         (buffer-name (format "(%s) expert server" (my/project-root))))
    (async-shell-command command (get-buffer-create buffer-name))
    (require 'eglot)
    (setf (alist-get 'elixir-ts-mode eglot-server-programs nil nil #'equal)
          `("localhost" ,port))
    (message "Server running on %d" port)))
#+end_src

* Programming languages

** Shell scripts

Bash formatter using ~shfmt~

#+begin_src emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define sh-format
    :program "shfmt"
    :args '("-i" "2" "-")
    :group 'sh))

(use-package bash-ts-mode
  :ensure nil
  :hook (sh-mode . bash-ts-mode)
  :mode-hydra
  ((:title (concat (nerd-icons--icon-info-for-buffer) " Bash commands"))
   ("Format"
    (("f" sh-format-buffer)))))
#+end_src

** C

=clang-format= is required for this, we can install it with =brew install clang-format=

#+begin_src emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define c-format
    :program "clang-format"))

(use-package c-ts-mode
  :ensure nil
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " C commands"))
   ("Format"
    (("f" c-format-buffer)))))

(use-package c++-ts-mode
  :ensure nil
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " C++ commands"))
   ("Format"
    (("f" c-format-buffer)))))
#+end_src

** Python

For LSP support one of ~ty~, ~pyrefly~ or ~pyright~ is required

Install ~flymake-ruff~

#+begin_src emacs-lisp
(use-package flymake-ruff
  :ensure t)
#+end_src

Generate a ~pyrightconfig.json~ file

#+begin_src emacs-lisp
(defun my/gen-pyright-config-file ()
  "Generate a configuration file for PyRight."
  (interactive)
  (let* ((input-dir (read-directory-name "Virtualenv directory: "))
         (venv-path (file-name-parent-directory input-dir))
         (venv (file-name-base (replace-regexp-in-string "/$" "" input-dir)))
         (data (make-hash-table)))
    (puthash "venvPath" venv-path data)
    (puthash "venv" venv data)
    (with-temp-buffer
      (insert (json-encode data))
      (write-file (file-name-concat venv-path "pyrightconfig.json")))))
#+end_src

#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define python-ruff-format
    :program "ruff"
    :args '("format" "-")
    :group 'python)

  (reformatter-define python-sort-imports
    :program "ruff"
    :args '("check" "--fix" "--select" "I001" "-")
    :group 'python))

(setq python-shell-completion-native-enable nil)

(use-package python-ts-mode
  :ensure nil
  :defer t
  :mode "\\.py\\'"
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Python commands"))
   ("Format"
    (("f" python-ruff-format-buffer)
     ("si" python-sort-imports-region))
    "Testing"
    (("tt" pythontest-test-at-point)
     ("tf" pythontest-test-file)
     ("ta" pythontest-test-all))))
  :hook (python-ts-mode . flymake-ruff-load))
#+END_SRC

Testing package

#+begin_src emacs-lisp
(use-package pythontest
  :ensure t
  :defer t
  :custom
  (pythontest-pytest-command "pytest -s -vv"))
#+end_src

Show a list of the available django commands and run the selected one using a compilation buffer.

#+BEGIN_SRC emacs-lisp
(defun my/run-django-command ()
  "Run a django command."
  (interactive)
  (let* ((python-bin (concat (getenv "VIRTUAL_ENV") "/bin/python"))
         (manage-py-file (concat (my/project-root) "manage.py"))
         (default-directory (my/project-root))
         (raw-help (shell-command-to-string (concat python-bin " " manage-py-file " help")))
         (splited-lines (split-string raw-help "\n"))
         (options (seq-filter #'(lambda (line) (cl-search "    " line)) splited-lines))
         (selection (completing-read "Pick django command: " (mapcar 'string-trim options)))
         (command (concat python-bin " " manage-py-file " " selection)))
    (compile command)))
#+END_SRC

** Erlang

#+begin_src emacs-lisp
(use-package erlang-ts-mode
  :ensure (:host github :repo "sebastiw/erlang-ts-mode")
  :defer t)
#+end_src

** Elixir

*** LSP server building

To have support for LSP we need to compile [[https://github.com/elixir-lang/expert][expert]] and setup the generated release into ~eglot-server-programs~.

Recommended versions(using ~mise~) for compiling ~expert~:

#+begin_src toml :tangle no
[tools]
elixir = "1.17.3-otp-27"
erlang = "27.3.4.2"
zig = "0.14.1"
#+end_src

Install with ~just install~

*** Compilation integration

Add regex to match ~mix test~ execution output and be able to navigate between errors.

#+begin_src emacs-lisp
(with-eval-after-load 'compile
  (push 'mix compilation-error-regexp-alist)
  (push '(mix "^\\ \\ \\ \\ \\ \\([a-zA-Z/_/.]+\\):\\([0-9]+\\)$" 1 2) compilation-error-regexp-alist-alist))
#+end_src

*** Config

#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define elixir-format
    :program "mix"
    :args '("format" "-")
    :group 'elixir))

(defun my/elixir-format-buffer ()
  "Format elixir buffers using eglot when is active otherwise use reformatter function."
  (interactive)
  ;; eglot formatter is preferred because it will use project .formatter.exs file
  ;; regular formatter generated by reformatted will ignore that file
  (if (and (featurep 'eglot) (eglot-managed-p))
      (eglot-format-buffer)
    (elixir-format-buffer)))

(defun my/elixir-format-project ()
  "Format the whole project."
  (interactive)
  (let* ((mix-file (expand-file-name (concat (locate-dominating-file (buffer-file-name) "mix.exs") "mix.exs")))
         (default-directory (file-name-directory mix-file)))
    (compile "mix format --migrate")))

(use-package elixir-ts-mode
  :ensure nil
  :mode (("\\.ex\\'" . elixir-ts-mode)
         ("\\.exs\\'" . elixir-ts-mode)
         ("\\mix.lock\\'" . elixir-ts-mode))
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Elixir Commands"))
   ("Format"
    (("ff" my/elixir-format-buffer)
     ("fa" my/elixir-format-project))
    "Testing"
    (("tt" my/mix-run-test-at-point)
     ("tf" my/mix-run-test-file)
     ("ta" my/mix-run-test-all)))))
#+END_SRC

~heex~ templates support

#+begin_src emacs-lisp
(defun my/heex-wrap-text-in-get-text ()
  "Convert region content into a tag using gettext."
  (interactive)
  (unless (region-active-p)
    (user-error "Must select some text first"))
  (save-excursion
    (let* ((text (buffer-substring-no-properties (region-beginning) (region-end)))
           (new-text (concat "<%= " "gettext(\"" text "\") %>")))
      (kill-region (region-beginning) (region-end))
      (insert new-text))))
#+end_src

#+begin_src emacs-lisp
(use-package heex-ts-mode
  :ensure nil
  :hook (heex-ts-mode . emmet-mode)
  :mode "\\.html.heex\\'"
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Heex Commands"))
   ("Format"
    (("f" my/heex-format-file))
    "Text"
    (("ii" my/phx-insert-hero-icon)
     ("wg" my/heex-wrap-text-in-get-text)))))
#+end_src

Set up hydra commands

#+begin_src emacs-lisp
(defun my/heex-format-file ()
  "Format heex file using mix command."
  (interactive)
  ;; avoid to show *Async-Shell-Command* buffer
  (let* ((display-buffer-alist (list (cons "\\*Async Shell Command\\*.*" (cons #'display-buffer-no-window nil))))
         (mix-file (expand-file-name (concat (locate-dominating-file (buffer-file-name) "mix.exs") "mix.exs")))
         (default-directory (file-name-directory mix-file)))
    (async-shell-command (format "mix format %s" (buffer-file-name)) nil)
    (revert-buffer (current-buffer) :ignore-auto :no-confirm)))
#+end_src

Custom functions to run elixir tests.

=elixir-extra-test-env= can be set up on =.dir-locals.el=

#+BEGIN_SRC emacs-lisp
(defun my/mix-run-test (&optional scope)
  "Run elixir test for the given SCOPE."
  (interactive)
  ;; we need to get absolute path of mix binary because when running
  ;; on nix this path will be different per project, version, etc
  (let* ((mix-binary (executable-find "mix"))
         (current-file (buffer-file-name))
         (current-line (line-number-at-pos))
         (mix-file (expand-file-name (concat (locate-dominating-file (buffer-file-name) "mix.exs") "mix.exs")))
         (default-directory (file-name-directory mix-file))
         (extra-env (if (boundp 'elixir-extra-test-env) elixir-extra-test-env ""))
         (mix-env (concat "MIX_ENV=test " extra-env)))

    (cond
     ((string-equal scope "file")
      (compile (format "%s %s test %s" mix-env mix-binary current-file)))

     ((string-equal scope "at-point")
      (compile (format "%s %s test %s:%s" mix-env mix-binary current-file current-line)))

     (t
      (compile (format "%s %s test" mix-env mix-binary))))))

(defun my/mix-run-test-file ()
  "Run mix test over the current file."
  (interactive)
  (my/mix-run-test "file"))

(defun my/mix-run-test-at-point ()
  "Run mix test at point."
  (interactive)
  (my/mix-run-test "at-point"))

(defun my/mix-run-test-all ()
  "Run mix test at point."
  (interactive)
  (my/mix-run-test))
#+END_SRC

#+begin_src emacs-lisp
(defun my/phx-insert-hero-icon ()
  "Insert icon component in a Elixir Phoenix application."
  (interactive)
  (let* ((base-dir (expand-file-name (locate-dominating-file (buffer-file-name) "mix.exs")))
         (icons-dir (format "%s/deps/heroicons/optimized/20/solid/" base-dir))
         (option (completing-read "Select icon: " (directory-files icons-dir nil "svg")))
         (no-extension (string-replace ".svg" "" (string-trim option) )))
    (insert (format "<.icon name=\"hero-%s\" />" no-extension))))
#+end_src

** LFE

#+BEGIN_SRC emacs-lisp
(use-package lfe-mode
  :ensure t
  :if (executable-find "lfe")
  :bind (:map lfe-mode-map
              ("C-c C-c" . lfe-eval-buffer))
  :init
  (defun lfe-eval-buffer ()
    "Send current buffer to inferior LFE process."
    (interactive)
    (if (eq (get-buffer-window "*inferior-lfe*") nil)
        (run-lfe nil))
    (lfe-eval-region (point-min) (point-max) nil)))
#+END_SRC

** Elm

Install Elm

#+BEGIN_SRC sh
npm -g install elm elm-format elm-oracle
#+END_SRC

#+BEGIN_SRC emacs-lisp

(use-package elm-mode
  :ensure t
  :if (executable-find "elm")
  :bind (:map elm-mode-map
              ("C-c C-d" . elm-oracle-doc-at-point))
  :config
  (add-hook 'elm-mode-hook #'elm-oracle-setup-completion)
  (add-to-list 'company-backends 'company-elm))

#+END_SRC

** Haskell

Install haskell binaries =hlint= and =hindent= and make sure =~/.local/bin/= is loaded in =PATH=.

#+BEGIN_SRC shell
stack install hlint
stack install hindent
#+END_SRC

#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define haskell-format
    :program "hindent"
    :group 'haskell))

(use-package haskell-mode
  :ensure t
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Haskell Commands"))
   ("Format"
    (("f" haskell-format-buffer))
    "Loading"
    (("l" haskell-process-load-file)))))

(defun my/run-hlint ()
  "Run  hlint over the current project."
  (interactive)
  (let ((default-directory (my/project-root)))
    (compile "hlint .")))

(defun my/run-hlint-buffer ()
  "Run  hlint over the current buffer."
  (interactive)
  (let* ((current-file (buffer-file-name))
         (default-directory (my/project-root)))
    (compile (concat "hlint " current-file))))
#+END_SRC

** Lua

#+BEGIN_SRC emacs-lisp
(use-package lua-ts-mode
  :ensure nil
  :defer t
  :mode "\\.lua\\'"
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Lua Commands"))
   ("Format"
    (("f" lua-format-buffer))
    "Compilation"
    (("b" compile)))))
#+END_SRC

Define formatter using [[https://github.com/JohnnyMorganz/StyLua][StyLua]]

#+begin_src emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define lua-format
    :program "stylua"
    :args '("-")
    :group 'lua))
#+end_src

** Java

#+begin_src emacs-lisp
(use-package java-ts-mode
  :ensure nil
  :defer t
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Java commands"))
   ("Format"
    (("f" eglot-format-buffer))
    "Testing"
    (("t" eglot-java-run-test))))
  :config
  (add-hook 'java-ts-mode-hook #'(lambda ()
                                   (setq-local tab-width 4))))
#+end_src

#+begin_src emacs-lisp
(use-package eglot-java
  :ensure t
  :defer t
  :hook (java-ts-mode . eglot-java-mode))
#+end_src

** Javascript

Linter using [[https://biomejs.dev][biome]]

#+begin_src emacs-lisp
(use-package flymake-biome
  :ensure t)
#+end_src

#+begin_src emacs-lisp
(use-package js-ts-mode
  :ensure nil
  :defer t
  :mode "\\.m?jsx?\\'"
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Javascript Commands"))
   ("Format"
    (("f" js-format-buffer))))
  :hook (js-ts-mode . flymake-biome-load))
#+end_src

*** Formattter

We need to use ~--stdin-file-path a.js~ to tell ~biome~ to use ~stdin~.

#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define js-format
    :program "biome"
    :args '("format" "--stdin-file-path" "a.js" "--javascript-formatter-indent-style" "space")))

(with-eval-after-load 'reformatter
  (reformatter-define ts-format
    :program "biome"
    :args '("format" "--stdin-file-path" "a.ts" "--indent-style" "space")))
#+END_SRC

** Typescript

#+begin_src emacs-lisp
(use-package typescript-ts-mode
  :ensure nil
  :defer t
  :mode "\\.tsx?\\'"
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Typescript Commands"))
   ("Format"
    (("f" ts-format-buffer))))
  :hook (typescript-ts-mode . flymake-biome-load))
#+end_src

** Svelte

#+begin_src emacs-lisp
(use-package svelte-ts-mode
  :ensure (:host github :repo "leafOfTree/svelte-ts-mode")
  :config
  (add-to-list 'treesit-language-source-alist
               '(svelte "https://github.com/tree-sitter-grammars/tree-sitter-svelte")))
#+end_src

after installation run ~M-x treesit-install-language-grammer~ to compile ~svelte~ grammar

** Swift

#+begin_src emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define swift-format
    :program "swift-format"
    :args '("/dev/stdin")))

(use-package swift-mode
  :ensure t
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Swift Commands"))
   ("Format"
    (("f" swift-format-buffer)))))
#+end_src

** Zig

#+begin_src emacs-lisp
(use-package zig-mode
  :ensure t
  :defer t
  :custom
  (zig-format-on-save nil))
#+end_src

** Odin

To compile grammar we need to run ~M-x treesit-install-language-grammar~

#+begin_src emacs-lisp
(defun my/run-odin ()
  "Run odin program."
  (interactive)
  (compile "odin run ."))

(use-package odin-ts-mode
  :ensure (:host github :repo "Sampie159/odin-ts-mode")
  :mode "\\.odin\\'"
  :init
  (add-to-list 'treesit-language-source-alist '(odin "https://github.com/tree-sitter-grammars/tree-sitter-odin"))

  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Odin Commands"))
   ("Format"
    (("f" eglot-format-buffer))
    "Run"
    (("r" my/run-odin))))
  :config
  ;; by default tab width is 8, that's too much space so we define 4
  ;; only for odin buffers
  (add-hook 'odin-ts-mode-hook #'(lambda ()
                                   (setq-local tab-width 4))))
#+end_src

** Rust

Install rust analyzer, this should be installed when ~rustup-init~ is executed but in case is not we can execute:

#+BEGIN_SRC sh
rustup component add rust-analyzer
#+END_SRC

Install rust source code, it is required by ~rust-analyzer~, in case it's not installed automatically

#+begin_src shell
rustup component add rust-src
#+end_src

#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define rust-format
    :program "rustfmt"))

(defun my/rust-format-buffer ()
  "Format rust buffer with LSP or fallback with rust fmt."
  (interactive)
  (if (and (featurep 'eglot) (eglot-managed-p))
      (eglot-format-buffer)
    (rust-format-buffer)))

(defun my/rust-cargo-build ()
  "Build current project."
  (interactive)
  (let ((default-directory (my/project-root)))
    (compile "cargo build")))

(defun my/rust-cargo-run ()
  "Run current project."
  (interactive)
  (let ((default-directory (my/project-root)))
    (compile "cargo run")))

(defun my/rust-cargo-test-at-point ()
  "Run the test function at or above point."
  (interactive)
  (let* ((default-directory (locate-dominating-file (buffer-file-name) "Cargo.toml"))
         (test-name (save-excursion
                      (re-search-backward "#\\[test\\]" nil t)
                      (forward-line 1)
                      (when (re-search-forward "fn\\s-+\\([a-zA-Z0-9_]+\\)" (line-end-position) t)
                        (match-string-no-properties 1)))))
    (if test-name
        (compile (format "cargo test %s" test-name))
      (user-error "No #[test] found above point"))))

(defun my/rust-cargo-test-all ()
  "Run current project."
  (interactive)
  (let ((default-directory (locate-dominating-file (buffer-file-name) "Cargo.toml")))
    (compile "cargo test")))

(use-package rust-ts-mode
  :ensure nil
  :mode "\\.rs\\'"
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Rust Commands"))
   ("Format"
    (("f" my/rust-format-buffer))
    "Cargo"
    (("b" my/rust-cargo-build)
     ("r" my/rust-cargo-run))
    "Testing"
    (("ta" my/rust-cargo-test-all)
     ("tt" my/rust-cargo-test-at-point)))))
#+END_SRC

#+begin_src emacs-lisp
(defun my/rust-run-file ()
  "Compile and rust current file."
  (interactive)
  (unless (buffer-file-name)
    (user-error "Save file before"))
  (let* ((path (buffer-file-name))
         (default-directory (file-name-directory path))
         (filename (buffer-name))
         (command (format "rustc %s && ./%s" filename (string-replace ".rs" "" filename))))
    (compile command)))
#+end_src

** Golang

Install dependencies: goimports

#+BEGIN_SRC sh
go install golang.org/x/tools/cmd/goimports@latest
#+END_SRC

Install ~gopls~ to have LSP support using eglot

#+begin_src shell
brew install gopls
#+end_src

#+begin_src emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define go-format
    :program "goimports"
    :group 'go-ts-mode))
#+end_src

#+BEGIN_SRC emacs-lisp
(use-package go-ts-mode
  :ensure nil
  :hook (go-ts-mode . eglot-ensure)
  :mode (("\\.go\\'" . go-ts-mode))
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Go Commands"))
   ("Format"
    (("f" go-format-buffer))
    "Execute"
    (("r" go-run))
    "Testing"
    (("tt" go-ts-mode-test-function-at-point)
     ("tf" go-ts-mode-test-this-file)
     ("ta" go-ts-mode-test-this-package))))
  :config
  ;; by default tab width is 8, that's too much space so we define 4
  ;; only for go buffers
  (add-hook 'go-ts-mode-hook #'(lambda ()
                                 (setq-local tab-width 4))))
#+END_SRC

** Common lisp

#+BEGIN_SRC emacs-lisp
(defconst inferior-lisp-program (executable-find "sbcl"))

(use-package sly
  :ensure t
  :defer t)
#+END_SRC

** Clojure

#+BEGIN_SRC emacs-lisp
(defun my/clj-format-code ()
  "Format clojure code using cider commands."
  (interactive)
  (if (region-active-p)
      (cider-format-region (region-beginning) (region-end))
    (cider-format-buffer)))

(defun my/cider-repl-reset ()
  "Call (reset) in the active repl and return to the position where was called."
  (interactive)
  (save-window-excursion
    (cider-insert-in-repl "(reset)" t)))

(use-package cider
  :ensure t
  :bind (:map cider-mode-map
              ("C-c C-f" . my/clj-format-code)
              ("C-c C-r" . my/cider-repl-reset)))

(with-eval-after-load 'evil
  (evil-set-initial-state 'cider-stacktrace-mode 'emacs))
#+END_SRC

#+begin_src emacs-lisp
(use-package clj-refactor
  :ensure t
  :after cider
  :bind (:map clojure-mode-map
              ("C-c C-a" . cljr-add-project-dependency))
  :hook (clojure . clj-refactor))
#+end_src

** Emacs lisp

Disable indentation with tabs for =emacs-lisp-mode=

#+begin_src emacs-lisp
(defun my/emacs-lisp-hook-setup ()
  (setq indent-tabs-mode nil))

(add-hook 'emacs-lisp-mode-hook 'my/emacs-lisp-hook-setup)
#+end_src

Enable flymake

#+begin_src emacs-lisp
(add-hook 'emacs-lisp-mode-hook #'(lambda () (flymake-mode 1)))
#+end_src

~package-lint~, used for packages development

#+begin_src emacs-lisp
(use-package package-lint
  :ensure t
  :defer t)
#+end_src

** Dart

#+BEGIN_SRC emacs-lisp
(with-eval-after-load 'reformatter
  (reformatter-define dart-format
    :program "dart"
    :args '("format")
    :group 'dart))

(defun my/dart-run-file ()
  "Execute the code of the current file."
  (interactive)
  (compile (format "dart %s" (buffer-file-name))))

(use-package dart-mode
  :ensure t
  :if (or (executable-find "dart") (executable-find "flutter"))
  :mode-hydra
  ((:title (concat (nerd-icons-icon-for-buffer) " Dart Commands"))
   ("Format"
    (("f" dart-format-buffer))
    "Execution"
    (("r" my/dart-run-file)))))
#+END_SRC

*** Flutter

#+begin_src emacs-lisp
(defun my/flutter-goto-logs-buffer()
  "Go to buffer logs buffer."
  (interactive)
  (let ((buffer (get-buffer flutter-buffer-name)))
    (unless buffer
      (user-error "flutter is not running."))
    (switch-to-buffer buffer)
    (goto-line (point-max))))

(use-package flutter
  :ensure t
  :after dart-mode
  :bind (:map dart-mode-map
              ("C-c C-r" . #'flutter-run-or-hot-reload)
              ("C-c C-l" . #'my/flutter-goto-logs-buffer))
  :hook (dart-mode . flutter-test-mode)
  :custom
  ;; sdk path will be the parent-parent directory of flutter cli
  (flutter-sdk-path (directory-file-name
                     (file-name-directory
                      (directory-file-name
                       (file-name-directory (executable-find "flutter")))))))
#+end_src

* Writing

Custom functions to speed up writing process

** Hugo

Insert =org-link= image using clipboard value, if the current file is =blog/demo.org= it will place the resulting image into =static/images/blog/demo/image.png=.

#+begin_src emacs-lisp
(defun my/hugo-insert-image-from-clipboard ()
  "Use clipoard image and put it in a generated images folder for the current file."
  (interactive)
  (let* ((absolute-path (buffer-file-name))
         (splitted (reverse (split-string absolute-path "/")))
         (filename (replace-regexp-in-string ".org" "" (car splitted)))
         (dir (nth 1 splitted))
         (base-image-path (concat (my/project-root) "static/images"))
         (result-image-dir (format "%s/%s/%s" base-image-path dir filename))
         (result-image-name (read-string "Filename: " "image.png"))
         (full-path-result-image (format "%s/%s" result-image-dir result-image-name)))

    (shell-command (format "mkdir -p %s" result-image-dir))
    (shell-command (format "pngpaste %s" full-path-result-image))
    (insert (format "[[file:%s]]" (car (cdr (split-string full-path-result-image "static")))))))
#+end_src

Insert docsy link

#+begin_src emacs-lisp
(defun my/docsy-insert-ref ()
  "Insert a link using docsy ref helper."
  (interactive)
  (let* ((filename (read-file-name "Select file: " (my/project-root)))
         (prefix-to-remove (concat (my/project-root) "content/"))
         (relative-path (string-replace prefix-to-remove "" filename)))
    (insert (format "{{< ref \"%s\" >}}" relative-path))))
#+end_src

* Custom functions

Simple bookmarks management

#+begin_src emacs-lisp
(defvar my/bookmarks (make-hash-table :test 'equal))

(defun my/bookmark-switch ()
  "Switch to selected bookmark."
  (interactive)
  (let* ((key (my/project-name))
         (items (gethash key my/bookmarks (make-hash-table :test 'equal)))
         (options (hash-table-keys items))
         (selected (completing-read "Pick buffer: " options))
         (selected-buffer (gethash selected items)))
    (when selected-buffer

      (switch-to-buffer selected-buffer))))

(defun my/add-bookmark ()
  "Add current buffer to bookmark list."
  (interactive)
  (let* ((project-key (my/project-name))
         (buffer-key (buffer-name))
         (items (gethash project-key my/bookmarks (make-hash-table :test 'equal))))
    (puthash buffer-key (current-buffer) items)
    (puthash project-key items my/bookmarks)
    (message "%s added to bookmarks" buffer-key)))
#+end_src

Manage window configurations, allows to save a "snapshot" of the current windows configuration. Also allows to restore a saved "snapshot".

#+BEGIN_SRC emacs-lisp
(defvar my/window-snapshots '())

(defun my/save-window-snapshot ()
  "Save the current window configuration into `window-snapshots` alist."
  (interactive)
  (let ((key (read-string "Enter a name for the snapshot: ")))
    (setf (alist-get key my/window-snapshots) (current-window-configuration))
    (message "%s window snapshot saved!" key)))

(defun my/get-window-snapshot (key)
  "Given a KEY return the saved value in `window-snapshots` alist."
  (let ((value (assoc key my/window-snapshots)))
    (cdr value)))

(defun my/restore-window-snapshot ()
  "Restore a window snapshot from the window-snapshots alist."
  (interactive)
  (let* ((snapshot-name (completing-read "Choose snapshot: " (mapcar #'car my/window-snapshots)))
         (snapshot (my/get-window-snapshot snapshot-name)))
    (if snapshot
        (set-window-configuration snapshot)
      (message "Snapshot %s not found" snapshot-name))))
#+END_SRC

Manipulate frame font height.

#+BEGIN_SRC emacs-lisp
(defun my/change-font-height (delta)
  "Use DELTA to increase/decrease the frame font height."
  (let* ((current-height (face-attribute 'default :height))
         (new-height (+ current-height delta)))
    (set-face-attribute 'default nil :height new-height)))

(defun my/decrease-font-height ()
  "Decrease font height by 10."
  (interactive)
  (my/change-font-height -10))

(defun my/increase-font-height ()
  "Increase font height by 10."
  (interactive)
  (my/change-font-height +10))
#+END_SRC

#+BEGIN_SRC emacs-lisp
(defun my/find-file-in-project ()
  "Custom find file function."
  (interactive)
  (if (my/project-p)
      (project-find-file)
    (call-interactively #'find-file)))

(defun my/find-file-in-project-local ()
  "Find inside a relative path."
  (interactive)
  ;; mix.exs for elixir monorepos and .env for terraform repositories
  ;; with many levels
  (when-let* ((base-dir (or (locate-dominating-file (buffer-file-name) "mix.exs")
                            (locate-dominating-file (buffer-file-name) ".env"))))
    (project-find-file-in "" (list base-dir) (project-current))))

(defun my/fold-buffer-when-is-too-big (max-lines)
  "Fold buffer is max lines if grater than as MAX-LINES."
  (if (> (count-lines (point-min) (point-max)) max-lines)
      (hs-hide-all)))

;; used to store all the configuration per perspective
(defvar my/toggle-window-configurations (make-hash-table))

(defun my/toggle-maximize ()
  "Toggle maximization of current window."
  (interactive)
  (let* ((key (persp-name (persp-curr)))
         (value (gethash key my/toggle-window-configurations)))
    (if (eq value nil)
        (progn
          (puthash key (current-window-configuration) my/toggle-window-configurations)
          (delete-other-windows))
      (progn
        (set-window-configuration value)
        (puthash key nil my/toggle-window-configurations)))))

(defun my/config-file ()
  "Open config file."
  (interactive)
  (find-file (expand-file-name (concat user-emacs-directory "bootstrap.org"))))

(defun my/toggle-spanish-characters ()
  "Enable/disable alt key to allow insert spanish characters."
  (interactive)
  (if (eq ns-alternate-modifier 'meta)
      (setq ns-alternate-modifier nil)
    (setq ns-alternate-modifier 'meta)))

(defun my/change-font-size()
  "Change frame font size."
  (interactive)
  (let* ((size (read-number "New size: ")))
    (set-face-attribute 'default nil :font (format "%s %d" my/font-family size))))
#+END_SRC

Function to extract clocks from org buffer and filter them by month

#+BEGIN_SRC emacs-lisp
(defun my/collect-clocks ()
  "Collect all the clocks of current buffer."
  (org-element-map (org-element-parse-buffer) 'clock #'(lambda (clock) clock)))

(defun my/filter-clocks-by-month (clocks month)
  "Filter CLOCKS using MONTH value."
  (seq-filter #'(lambda (clock)
                  (eq (org-element-property :month-end (org-element-property :value clock)) month)) clocks))

(defun my/org-filter-clocks-report ()
  "Create a buffer with the tasks filtered by month."
  (interactive)
  (let* ((month (read-number "Insert month: "))
         (clocks (my/collect-clocks))
         (filtered-clocks (my/filter-clocks-by-month clocks month))
         (buffer (get-buffer-create "*clocks report*")))
    (switch-to-buffer buffer)
    (org-mode)
    (insert "* Report\n")
    (seq-map #'(lambda (clock)
                 (insert (format "CLOCK: %s\n" (org-element-property :raw-value (org-element-property :value clock))))) filtered-clocks)
    (org-clock-display)))
#+END_SRC

Copy absolute and relative path to clipboard

#+BEGIN_SRC emacs-lisp
(defun my/copy-abs-path ()
  "Copy absolute path of the buffer to clipboard"
  (interactive)
  (if buffer-file-name
      (progn
        (kill-new buffer-file-name)
        (message (format "%s copied to clipboard" buffer-file-name)))
    (message "File not saved yet")))

(defun my/copy-relative-path ()
  "Copy relative path of the buffer to clipboard"
  (interactive)
  (if (and (my/project-p) buffer-file-name)
      (let ((path (file-relative-name buffer-file-name (my/project-root))))
        (kill-new path)
        (message (format "%s copied to clipboard" path)))
    (message "File not saved yet or not inside project")))
#+END_SRC

Create a temp file with the current buffer content and render it with =eww=.

#+BEGIN_SRC emacs-lisp
(defun my/preview-buffer-in-eww ()
  "Preview buffer content in EWW."
  (interactive)
  (let* ((temp-file (make-temp-name (temporary-file-directory)))
         (path (concat temp-file ".html")))
    (write-file path)
    (kill-buffer)
    (eww-open-file path)))

(defun my/preview-buffer-in-xwidget-browser ()
  "Preview buffer content in EWW."
  (interactive)
  (let* ((temp-file (make-temp-name (temporary-file-directory)))
         (path (concat temp-file ".html")))
    (write-file path)
    (kill-buffer)
    (xwidget-webkit-browse-url (format "file://%s" path))))
#+END_SRC

Resize window: allow create a "resize mode" and use hjkl to increase/decrease width/height of the current window

#+BEGIN_SRC emacs-lisp
(defun my/resize-window ()
  "Resize window using j k h l keys."
  (interactive)
  (let ((keys-map '((?h . evil-window-decrease-width)
                    (?j . evil-window-decrease-height)
                    (?k . evil-window-increase-height)
                    (?l . evil-window-increase-width)))
        (overlay (make-overlay (point-min) (point-max) (window-buffer))))
    (let ((is-reading t))
      (overlay-put overlay 'face '((t (:foreground "gray40"))))
      (while is-reading
        (let ((action (alist-get (read-key) keys-map)))
          (if action
              (apply action '(1))
            (setq is-reading nil)
            (delete-overlay overlay)))))))
#+END_SRC

Kill the current buffer and delete the related file

#+BEGIN_SRC emacs-lisp
(defun my/delete-close-file ()
  "Delete the current file and kill its buffer."
  (interactive)
  (when buffer-file-name
    (delete-file buffer-file-name)
    (kill-buffer)))
#+END_SRC

Copy json text from clipboard in a new buffer and format it

#+begin_src emacs-lisp
(defun my/copy-and-format-json-from-clipboard ()
  "Copy content from clipboard and format it in a new buffer."
  (interactive)
  (let ((buffer (generate-new-buffer "tmp.json")))
    (with-current-buffer buffer
      (yank)
      (json-ts-mode)
      (json-format-region (point-min) (point-max)))
    (set-window-buffer nil buffer)))
#+end_src

** MacOS

Functions to open Finder using current file or current project.

#+BEGIN_SRC emacs-lisp
(defun my/open-finder-at (path)
  "Open Finder app with the given PATH."
  (let* ((finder (executable-find "open"))
	 (command (format "%s %s" finder path)))
    (shell-command command)))

(defun my/open-project-in-finder ()
  "Open current project in Finder app."
  (interactive)
  (if (my/project-p)
      (my/open-finder-at (my/project-root))
    (message "There is no active project.")))

(defun my/open-current-file-in-finder ()
  "Open current file in Finder."
  (interactive)
  (let ((file (buffer-file-name)))
    (if file
        (my/open-finder-at (file-name-directory file))
      (message "Buffer has not been saved yet!"))))
#+END_SRC

Open current file with an macOS app. Installed macOS apps will be listed using ~completing-read~

#+BEGIN_SRC emacs-lisp
(defun my/macos-open-file-with ()
  "Open current file with and macOS installed app."
  (interactive)
  (let* ((apps-list (directory-files "/Applications" nil "\\.app$"))
         (selected-app (completing-read "Choose an application: " apps-list)))
    (shell-command (format "open %s -a '%s'" (buffer-file-name) selected-app))))
#+END_SRC

Open the current file with macOS =open= command. This will open the file with the default app configured for the type of file.

#+BEGIN_SRC emacs-lisp
(defun my/macos-open-current-file ()
  (interactive)
  (shell-command (format "open %s" (buffer-file-name))))
#+END_SRC

Save image from clipboard to path.

#+begin_src emacs-lisp
(defun my/save-image-from-clipboard ()
  "Save image from clipboard to the given path."
  (interactive)
  (unless (executable-find "pngpaste")
    (user-error "Install pngpaste to continue"))
  (let* ((path (read-file-name ""))
         (command (format "pngpaste %s" path)))
    (shell-command command)
    (kill-new path)))
#+end_src

af

.emacs.d/snippets/dart-mode/af · 5 lines

# -*- mode: snippet -*-
# name: Arrow function
# key: af
# --
($1) => $2

fu

.emacs.d/snippets/dart-mode/fu · 7 lines

# -*- mode: snippet -*-
# name: Inline function
# key: fu
# --
($1) {
  $2
}

iclass

.emacs.d/snippets/dart-mode/iclass · 7 lines

# -*- mode: snippet -*-
# name: Inherit class
# key: icls
# --
class $1 extends $2 {
  $0
}

print

.emacs.d/snippets/dart-mode/print · 5 lines

# -*- mode: snippet -*-
# name: print
# key: p
# --
print($1);$2

stanim

.emacs.d/snippets/dart-mode/stanim · 33 lines

# -*- mode: snippet -*-
# name: Flutter widget with AnimationController
# key: stanim
# group: flutter
# --
class $1 extends StatefulWidget {
  @override
  $1State createState() => $1State();
}

class $1State extends State<$1>
with SingleTickerProviderStateMixin {
  AnimationController _controller;
  
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this);
  }
  
  @override
  void dispose() {
    super.dispose();
    _controller.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Container(
      $2
    );
  }
}

stful

.emacs.d/snippets/dart-mode/stful · 18 lines

# -*- mode: snippet -*-
# name: Flutter stateful widget
# key: stful
# group: flutter
# --
class $1 extends StatefulWidget {
  @override
  _$1State createState() => _$1State();
}

class _$1State extends State<$1> {
  @override
  Widget build(BuildContext context) {
    return Container(
      $2
    );
  }
}

stless

.emacs.d/snippets/dart-mode/stless · 13 lines

# -*- mode: snippet -*-
# name: Flutter stateless widget
# key: stless
# group: flutter
# --
class $1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
    $2
  );
  }
}

inspect-ecto-query

.emacs.d/snippets/elixir-mode/inspect-ecto-query · 5 lines

# -*- mode: snippet -*-
# name: ecto inspect query
# key: iq
# --
IO.inspect(Ecto.Adapters.SQL.to_sql(:all, ${1:Repo}, ${0:query}))

mixtask

.emacs.d/snippets/elixir-mode/mixtask · 14 lines

# -*- mode: snippet -*-
# name: mix task
# key: mixtask
# --
defmodule Mix.Tasks.$1 do
  @moduledoc false
  use Mix.Task

  def run(_) do
    Application.ensure_all_started(:$2)
    IO.puts("runnning...")
    $0
  end
end

pipe-inspect

.emacs.d/snippets/elixir-mode/pipe-inspect · 5 lines

# -*- mode: snippet -*-
# name: Pipe inspect
# key: pi
# --
|> IO.inspect(label: "$1")$0

if-error

.emacs.d/snippets/go-mode/if-error · 7 lines

# -*- mode: snippet -*-
# name: if error
# key: ie
# --
if err != nil {
   $0
}

inspect

.emacs.d/snippets/go-mode/inspect · 5 lines

# -*- mode: snippet -*-
# name: inspect
# key: i
# --
fmt.Printf("Type %T for %[1]v\n", $1)$0

print

.emacs.d/snippets/go-mode/print · 5 lines

# -*- mode: snippet -*-
# name: print
# key: p
# --
fmt.Println($0)

inspect

.emacs.d/snippets/js-mode/inspect · 5 lines

# -*- mode: snippet -*-
# name: inspect variable
# key: i
# --
console.log("$1", $1)$0

inspect

.emacs.d/snippets/python-mode/inspect · 5 lines

# -*- mode: snippet -*-
# name: inspect variable
# key: i
# --
print("$1", $1)$0

logger

.emacs.d/snippets/python-mode/logger · 19 lines

# -*- mode: snippet -*-
# name: logger
# key: logger
# --

import logging
import sys


logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

logger.addHandler(handler)
$0

inspect

.emacs.d/snippets/rust-mode/inspect · 5 lines

# -*- mode: snippet -*-
# name: inspect value
# key: i
# --
println!("{:?}", $1);$0

Espanso

default.yml

espanso/config/default.yml · 2 lines

search_shortcut: off
show_icon: false

base.yml

espanso/match/base.yml · 62 lines

---
# espanso match file

# For a complete introduction, visit the official docs at: https://espanso.org/docs/

# You can use this file to define the base matches (aka snippets)
# that will be available in every application when using espanso.

# Matches are substitution rules: when you type the "trigger" string
# it gets replaced by the "replace" string.
matches:
  - trigger: "::date"
    replace: "{{date}}"
    vars:
      - name: date
        type: date
        params:
          format: "%m/%d/%Y"

  - trigger: "::isodate"
    replace: "{{date}}"
    vars:
      - name: date
        type: date
        params:
          format: "%Y-%m-%d"

  - trigger: "::time"
    replace: "{{time}}"
    vars:
      - name: time
        type: date
        params:
          format: "%X"

  - trigger: "::fliptable"
    replace: "(ノ°Д°)ノ︵ ┻━┻"

  - trigger: "::well"
    replace: "¯\\_(ツ)_/¯"

  - trigger: "::ip"
    replace: "{{ip}}"
    vars:
      - name: ip
        type: shell
        params:
          cmd: "curl ifconfig.me"

  - trigger: "::arigato"
    replace: "ありがとう"

  - trigger: "::kudasai"
    replace: "ください"

  - trigger: "::lorem"
    replace: "{{lorem}}"
    vars:
      - name: lorem
        type: shell
        params:
          cmd: "lorem -n 50"

Eww

eww.scss

eww/eww.scss · 210 lines

.background {
  background-color: transparent;
}

window {
  background-color: transparent;
}

.calendar-panel {
  background-color: rgba(15, 15, 15, 0.45);
  padding: 12px 14px;
  border-radius: 8px;
}

.desktop-calendar,
.desktop-calendar header,
.desktop-calendar button,
.desktop-calendar label,
.desktop-calendar grid {
  background-color: transparent;
  background-image: none;
  color: #eee;
  border: none;
  box-shadow: none;
}

.desktop-calendar button:hover {
  background-color: rgba(255, 255, 255, 0.1);
}

.desktop-calendar:selected,
.desktop-calendar label:selected,
.desktop-calendar .today {
  background-color: #eee;
  color: #0f0f0f;
  border-radius: 4px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.6);
}

.weather-panel {
  background-color: rgba(15, 15, 15, 0.45);
  padding: 12px 14px;
  border-radius: 8px;
}

.weather-glyph {
  font-size: 32px;
  color: #eee;
}

.weather-temp {
  font-size: 20px;
  font-weight: 500;
  color: #eee;
  text-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
}

.weather-desc {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.6);
}

.weather-sub {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.5);
}

.weather-city {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.6);
  letter-spacing: 0.5px;
}


.net-panel {
  background-color: rgba(15, 15, 15, 0.45);
  padding: 12px 14px;
  border-radius: 8px;
}

.net-header {
  color: rgba(255, 255, 255, 0.5);
  font-size: 11px;
  letter-spacing: 1px;
}

.net-arrow {
  color: rgba(255, 255, 255, 0.6);
  font-size: 13px;
}

.net-value {
  color: #eee;
  font-size: 13px;
  font-weight: 500;
  text-shadow: 0 0 4px rgba(0, 0, 0, 0.8);
}

.net-usage-row {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.5);
  margin-top: 2px;
}

.ram-panel {
  background-color: rgba(15, 15, 15, 0.45);
  padding: 12px 14px;
  border-radius: 8px;
}

.ram-header {
  color: rgba(255, 255, 255, 0.5);
  font-size: 11px;
  letter-spacing: 1px;
}

.usage-gauge {
  color: #eee;
  background-color: rgba(255, 255, 255, 0.15);
}

.usage-gauge-label {
  min-width: 56px;
  min-height: 56px;
  background-color: #161616;
  border-radius: 50%;
  color: #eee;
  font-size: 20px;
  font-weight: 500;
}

.usage-gauge.critical,
.usage-gauge.critical .usage-gauge-label {
  color: #f53c3c;
}

.ram-sub {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.5);
  margin-top: 2px;
}

.cpu-panel,
.gpu-panel {
  background-color: rgba(15, 15, 15, 0.45);
  padding: 12px 14px;
  border-radius: 8px;
}

.cpu-header,
.gpu-header {
  color: rgba(255, 255, 255, 0.5);
  font-size: 11px;
  letter-spacing: 1px;
}

.cpu-sub,
.gpu-sub {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.5);
  margin-top: 2px;
}

.disk-panel {
  background-color: rgba(15, 15, 15, 0.45);
  padding: 12px 14px;
  border-radius: 8px;
}

.disk-header {
  color: rgba(255, 255, 255, 0.5);
  font-size: 11px;
  letter-spacing: 1px;
}

.disk-bar {
  min-height: 8px;
  margin: 6px 0;
}

.disk-bar trough {
  min-height: 8px;
  background-color: rgba(255, 255, 255, 0.15);
  border-radius: 4px;
}

.disk-bar trough progress {
  background-color: #eee;
  border-radius: 4px;
}

.disk-bar.critical trough progress {
  background-color: #f53c3c;
}

.disk-percent {
  min-width: 30px;
  color: #eee;
  font-size: 11px;
}

.disk-percent.critical {
  color: #f53c3c;
}

.disk-sub {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.5);
  margin-top: 2px;
}

eww.yuck

eww/eww.yuck · 148 lines

(defwidget calendar-panel []
  (box :class "calendar-panel"
    (calendar :class "desktop-calendar"
              :show-heading true
              :show-day-names true
              :show-week-numbers false)))

(defwindow calendar-window
  :monitor 0
  :windowtype "dock"
  :stacking "bottom"
  :exclusive false
  :focusable false
  :geometry (geometry
              :x "24px"
              :y "24px"
              :width "280px"
              :height "220px"
              :anchor "top left")
  (calendar-panel))

(defpoll weather-data
  :interval "15m"
  :initial "{\"icon\": \"\", \"temp\": \"--\", \"feels\": \"--\", \"desc\": \"loading\", \"humidity\": \"--\", \"wind\": \"--\", \"city\": \"--\"}"
  `scripts/weather.sh`)

(defwidget weather-panel []
  (box :class "weather-panel" :orientation "v" :space-evenly false :spacing 4 :height 102
    (label :class "weather-city" :text {weather-data.city} :halign "start")
    (box :orientation "h" :space-evenly false :spacing 10
      (label :class "weather-glyph" :text {weather-data.icon})
      (box :orientation "v" :space-evenly false
        (label :class "weather-temp" :text "${weather-data.temp}°" :halign "start")
        (label :class "weather-desc" :text {weather-data.desc} :halign "start")))
    (box :class "weather-sub" :orientation "h" :space-evenly false :spacing 10
      (label :text "feels ${weather-data.feels}°")
      (label :text "${weather-data.humidity}% hum")
      (label :text "${weather-data.wind} km/h"))))

(defpoll net-speed
  :interval "1s"
  `scripts/net.sh speed`)

(defpoll net-usage
  :interval "30s"
  `scripts/net.sh usage`)

(defwidget net-panel []
  (box :class "net-panel" :orientation "v" :space-evenly false :spacing 6 :height 102
    (label :class "net-header" :text "network" :halign "start")
    (box :orientation "h" :space-evenly false :spacing 16
      (box :orientation "h" :space-evenly false :spacing 4
        (label :class "net-arrow" :text "↓")
        (label :class "net-value" :text {net-speed.down}))
      (box :orientation "h" :space-evenly false :spacing 4
        (label :class "net-arrow" :text "↑")
        (label :class "net-value" :text {net-speed.up})))
    (box :class "net-usage-row" :orientation "h" :space-evenly false :spacing 12
      (label :text "total ↓ ${net-usage.down_total}")
      (label :text "↑ ${net-usage.up_total}"))))

(defpoll ram-data
  :interval "5s"
  :initial "{\"used\": \"--\", \"total\": \"--\", \"percent\": \"0\"}"
  `scripts/ram.sh`)

(defwidget ram-panel []
  (box :class "ram-panel" :orientation "v" :space-evenly false :spacing 4 :height 138
    (label :class "ram-header" :text "memory" :halign "start")
    (circular-progress :class "usage-gauge ${ram-data.percent > 85 ? "critical" : ""}"
                       :value {ram-data.percent}
                       :thickness 8
                       :width 72
                       :height 72
      (label :class "usage-gauge-label" :text "${ram-data.percent}%" :width 56 :height 56 :halign "center"))
    (label :class "ram-sub" :text "${ram-data.used} / ${ram-data.total}" :halign "start")))

(defpoll cpu-data
  :interval "2s"
  :initial "{\"percent\": \"0\", \"load\": \"--\", \"cores\": \"--\"}"
  `scripts/cpu.sh`)

(defwidget cpu-panel []
  (box :class "cpu-panel" :orientation "v" :space-evenly false :spacing 4 :height 138
    (label :class "cpu-header" :text "cpu" :halign "start")
    (circular-progress :class "usage-gauge ${cpu-data.percent > 85 ? "critical" : ""}"
                       :value {cpu-data.percent}
                       :thickness 8
                       :width 72
                       :height 72
      (label :class "usage-gauge-label" :text "${cpu-data.percent}%" :width 56 :height 56 :halign "center"))
    (label :class "cpu-sub" :text "load ${cpu-data.load} / ${cpu-data.cores} cores" :halign "start")))

(defpoll gpu-data
  :interval "2s"
  :initial "{\"percent\": \"0\", \"vram_used\": \"--\", \"vram_total\": \"--\"}"
  `scripts/gpu.sh`)

(defwidget gpu-panel []
  (box :class "gpu-panel" :orientation "v" :space-evenly false :spacing 4 :height 138
    (label :class "gpu-header" :text "gpu" :halign "start")
    (circular-progress :class "usage-gauge ${gpu-data.percent > 85 ? "critical" : ""}"
                       :value {gpu-data.percent}
                       :thickness 8
                       :width 72
                       :height 72
      (label :class "usage-gauge-label" :text "${gpu-data.percent}%" :width 56 :height 56 :halign "center"))
    (label :class "gpu-sub" :text "${gpu-data.vram_used}M / ${gpu-data.vram_total}M vram" :halign "start")))

(defpoll disk-data
  :interval "30s"
  :initial "{\"percent\": \"0\", \"used\": \"--\", \"total\": \"--\"}"
  `scripts/disk.sh`)

(defwidget disk-panel []
  (box :class "disk-panel" :orientation "v" :space-evenly false :spacing 4 :height 102
    (label :class "disk-header" :text "disk" :halign "start")
    (progress :class "disk-bar ${disk-data.percent > 85 ? "critical" : ""}"
              :value {disk-data.percent}
              :hexpand true)
    (box :class "disk-sub" :orientation "h" :space-evenly false
      (label :text "${disk-data.used} / ${disk-data.total}" :halign "start" :hexpand true)
      (label :class "disk-percent ${disk-data.percent > 85 ? "critical" : ""}"
             :text "${disk-data.percent}%"
             :halign "end"))))

(defwidget widget-stack []
  (box :orientation "v" :space-evenly false :spacing 12
    (weather-panel)
    (net-panel)
    (ram-panel)
    (cpu-panel)
    (gpu-panel)
    (disk-panel)))

(defwindow widgets
  :monitor 0
  :windowtype "dock"
  :stacking "bottom"
  :exclusive false
  :focusable false
  :geometry (geometry
              :x "24px"
              :y "24px"
              :width "200px"
              :height "780px"
              :anchor "top right")
  (widget-stack))

cpu.sh

eww/scripts/cpu.sh · 22 lines

#!/usr/bin/env bash
# Usage: cpu.sh

read -r _ user nice system idle iowait irq softirq steal _ _ </proc/stat
prev_idle=$((idle + iowait))
prev_total=$((user + nice + system + idle + iowait + irq + softirq + steal))

sleep 0.5

read -r _ user nice system idle iowait irq softirq steal _ _ </proc/stat
idle_now=$((idle + iowait))
total_now=$((user + nice + system + idle + iowait + irq + softirq + steal))

diff_idle=$((idle_now - prev_idle))
diff_total=$((total_now - prev_total))
percent=$(((1000 * (diff_total - diff_idle) / diff_total + 5) / 10))

load=$(awk '{print $1}' /proc/loadavg)
cores=$(nproc)

jq -n --arg percent "$percent" --arg load "$load" --arg cores "$cores" \
  '{percent: $percent, load: $load, cores: $cores}'

disk.sh

eww/scripts/disk.sh · 7 lines

#!/usr/bin/env bash
# Usage: disk.sh

read -r percent used total <<<"$(df -h / | awk 'NR==2 {gsub("%","",$5); print $5, $3, $2}')"

jq -n --arg percent "$percent" --arg used "$used" --arg total "$total" \
  '{percent: $percent, used: $used, total: $total}'

gpu.sh

eww/scripts/gpu.sh · 17 lines

#!/usr/bin/env bash
# Usage: gpu.sh

if [ -f /sys/class/drm/card1/device/gpu_busy_percent ]; then
  percent=$(cat /sys/class/drm/card1/device/gpu_busy_percent)
  vram_used=$(($(cat /sys/class/drm/card1/device/mem_info_vram_used 2>/dev/null || echo 0) / 1048576))
  vram_total=$(($(cat /sys/class/drm/card1/device/mem_info_vram_total 2>/dev/null || echo 0) / 1048576))
elif command -v nvidia-smi >/dev/null; then
  read -r percent vram_used vram_total <<<"$(nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total --format=csv,noheader,nounits | tr -d ',')"
else
  percent=0
  vram_used=0
  vram_total=0
fi

jq -n --arg percent "$percent" --arg vram_used "$vram_used" --arg vram_total "$vram_total" \
  '{percent: $percent, vram_used: $vram_used, vram_total: $vram_total}'

net.sh

eww/scripts/net.sh · 79 lines

#!/usr/bin/env bash
# Usage: net.sh speed | net.sh usage

mode="$1"

# Detect first active (UP, non-loopback) interface
iface=$(ip -brief link | grep "  UP " | awk '{print $1}' | xargs | awk '$1 != "lo" {print $1; exit}')

if [ -z "$iface" ]; then
  if [ "$mode" = "speed" ]; then
    echo '{"down": "--", "up": "--"}'
  else
    echo '{"down_total": "--", "up_total": "--"}'
  fi
  exit 0
fi

rx_path="/sys/class/net/$iface/statistics/rx_bytes"
tx_path="/sys/class/net/$iface/statistics/tx_bytes"

if [ ! -f "$rx_path" ]; then
  if [ "$mode" = "speed" ]; then
    echo '{"down": "--", "up": "--"}'
  else
    echo '{"down_total": "--", "up_total": "--"}'
  fi
  exit 0
fi

human_rate() {
  local kb=$1
  if [ "$kb" -gt 1000 ]; then
    echo "$(echo "scale=1; $kb/1024" | bc) MB/s"
  else
    echo "${kb} KB/s"
  fi
}

human_total() {
  local bytes=$1
  if [ "$bytes" -gt 1073741824 ]; then
    echo "$(echo "scale=2; $bytes/1073741824" | bc) GB"
  else
    echo "$((bytes / 1048576)) MB"
  fi
}

case "$mode" in
speed)
  rx1=$(cat "$rx_path")
  tx1=$(cat "$tx_path")
  sleep 1
  rx2=$(cat "$rx_path")
  tx2=$(cat "$tx_path")

  down_kb=$(((rx2 - rx1) / 1024))
  up_kb=$(((tx2 - tx1) / 1024))

  down=$(human_rate "$down_kb")
  up=$(human_rate "$up_kb")

  jq -n --arg down "$down" --arg up "$up" '{down: $down, up: $up}'
  ;;

usage)
  rx=$(cat "$rx_path")
  tx=$(cat "$tx_path")

  down_total=$(human_total "$rx")
  up_total=$(human_total "$tx")

  jq -n --arg down "$down_total" --arg up "$up_total" '{down_total: $down, up_total: $up}'
  ;;

*)
  echo '{"error": "usage: net.sh speed|usage"}'
  exit 1
  ;;
esac

ram.sh

eww/scripts/ram.sh · 22 lines

#!/usr/bin/env bash
# Usage: ram.sh

mem=$(free -m | awk '/Mem:/ {printf "%d %d %d", $3, $2, ($3/$2)*100}')
used=$(echo "$mem" | awk '{print $1}')
total=$(echo "$mem" | awk '{print $2}')
percent=$(echo "$mem" | awk '{print $3}')

human() {
  local mb=$1
  if [ "$mb" -gt 1024 ]; then
    echo "$(echo "scale=1; $mb/1024" | bc)G"
  else
    echo "${mb}M"
  fi
}

used_h=$(human "$used")
total_h=$(human "$total")

jq -n --arg used "$used_h" --arg total "$total_h" --arg percent "$percent" \
  '{used: $used, total: $total, percent: $percent}'

weather.sh

eww/scripts/weather.sh · 39 lines

#!/usr/bin/env bash
data=$(curl -s "wttr.in/?format=j1" --max-time 5)
location=$(curl -s "wttr.in/?format=%l" --max-time 5)

if [ -z "$data" ]; then
  echo '{"icon": "\uf071", "temp": "--", "feels": "--", "desc": "offline", "humidity": "--", "wind": "--", "city": "--"}'
  exit 0
fi

temp=$(echo "$data" | jq -r '.current_condition[0].temp_C')
feels=$(echo "$data" | jq -r '.current_condition[0].FeelsLikeC')
desc=$(echo "$data" | jq -r '.current_condition[0].weatherDesc[0].value')
humidity=$(echo "$data" | jq -r '.current_condition[0].humidity')
wind=$(echo "$data" | jq -r '.current_condition[0].windspeedKmph')
code=$(echo "$data" | jq -r '.current_condition[0].weatherCode')
# The penultimate component is the city; single-component responses use the full value.
city=$(printf '%s' "$location" | awk -F',' '{ city = $(NF - 1); gsub(/^[[:space:]]+|[[:space:]]+$/, "", city); print toupper(city) }')
[ -n "$city" ] || city="--"

case "$code" in
113) icon="\ue30d" ;;
116) icon="\ue302" ;;
119 | 122) icon="\ue312" ;;
143 | 248 | 260) icon="\ue313" ;;
176 | 263 | 266 | 281 | 284 | 293 | 296 | 299 | 302 | 305 | 308 | 311 | 314 | 353 | 356 | 359) icon="\ue318" ;;
179 | 182 | 185 | 227 | 230 | 317 | 320 | 323 | 326 | 329 | 332 | 335 | 338 | 350 | 362 | 365 | 368 | 371 | 374 | 377) icon="\ue31a" ;;
200 | 386 | 389 | 392 | 395) icon="\ue31d" ;;
*) icon="\ue312" ;;
esac

jq -n \
  --arg icon "$icon" \
  --arg temp "$temp" \
  --arg feels "$feels" \
  --arg desc "$desc" \
  --arg humidity "$humidity" \
  --arg wind "$wind" \
  --arg city "$city" \
  '{icon: $icon, temp: $temp, feels: $feels, desc: $desc, humidity: $humidity, wind: $wind, city: $city}'

General

.aerospace.toml

.aerospace.toml · 174 lines

# Place a copy of this config to ~/.aerospace.toml
# After that, you can edit ~/.aerospace.toml to your liking

# You can use it to add commands that run after login to macOS user session.
# 'start-at-login' needs to be 'true' for 'after-login-command' to work
# Available commands: https://nikitabobko.github.io/AeroSpace/commands
after-login-command = []

# You can use it to add commands that run after AeroSpace startup.
# 'after-startup-command' is run after 'after-login-command'
# Available commands : https://nikitabobko.github.io/AeroSpace/commands
after-startup-command = []

# Start AeroSpace at login
start-at-login = false

# Normalizations. See: https://nikitabobko.github.io/AeroSpace/guide#normalization
enable-normalization-flatten-containers = true
enable-normalization-opposite-orientation-for-nested-containers = true

# See: https://nikitabobko.github.io/AeroSpace/guide#layouts
# The 'accordion-padding' specifies the size of accordion padding
# You can set 0 to disable the padding feature
accordion-padding = 30

# Possible values: tiles|accordion
default-root-container-layout = 'tiles'

# Possible values: horizontal|vertical|auto
# 'auto' means: wide monitor (anything wider than high) gets horizontal orientation,
#               tall monitor (anything higher than wide) gets vertical orientation
default-root-container-orientation = 'auto'

# Mouse follows focus when focused monitor changes
# Drop it from your config, if you don't like this behavior
# See https://nikitabobko.github.io/AeroSpace/guide#on-focus-changed-callbacks
# See https://nikitabobko.github.io/AeroSpace/commands#move-mouse
# Fallback value (if you omit the key): on-focused-monitor-changed = []
on-focused-monitor-changed = ['move-mouse monitor-lazy-center']

# You can effectively turn off macOS "Hide application" (cmd-h) feature by toggling this flag
# Useful if you don't use this macOS feature, but accidentally hit cmd-h or cmd-alt-h key
# Also see: https://nikitabobko.github.io/AeroSpace/goodies#disable-hide-app
automatically-unhide-macos-hidden-apps = false

# Possible values: (qwerty|dvorak)
# See https://nikitabobko.github.io/AeroSpace/guide#key-mapping
[key-mapping]
preset = 'qwerty'

# Gaps between windows (inner-*) and between monitor edges (outer-*).
# Possible values:
# - Constant:     gaps.outer.top = 8
# - Per monitor:  gaps.outer.top = [{ monitor.main = 16 }, { monitor."some-pattern" = 32 }, 24]
#                 In this example, 24 is a default value when there is no match.
#                 Monitor pattern is the same as for 'workspace-to-monitor-force-assignment'.
#                 See: https://nikitabobko.github.io/AeroSpace/guide#assign-workspaces-to-monitors
[gaps]
inner.horizontal = 0
inner.vertical =   0
outer.left =       0
outer.bottom =     0
outer.top =        0
outer.right =      0

# 'main' binding mode declaration
# See: https://nikitabobko.github.io/AeroSpace/guide#binding-modes
# 'main' binding mode must be always presented
# Fallback value (if you omit the key): mode.main.binding = {}
[mode.main.binding]

# All possible keys:
# - Letters.        a, b, c, ..., z
# - Numbers.        0, 1, 2, ..., 9
# - Keypad numbers. keypad0, keypad1, keypad2, ..., keypad9
# - F-keys.         f1, f2, ..., f20
# - Special keys.   minus, equal, period, comma, slash, backslash, quote, semicolon, backtick,
#                   leftSquareBracket, rightSquareBracket, space, enter, esc, backspace, tab
# - Keypad special. keypadClear, keypadDecimalMark, keypadDivide, keypadEnter, keypadEqual,
#                   keypadMinus, keypadMultiply, keypadPlus
# - Arrows.         left, down, up, right

# All possible modifiers: cmd, alt, ctrl, shift

# All possible commands: https://nikitabobko.github.io/AeroSpace/commands

# See: https://nikitabobko.github.io/AeroSpace/commands#exec-and-forget
# You can uncomment the following lines to open up terminal with alt + enter shortcut (like in i3)
# alt-enter = '''exec-and-forget osascript -e '
# tell application "Terminal"
#     do script
#     activate
# end tell'
# '''

# See: https://nikitabobko.github.io/AeroSpace/commands#layout
alt-slash = 'layout tiles horizontal vertical'
alt-comma = 'layout accordion horizontal vertical'

# See: https://nikitabobko.github.io/AeroSpace/commands#focus
alt-h = 'focus left'
alt-j = 'focus down'
alt-k = 'focus up'
alt-l = 'focus right'

# See: https://nikitabobko.github.io/AeroSpace/commands#move
alt-shift-h = 'move left'
alt-shift-j = 'move down'
alt-shift-k = 'move up'
alt-shift-l = 'move right'

# See: https://nikitabobko.github.io/AeroSpace/commands#resize
alt-shift-minus = 'resize smart -50'
alt-shift-equal = 'resize smart +50'

# See: https://nikitabobko.github.io/AeroSpace/commands#workspace
alt-1 = 'workspace 1'
alt-2 = 'workspace 2'
alt-3 = 'workspace 3'
alt-4 = 'workspace 4'
alt-5 = 'workspace 5'
alt-6 = 'workspace 6'
alt-7 = 'workspace 7'
alt-8 = 'workspace 8'
alt-9 = 'workspace 9'
alt-0 = 'workspace 0'

# See: https://nikitabobko.github.io/AeroSpace/commands#move-node-to-workspace
alt-shift-1 = 'move-node-to-workspace 1'
alt-shift-2 = 'move-node-to-workspace 2'
alt-shift-3 = 'move-node-to-workspace 3'
alt-shift-4 = 'move-node-to-workspace 4'
alt-shift-5 = 'move-node-to-workspace 5'
alt-shift-6 = 'move-node-to-workspace 6'
alt-shift-7 = 'move-node-to-workspace 7'
alt-shift-8 = 'move-node-to-workspace 8'
alt-shift-9 = 'move-node-to-workspace 9'
alt-shift-0 = 'move-node-to-workspace 0'

# See: https://nikitabobko.github.io/AeroSpace/commands#workspace-back-and-forth
alt-tab = 'workspace-back-and-forth'
# See: https://nikitabobko.github.io/AeroSpace/commands#move-workspace-to-monitor
alt-shift-tab = 'move-workspace-to-monitor --wrap-around next'

# See: https://nikitabobko.github.io/AeroSpace/commands#mode
alt-shift-semicolon = 'mode service'

# 'service' binding mode declaration.
# See: https://nikitabobko.github.io/AeroSpace/guide#binding-modes
[mode.service.binding]
esc = ['reload-config', 'mode main']
r = ['flatten-workspace-tree', 'mode main'] # reset layout
f = ['layout floating tiling', 'mode main'] # Toggle between floating and tiling layout
backspace = ['close-all-windows-but-current', 'mode main']

# sticky is not yet supported https://github.com/nikitabobko/AeroSpace/issues/2
#s = ['layout sticky tiling', 'mode main']

alt-shift-h = ['join-with left', 'mode main']
alt-shift-j = ['join-with down', 'mode main']
alt-shift-k = ['join-with up', 'mode main']
alt-shift-l = ['join-with right', 'mode main']

down = 'volume down'
up = 'volume up'

shift-down = ['volume set 0', 'mode main']

# custom rules
# https://nikitabobko.github.io/AeroSpace/guide#on-window-detected-callback
[[on-window-detected]]
if.app-id = 'com.apple.finder'
check-further-callbacks = true
run = ['layout floating']

.alacritty.toml

.alacritty.toml · 627 lines

[general]
live_config_reload = true

[bell]
animation = "EaseOutExpo"
duration = 0

[colors]
draw_bold_text_with_bright_colors = true
indexed_colors = []

[colors.bright]
black = "0x4d4d4d"
blue = "0xcaa9fa"
cyan = "0x9aedfe"
green = "0x5af78e"
magenta = "0xff92d0"
red = "0xff6e67"
white = "0xe6e6e6"
yellow = "0xf4f99d"

[colors.cursor]
cursor = "0xf8f8f2"
text = "0x44475a"

[colors.dim]
black = "0x14151b"
blue = "0x4d5b86"
cyan = "0x59dffc"
green = "0x1ef956"
magenta = "0xff46b0"
red = "0xff2222"
white = "0xe6e6d1"
yellow = "0xebf85b"

[colors.normal]
black = "0x000000"
blue = "0xbd93f9"
cyan = "0x8be9fd"
green = "0x50fa7b"
magenta = "0xff79c6"
red = "0xff5555"
white = "0xbfbfbf"
yellow = "0xf1fa8c"

[colors.primary]
background = "0x282a36"
foreground = "0xf8f8f2"

[cursor]
style = "Block"
unfocused_hollow = true

[env]
TERM = "xterm-256color"

[font]
size = 12.0

[font.normal]
family = "JetBrainsMono Nerd Font"

[font.glyph_offset]
x = 0
y = 0

[font.offset]
x = 0
y = 0

[[keyboard.bindings]]
action = "Paste"
key = "V"
mods = "Command"

[[keyboard.bindings]]
action = "Copy"
key = "C"
mods = "Command"

[[keyboard.bindings]]
action = "Paste"
key = "Paste"

[[keyboard.bindings]]
action = "Copy"
key = "Copy"

[[keyboard.bindings]]
action = "Hide"
key = "H"
mods = "Command"

[[keyboard.bindings]]
action = "Quit"
key = "Q"
mods = "Command"

[[keyboard.bindings]]
action = "Quit"
key = "W"
mods = "Command"

[[keyboard.bindings]]
chars = "\u001BOH"
key = "Home"
mode = "AppCursor"

[[keyboard.bindings]]
chars = "\u001B[H"
key = "Home"
mode = "~AppCursor"

[[keyboard.bindings]]
chars = "\u001BOF"
key = "End"
mode = "AppCursor"

[[keyboard.bindings]]
chars = "\u001B[F"
key = "End"
mode = "~AppCursor"

[[keyboard.bindings]]
action = "ResetFontSize"
key = "Key0"
mods = "Command"

[[keyboard.bindings]]
action = "IncreaseFontSize"
key = "Equals"
mods = "Command"

[[keyboard.bindings]]
action = "DecreaseFontSize"
key = "Minus"
mods = "Command"

[[keyboard.bindings]]
action = "ClearHistory"
key = "K"
mods = "Command"

[[keyboard.bindings]]
chars = "\f"
key = "K"
mods = "Command"

[[keyboard.bindings]]
chars = "\u001B[5;2~"
key = "PageUp"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[5;5~"
key = "PageUp"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[5~"
key = "PageUp"

[[keyboard.bindings]]
chars = "\u001B[6;2~"
key = "PageDown"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[6;5~"
key = "PageDown"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[6~"
key = "PageDown"

[[keyboard.bindings]]
chars = "\u001B[Z"
key = "Tab"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u007F"
key = "Back"

[[keyboard.bindings]]
chars = "\u001B\u007F"
key = "Back"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[2~"
key = "Insert"

[[keyboard.bindings]]
chars = "\u001B[3~"
key = "Delete"

[[keyboard.bindings]]
chars = "\u001B[1;2D"
key = "Left"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[1;5D"
key = "Left"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001Bb"
key = "Left"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[D"
key = "Left"
mode = "~AppCursor"

[[keyboard.bindings]]
chars = "\u001BOD"
key = "Left"
mode = "AppCursor"

[[keyboard.bindings]]
chars = "\u001B[1;2C"
key = "Right"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[1;5C"
key = "Right"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001Bf"
key = "Right"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[C"
key = "Right"
mode = "~AppCursor"

[[keyboard.bindings]]
chars = "\u001BOC"
key = "Right"
mode = "AppCursor"

[[keyboard.bindings]]
chars = "\u001B[1;2A"
key = "Up"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[1;5A"
key = "Up"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[1;3A"
key = "Up"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[A"
key = "Up"
mode = "~AppCursor"

[[keyboard.bindings]]
chars = "\u001BOA"
key = "Up"
mode = "AppCursor"

[[keyboard.bindings]]
chars = "\u001B[1;2B"
key = "Down"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[1;5B"
key = "Down"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[1;3B"
key = "Down"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[B"
key = "Down"
mode = "~AppCursor"

[[keyboard.bindings]]
chars = "\u001BOB"
key = "Down"
mode = "AppCursor"

[[keyboard.bindings]]
chars = "\u001BOP"
key = "F1"

[[keyboard.bindings]]
chars = "\u001BOQ"
key = "F2"

[[keyboard.bindings]]
chars = "\u001BOR"
key = "F3"

[[keyboard.bindings]]
chars = "\u001BOS"
key = "F4"

[[keyboard.bindings]]
chars = "\u001B[15~"
key = "F5"

[[keyboard.bindings]]
chars = "\u001B[17~"
key = "F6"

[[keyboard.bindings]]
chars = "\u001B[18~"
key = "F7"

[[keyboard.bindings]]
chars = "\u001B[19~"
key = "F8"

[[keyboard.bindings]]
chars = "\u001B[20~"
key = "F9"

[[keyboard.bindings]]
chars = "\u001B[21~"
key = "F10"

[[keyboard.bindings]]
chars = "\u001B[23~"
key = "F11"

[[keyboard.bindings]]
chars = "\u001B[24~"
key = "F12"

[[keyboard.bindings]]
chars = "\u001B[1;2P"
key = "F1"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[1;2Q"
key = "F2"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[1;2R"
key = "F3"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[1;2S"
key = "F4"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[15;2~"
key = "F5"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[17;2~"
key = "F6"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[18;2~"
key = "F7"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[19;2~"
key = "F8"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[20;2~"
key = "F9"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[21;2~"
key = "F10"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[23;2~"
key = "F11"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[24;2~"
key = "F12"
mods = "Shift"

[[keyboard.bindings]]
chars = "\u001B[1;5P"
key = "F1"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[1;5Q"
key = "F2"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[1;5R"
key = "F3"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[1;5S"
key = "F4"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[15;5~"
key = "F5"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[17;5~"
key = "F6"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[18;5~"
key = "F7"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[19;5~"
key = "F8"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[20;5~"
key = "F9"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[21;5~"
key = "F10"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[23;5~"
key = "F11"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[24;5~"
key = "F12"
mods = "Control"

[[keyboard.bindings]]
chars = "\u001B[1;6P"
key = "F1"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[1;6Q"
key = "F2"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[1;6R"
key = "F3"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[1;6S"
key = "F4"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[15;6~"
key = "F5"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[17;6~"
key = "F6"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[18;6~"
key = "F7"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[19;6~"
key = "F8"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[20;6~"
key = "F9"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[21;6~"
key = "F10"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[23;6~"
key = "F11"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[24;6~"
key = "F12"
mods = "Alt"

[[keyboard.bindings]]
chars = "\u001B[1;3P"
key = "F1"
mods = "Command"

[[keyboard.bindings]]
chars = "\u001B[1;3Q"
key = "F2"
mods = "Command"

[[keyboard.bindings]]
chars = "\u001B[1;3R"
key = "F3"
mods = "Command"

[[keyboard.bindings]]
chars = "\u001B[1;3S"
key = "F4"
mods = "Command"

[[keyboard.bindings]]
chars = "\u001B[15;3~"
key = "F5"
mods = "Command"

[[keyboard.bindings]]
chars = "\u001B[17;3~"
key = "F6"
mods = "Command"

[[keyboard.bindings]]
chars = "\u001B[18;3~"
key = "F7"
mods = "Command"

[[keyboard.bindings]]
chars = "\u001B[19;3~"
key = "F8"
mods = "Command"

[[keyboard.bindings]]
chars = "\u001B[20;3~"
key = "F9"
mods = "Command"

[[keyboard.bindings]]
chars = "\u001B[21;3~"
key = "F10"
mods = "Command"

[[keyboard.bindings]]
chars = "\u001B[23;3~"
key = "F11"
mods = "Command"

[[keyboard.bindings]]
chars = "\u001B[24;3~"
key = "F12"
mods = "Command"

[[keyboard.bindings]]
chars = """

"""
key = "NumpadEnter"

# Shift+Enter for multi-line input in pi
[[keyboard.bindings]]
chars = "\u001B[13;2u"
key = "Return"
mods = "Shift"

[mouse]
hide_when_typing = false

[[mouse.bindings]]
action = "PasteSelection"
mouse = "Middle"

[scrolling]
history = 10000
multiplier = 3

[selection]
save_to_clipboard = false
semantic_escape_chars = ",│`|:\"' ()[]{}<>"

[window]
decorations = "full"
dynamic_title = true
opacity = 0.9
blur = true
option_as_alt = "both"

[window.dimensions]
columns = 200
lines = 50

[window.padding]
x = 2
y = 2

.editorconfig

.editorconfig · 22 lines

# http://editorconfig.org

root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.{ini,yml,json,jsonc,lua,sh,css,scss}]
indent_style = space
indent_size = 2

[*.md]
trim_trailing_whitespace = false

[Makefile]
indent_style = tab

[.gitconfig]
indent_style = tab

.git_commit_message.txt

.git_commit_message.txt · 5 lines

# If applied, this commit will...

# Explain why this change is being made

# Provide links to any relevant tickets, articles or other resources

.gitconfig

.gitconfig · 35 lines

[user]
	name = Erick Navarro
	email = erick@navarro.io
	signingKey = ~/.ssh/id_ed25519.pub
[alias]
	pl = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative
	cm = commit -v
	co = checkout
	br = branch
	json = log --pretty=format:'{%n  "commit": "%H",%n  "abbreviated_commit": "%h",%n  "tree": "%T",%n  "abbreviated_tree": "%t",%n  "parent": "%P",%n  "abbreviated_parent": "%p",%n  "refs": "%D",%n  "encoding": "%e",%n  "subject": "%s",%n  "sanitized_subject_line": "%f",%n  "body": "%b",%n  "commit_notes": "%N",%n  "verification_flag": "%G?",%n  "signer": "%GS",%n  "signer_key": "%GK",%n  "author": {%n    "name": "%aN",%n    "email": "%aE",%n    "date": "%aD"%n  },%n  "commiter": {%n    "name": "%cN",%n    "email": "%cE",%n    "date": "%cD"%n  }%n},'
[core]
	editor = vim
	autocrlf = input
	quotepath = false
	excludesfile = ~/.gitignore
[github]
	user = erickgnavar
[diff]
	compactionHeuristic = true
[color "diff"]
	old = red strike
	new = green italic
[commit]
	template = ~/.git_commit_message.txt
	gpgsign = true
[gpg]
	format = ssh
[status]
	showUntrackedFiles = all
[gitlab]
	user = erickgnavar
[pull]
	rebase = true
[init]
	defaultBranch = master

.gitignore

.gitignore · 5 lines

.DS_Store
.emacs.d/elpa
.emacs.d/backups
.emacs.d/quelpa
*.elc

.ideavimrc

.ideavimrc · 2 lines

nnoremap gcc :action CommentByLineComment<CR>
vnoremap gc :action CommentByLineComment<CR>

.shellcheckrc

.shellcheckrc · 3 lines

disable=SC1090 # load files using source or .
disable=SC1091 # cannot reach files outside project
disable=SC2012 # temporary until replace ls with find

.tmux.conf

.tmux.conf · 121 lines

# Enable extended keys for Shift+Enter, Ctrl+Enter etc.
set -g extended-keys on
set -g extended-keys-format csi-u

# Terminal capabilities
set -g default-terminal "tmux-256color"
set -ag terminal-overrides ",xterm-256color:RGB"

# Change prefix key
unbind C-b
set -g prefix C-Space

# reload conf
bind r source-file ~/.tmux.conf \; display "tmux config reloaded!"

# move through panes
unbind h
bind -r h select-pane -L

unbind j
bind -r j select-pane -D

unbind k
bind -r k select-pane -U

unbind l
bind -r l select-pane -R

unbind n
bind -r n next-window

unbind p
bind -r p previous-window

# Resizing
bind -r C-h resize-pane -L 5
bind -r C-j resize-pane -D 5
bind -r C-k resize-pane -U 5
bind -r C-l resize-pane -R 5

# Move current pane to a separate window
bind b break-pane -d

# More comfortable split shortcuts
bind '\' split-window -h -c '#{pane_current_path}'  # Split panes horizontal
bind - split-window -v -c '#{pane_current_path}'  # Split panes vertically

# fzf session switcher on prefix + space
bind Space display-popup -E 'tmux list-sessions -F "#{session_name}" | fzf --prompt="session> " | xargs -r tmux switch-client -t'

# allow to select links in buffer using fzf
bind u display-popup -E "tmux capture-pane -J -p | grep -oE 'https?://[^[:space:]]+' | sort -u | fzf --reverse | xargs open"

# window list
setw -g automatic-rename on
set-window-option -g mode-keys vi
set-window-option -g automatic-rename off

# enable mouse
set -g mouse on

# Status update interval
set -g status-interval 1

# UI improvements copied originally from https://github.com/jimeh/tmux-themepack

# Basic status bar colors
set -g status-style fg=cyan,bg=black

# Left side of status bar
set -g status-left-style fg=green,bg=black
set -g status-left-length 40
set -g status-left "#S #[fg=white]» #[fg=yellow]#I #[fg=cyan]#P"

# Right side of status bar
set -g status-right-style fg=cyan,bg=black
set -g status-right-length 50
set -g status-right "#H #[fg=white]« #[fg=yellow]%H:%M:%S #[fg=green]%d-%b-%y"

# Window status
set -g window-status-format " #I:#W#F "
set -g window-status-current-format " #I:#W#F "

# Current window status
set -g window-status-current-style fg=black,bg=red

# Window with activity status
set -g window-status-activity-style fg=black,bg=yellow

# Window separator
set -g window-status-separator ""

# Window status alignment
set -g status-justify centre

# show border indicator and arrows
set -g pane-border-indicators both

# Pane border
set -g pane-border-style fg=colour240,bg=default
set -g pane-border-lines heavy

# Active pane border
set -g pane-active-border-style fg=magenta,bg=default,bold

# Pane number indicator
set -g display-panes-colour default
set -g display-panes-active-colour default

# Clock mode
set -g clock-mode-colour red
set -g clock-mode-style 24

# Message
set -g message-style fg=default,bg=default

# Command message
set -g message-command-style fg=default,bg=default

# Mode
set -g mode-style fg=default,bg=red

.vimrc

.vimrc · 164 lines

set nocompatible

" --- vim-plug bootstrap -------------------------------------------------
" Downloads plug.vim to ~/.vim/autoload/ on first run so plug#begin is
" available immediately, then runs :PlugInstall to fetch the plugins.
let s:plug_install = empty(glob('~/.vim/autoload/plug.vim'))
if s:plug_install
    echo "Installing vim-plug..."
    silent !mkdir -p ~/.vim/autoload
    silent !curl -fLo ~/.vim/autoload/plug.vim
        \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
endif
call plug#begin('~/.vim/plugged')

" Plugins
Plug 'tomtom/tcomment_vim'
" Zen coding
Plug 'mattn/emmet-vim'
" Airline
Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes'
Plug 'tpope/vim-surround'
Plug 'Townk/vim-autoclose' " Allow show diff sign of opened files
Plug 'lilydjwg/colorizer'
Plug 'editorconfig/editorconfig-vim'
Plug 'sheerun/vim-polyglot'  " Plugins for most of the programming languages
Plug 'elzr/vim-json'
Plug 'mattn/webapi-vim' "gist-vim requirement
Plug 'mattn/gist-vim'
Plug 'tpope/vim-fugitive'
Plug 'airblade/vim-gitgutter'
Plug 'henrik/vim-indexed-search'
Plug 'airblade/vim-rooter'
Plug 'terryma/vim-expand-region'
Plug 'chriskempson/tomorrow-theme', { 'rtp': 'vim' }
" Provides the fzf#run autoload glue used by fzf.vim; the binary comes from the system.
Plug 'junegunn/fzf'
Plug 'junegunn/fzf.vim'
Plug 'andymass/vim-matchup'

call plug#end()

let g:loaded_matchit = 1

if s:plug_install
    echo "Installing plugins, please ignore key map error messages"
    echo ""
    :PlugInstall
endif

" ============================================================================
" Vim settings and mappings
" You can edit them as you wish

" allow plugins by file type (required for plugins!)
filetype plugin on
filetype indent on

" tabs and spaces handling
set expandtab
set tabstop=4
set softtabstop=4
set shiftwidth=4

set ls=2 "always show status bar

set incsearch "incremental search
set hlsearch "highlighted search results

syntax enable "enable syntax highlight
set path+=**

" avoid to show an error with long file paths
set shortmess=F

set number "show line numbers
set showcmd "show last command
set wildmenu "visual autocomplete for command menu
set showmatch "highlight match [{}]
set encoding=utf-8
nnoremap <space> za

" Gist commands
let g:gist_clip_command = 'pbcopy'
let g:gist_detect_filetype = 1

set completeopt-=preview

" save as sudo
ca w!! w !sudo tee "%"

" colors
if has('gui_running')
    set guifont=JetBrainsMono\ Nerd\ Font:h12
endif

silent! colorscheme Tomorrow-Night-Bright

set scrolloff=3 " when scrolling, keep cursor 3 lines away from screen border

" better backup, swap and undos storage
set directory=~/.vim/dirs/tmp     " directory to place swap files in
set backup                        " make backup files
set backupdir=~/.vim/dirs/backups " where to put backup files
set undofile                      " persistent undos - undo after you re-open the file
set undodir=~/.vim/dirs/undos
set viminfo+=n~/.vim/dirs/viminfo

" create needed directories if they don't exist
if !isdirectory(&backupdir)
    call mkdir(&backupdir, "p")
endif
if !isdirectory(&directory)
    call mkdir(&directory, "p")
endif
if !isdirectory(&undodir)
    call mkdir(&undodir, "p")
endif

" Setup clipboard
set clipboard=unnamed

" Setup leader functions
nmap <Leader>a :Rg<space>
nmap <Leader>b :Buffers<ENTER>
nmap <Leader>c :ClearCtrlPCache<ENTER>
nmap <leader>e :call MyFindFiles()<ENTER>
nmap <Leader>g :Git<ENTER>
nmap <Leader>k :bdelete<space>
nmap <Leader>n :enew<ENTER>
nmap <Leader>q :BLines<ENTER>

function MyFindFiles()
  if system('git rev-parse --is-inside-work-tree 2>/dev/null') =~ 'true'
    :GFiles --exclude-standard --others --cached
  else
    :Files
  endif
endfunction

" " Autoclose ------------------------------
" " Fix to let ESC work as espected with Autoclose plugin
let g:AutoClosePumvisible = {"ENTER": "\<C-Y>", "ESC": "\<ESC>"}

set backspace=2

let g:airline_powerline_fonts = 0
let g:airline#extensions#tabline#enabled = 1
let g:airline#extensions#whitespace#enabled = 0

set lazyredraw
set ttyfast

" GitGutter config
set updatetime=100

" Navigate through hunks
nmap <c-n> <Nop>
nmap <c-p> <Nop>
nmap <c-n> :GitGutterNextHunk<ENTER>
nmap <c-p> :GitGutterPrevHunk<ENTER>

" automatically rebalance windows on vim resize
autocmd VimResized * :wincmd = "

.Xmodmap

.Xmodmap · 5 lines

!switch caps lock with escape
remove Lock = Caps_Lock
keysym Caps_Lock = Escape
keysym Escape = Caps_Lock
add Lock = Caps_Lock

Brewfile

Brewfile · 178 lines

tap "grafana/grafana"
tap "homebrew/bundle"
tap "homebrew/services"
tap "nikitabobko/tap"
tap "twitchdev/twitch"
brew "libtiff"
brew "little-cms2"
brew "webp"
brew "arp-scan"
brew "aspell"
brew "autoconf"
brew "automake"
brew "openssl@3"
brew "bat"
brew "biome"
brew "btop"
brew "caddy", restart_service: :changed
brew "clang-format"
brew "openjdk"
brew "clojure"
brew "cmake"
brew "cocoapods"
brew "colima"
brew "cookiecutter"
brew "curl"
brew "libusb"
brew "cyme"
brew "d2"
brew "defaultbrowser"
brew "docker"
brew "docker-compose"
brew "duf"
brew "exercism"
brew "exiftool"
brew "expat"
brew "eza"
brew "fastfetch"
brew "gnutls"
brew "tesseract"
brew "ffmpeg"
brew "flyctl"
brew "fzf"
brew "gh"
brew "git"
brew "git-delta"
brew "gnu-sed"
brew "gnupg"
brew "go"
brew "gopls"
brew "grafana", restart_service: :changed
brew "librsvg"
brew "graphviz"
brew "helm"
brew "ncurses"
brew "htop"
brew "httpie"
brew "hugo"
brew "imagemagick"
brew "jdtls"
brew "jpeg"
brew "jq"
brew "just"
brew "kondo"
brew "kubernetes-cli"
brew "leiningen"
brew "libgccjit"
brew "libiconv"
brew "libpq"
brew "libvterm"
brew "libxml2"
brew "lorem"
brew "lsusb"
brew "lua-language-server"
brew "mailutils"
brew "markdownlint-cli"
brew "minikube"
brew "minio", restart_service: :changed
brew "mise"
brew "nerdfetch"
brew "nmap"
brew "ocaml"
brew "raylib"
brew "odin"
brew "opam"
brew "pandoc"
brew "pkgconf"
brew "plantuml"
brew "pngpaste"
brew "poppler"
brew "postgresql@13"
brew "prometheus"
brew "pyright"
brew "ripgrep"
brew "ruff"
brew "sbcl"
brew "sevenzip"
brew "shellcheck"
brew "shfmt"
brew "sl"
brew "sqlfluff"
brew "stripe-cli"
brew "stylua"
brew "tdlib", args: ["HEAD"]
brew "tealdeer"
brew "tectonic"
brew "terraform"
brew "texinfo"
brew "texlab"
brew "tinymist"
brew "tmux"
brew "tokei"
brew "tree"
brew "tree-sitter"
brew "typst"
brew "typstyle"
brew "uv"
brew "vim"
brew "wakatime-cli"
brew "watch"
brew "wget"
brew "wrk"
brew "wxwidgets"
brew "xmlformat"
brew "yamllint"
brew "youtube-dl"
brew "zlib"
brew "twitchdev/twitch/twitch-cli"
cask "1password"
cask "aerospace"
cask "alacritty"
cask "alfred"
cask "audacity"
cask "balenaetcher"
cask "blender@lts"
cask "brave-browser"
cask "bruno"
cask "claude"
cask "cleanshot"
cask "cyberduck"
cask "deckset"
cask "discord"
cask "espanso"
cask "firefox"
cask "font-jetbrains-mono-nerd-font"
cask "font-monocraft"
cask "font-open-dyslexic"
cask "font-pacifico"
cask "freecad"
cask "gcloud-cli"
cask "google-chrome"
cask "hammerspoon"
cask "homerow"
cask "imageoptim"
cask "keycastr"
cask "krisp"
cask "loopback"
cask "minecraft"
cask "obs"
cask "openemu"
cask "orcaslicer"
cask "pencil"
cask "pixieditor"
cask "qflipper"
cask "raspberry-pi-imager"
cask "soundsource"
cask "spotify"
cask "steam"
cask "telegram"
cask "textual"
cask "tor-browser"
cask "transmission"
cask "tunnelblick"
cask "vitalsource-bookshelf"
cask "vlc"
cask "wireshark-app"
cask "yubico-authenticator"
cask "zen"
cask "zotero"

global.gitignore

global.gitignore · 11 lines

# macOS files
.DS_Store
# direnv file
.envrc
# mise local files
mise.local.toml
# emacs per project configuration file
.dir-locals.el
# mutagen config files
mutagen.yml
mutagen.yml.lock

placements.txt

placements.txt · 66 lines

.alacritty.toml ~/.alacritty.toml
.emacs.d/bootstrap.org ~/.config/emacs/bootstrap.org
.emacs.d/early-init.el ~/.config/emacs/early-init.el
.emacs.d/init.el ~/.config/emacs/init.el
.emacs.d/snippets ~/.config/emacs/snippets
.emacs.d/elpaca-lockfile.eld ~/.config/emacs/elpaca-lockfile.eld
.git_commit_message.txt ~/.git_commit_message.txt
.gitconfig ~/.gitconfig
.tmux.conf ~/.tmux.conf
.vimrc ~/.vimrc
.ideavimrc ~/.ideavimrc
espanso/match/base.yml ~/Library/Preferences/espanso/match/base.yml
espanso/config/default.yml ~/Library/Preferences/espanso/config/default.yml
global.gitignore ~/.gitignore
zsh/zshenv.sh ~/.zshenv
zsh/zshrc.sh ~/.zshrc
zsh/main.sh ~/.config/zsh/main.sh
zsh/mac.sh ~/.config/zsh/mac.sh
zsh/linux.sh ~/.config/zsh/linux.sh
hammerspoon/init.lua ~/.hammerspoon/init.lua
hammerspoon/windows.lua ~/.hammerspoon/windows.lua
.Xmodmap ~/.Xmodmap
polybar/config.ini ~/.config/polybar/config.ini
polybar/launch.sh ~/.config/polybar/launch.sh
i3/config ~/.config/i3/config
hyprland/hyperland.conf ~/.config/hypr/hyprland.conf
hyprland/scripts/zoom.sh ~/.config/hypr/scripts/zoom.sh
hyprland/scripts/actions.sh ~/.config/hypr/scripts/actions.sh
sway/config ~/.config/sway/config
sway/scripts/wallpaper.sh ~/.config/sway/scripts/wallpaper.sh
sway/scripts/lockscreen.sh ~/.config/sway/scripts/lockscreen.sh
sway/scripts/screenshot.sh ~/.config/sway/scripts/screenshot.sh
sway/scripts/clipboard-menu.sh ~/.config/sway/scripts/clipboard-menu.sh
sway/scripts/wlogout-menu.sh ~/.config/sway/scripts/wlogout-menu.sh
waybar/config.jsonc ~/.config/waybar/config.jsonc
waybar/styles.css ~/.config/waybar/styles.css
waybar/scripts/spotify.sh ~/.config/waybar/scripts/spotify.sh
waybar/scripts/gpu.sh ~/.config/waybar/scripts/gpu.sh
waybar/scripts/vpn.sh ~/.config/waybar/scripts/vpn.sh
waybar/scripts/vnc-session.sh ~/.config/waybar/scripts/vnc-session.sh
waybar/scripts/vnc-toggle.sh ~/.config/waybar/scripts/vnc-toggle.sh
eww/eww.yuck ~/.config/eww/eww.yuck
eww/eww.scss ~/.config/eww/eww.scss
eww/scripts/weather.sh ~/.config/eww/scripts/weather.sh
eww/scripts/net.sh ~/.config/eww/scripts/net.sh
eww/scripts/ram.sh ~/.config/eww/scripts/ram.sh
eww/scripts/cpu.sh ~/.config/eww/scripts/cpu.sh
eww/scripts/gpu.sh ~/.config/eww/scripts/gpu.sh
eww/scripts/disk.sh ~/.config/eww/scripts/disk.sh
wlogout/layout ~/.config/wlogout/layout
wlogout/style.css ~/.config/wlogout/style.css
swaync/config.json ~/.config/swaync/config.json
swaync/style.css ~/.config/swaync/style.css
rofi/config.rasi ~/.config/rofi/config.rasi
swappy/config ~/.config/swappy/config
ghostty/config ~/.config/ghostty/config
gtk/gtk-3.0/settings.ini ~/.config/gtk-3.0/settings.ini
gtk/gtk-4.0/settings.ini ~/.config/gtk-4.0/settings.ini
.aerospace.toml ~/.aerospace.toml
jj/config.toml ~/.config/jj/config.toml
.pi/agent/extensions/prompt-prefix.ts ~/.pi/agent/extensions/prompt-prefix.ts
.pi/agent/themes/tomorrow.json ~/.pi/agent/themes/tomorrow.json
.pi/agent/AGENTS.md ~/.pi/agent/AGENTS.md
.pi/agent/extensions/lang-directives.ts ~/.pi/agent/extensions/lang-directives.ts
.pi/agent/lang-directives.json ~/.pi/agent/lang-directives.json
.pi/agent/skills ~/.pi/agent/skills

README.md

README.md · 25 lines

# Dotfiles

These are my config files for the tools I use:

## Emacs

Requires emacs 26 because it uses native line numbers. Go to `.emacs.d` folder to check the config.

Tested on MacOS Mojave

## Vim

I use vim for quick edits, check out the cconf [here](.vimrc)

## Alacrity

A multiplatform terminal emulator. It's simple and configurable by a yaml [config](.alacritty.yml) file.

## Tmux

A terminal multiplexer, easy to handle panes, splitted windows, sessions, etc. 

## ZSH

Go to `zsh` folder to check the config.

setup.sh

setup.sh · 33 lines

#!/usr/bin/env bash
set -euo pipefail

echo "Setting up your system!"

while read -r line; do
  # Skip empty lines and comments
  [[ -z "$line" || "$line" == \#* ]] && continue

  config_file="$(pwd)/$(echo "$line" | awk '{print $1}')"
  path=$(echo "$line" | awk '{print $2}')
  # Expand ~ to $HOME (avoids unsafe eval)
  path="${path/#\~/$HOME}"
  parent_dir=$(dirname "$path")

  # ensure the parent directory exists
  if [ ! -d "$parent_dir" ]; then
    echo "creating $parent_dir"
    mkdir -p "$parent_dir"
  fi

  if [ -e "$path" ]; then
    echo "$path already exists"
  else
    ln -s "$config_file" "$path"
    echo "symlink created: $config_file -> $path"
  fi
done <placements.txt

if [ ! -d "$HOME/.tmuxifier/" ]; then
  echo "tmuxifier not installed, cloning..."
  git clone https://github.com/jimeh/tmuxifier.git ~/.tmuxifier
fi

Ghostty

config

ghostty/config · 6 lines

theme = dracula
font-size = 12
font-family = JetBrainsMono Nerd Font
background-opacity = 0.9
background-blur-radius = 20
confirm-close-surface = false

GitHub

bash-ci.yaml

.github/workflows/bash-ci.yaml · 22 lines

---
name: Bash CI

on:
  push:
    branches: [ master ]

jobs:
  lint-and-formatter:
    runs-on: ubuntu-latest
    steps:
      - name: Check out the repo
        uses: actions/checkout@v7

      - name: Install shellcheck
        run: sudo apt update && sudo apt install shellcheck shfmt --yes

      - name: Run formatter using shfmt
        run: shfmt -l -i 2 .

      - name: Run shellcheck
        run: find . -name "*.sh" -type f -exec shellcheck {} \;

lua-ci.yaml

.github/workflows/lua-ci.yaml · 19 lines

---
name: Lua CI

on:
  push:
    branches: [ master ]

jobs:
  formatter:
    runs-on: ubuntu-latest
    steps:
      - name: Check out the repo
        uses: actions/checkout@v7

      - uses: JohnnyMorganz/stylua-action@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          version: "0.20.0"
          args: --check --config-path hammerspoon/.stylua.toml .

publish-config-site.yml

.github/workflows/publish-config-site.yml · 53 lines

---
name: Publish config site

on:
  push:
    branches: [master]

permissions:
  contents: write

jobs:
  publish:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v7
      - name: Install Emacs
        run: sudo apt-get update && sudo apt-get install --yes emacs-nox
      - name: Generate site
        run: ./scripts/generate-config-site.py
      - name: Publish gh-pages branch
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          gh auth setup-git
          publish_dir="${RUNNER_TEMP}/config-site-pages"
          repository="https://github.com/${GITHUB_REPOSITORY}.git"

          if git ls-remote --exit-code --heads "$repository" gh-pages >/dev/null; then
            git clone --depth=1 --branch=gh-pages "$repository" "$publish_dir"
          else
            branch_status=$?
            if ((branch_status != 2)); then
              echo "Failed to inspect the gh-pages branch" >&2
              exit "$branch_status"
            fi
            git init --initial-branch=gh-pages "$publish_dir"
            git -C "$publish_dir" remote add origin "$repository"
          fi

          git -C "$publish_dir" rm -r --ignore-unmatch .
          cp -a site/. "$publish_dir"/
          git -C "$publish_dir" add --all
          git -C "$publish_dir" config user.name "$GITHUB_ACTOR"
          git -C "$publish_dir" config user.email "${GITHUB_ACTOR_ID}+${GITHUB_ACTOR}@users.noreply.github.com"

          if git -C "$publish_dir" diff --cached --quiet; then
            echo "No site changes to publish"
          else
            git -C "$publish_dir" commit \
              --message="deploy: publish config site" \
              --message="Generated from ${GITHUB_SHA}"
            git -C "$publish_dir" push origin gh-pages
          fi

Gtk

settings.ini

gtk/gtk-3.0/settings.ini · 4 lines

[Settings]
gtk-theme-name=adw-gtk3-dark
gtk-icon-theme-name=Papirus-Dark
gtk-application-prefer-dark-theme=1

settings.ini

gtk/gtk-4.0/settings.ini · 4 lines

[Settings]
gtk-theme-name=Adwaita-dark
gtk-icon-theme-name=Papirus-Dark
gtk-application-prefer-dark-theme=1

Hammerspoon

.stylua.toml

hammerspoon/.stylua.toml · 4 lines

line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
call_parentheses = "None"

init.lua

hammerspoon/init.lua · 40 lines

-- prefix used to call all the hot keys
leader = { "cmd", "alt", "ctrl" }

hs.hotkey.bind(leader, "R", function()
  hs.reload()
end)

hs.hotkey.bind(leader, "T", function()
  hs.application.open "Terminal.app"
end)

-- windows management config
require "windows"

-- shortcuts to open more common application
hs.hotkey.bind("alt", "1", function()
  hs.application.open "Alacritty.app"
end)

hs.hotkey.bind("alt", "2", function()
  local defaultBrowser = hs.urlevent.getDefaultHandler "http"
  hs.application.launchOrFocusByBundleID(defaultBrowser)
end)

hs.hotkey.bind("alt", "3", function()
  local app = hs.application.find "Emacs"

  if app then
    app:activate()
  else
    -- we need to execute Emacs this way to be able to use mise
    -- properly, otherwise we can't execute stuff like, mix, elixir
    -- and so on
    hs.task.new("/bin/zsh", nil, { "-l", "-c", "emacs > /dev/null 2>&1 & disown" }):start()
  end
end)

hs.hotkey.bind("alt", "4", function()
  hs.application.open "Spotify.app"
end)

windows.lua

hammerspoon/windows.lua · 71 lines

-- coordinates X from left to right and Y from top to bottom

-- remove window movement animation
hs.window.animationDuration = 0

local function getWindowContext()
  local win = hs.window.focusedWindow()
  return win, win:frame(), win:screen(), win:screen():frame()
end

-- put to left
hs.hotkey.bind(leader, "H", function()
  local win, frame, screen, max = getWindowContext()
  frame.x = max.x
  frame.y = max.y
  frame.w = max.w / 2
  frame.h = max.h
  win:setFrame(frame)
end)

-- put to right
hs.hotkey.bind(leader, "L", function()
  local win, frame, screen, max = getWindowContext()
  frame.x = max.x + (max.w / 2)
  frame.y = max.y
  frame.w = max.w / 2
  frame.h = max.h
  win:setFrame(frame)
end)

-- put to top
hs.hotkey.bind(leader, "K", function()
  local win, frame, screen, max = getWindowContext()
  frame.x = max.x
  frame.y = max.y
  frame.w = max.w
  frame.h = max.h / 2
  win:setFrame(frame)
end)

-- put to bottom
hs.hotkey.bind(leader, "J", function()
  local win, frame, screen, max = getWindowContext()
  frame.x = max.x
  frame.y = max.y + (max.h / 2)
  frame.w = max.w
  frame.h = max.h / 2
  win:setFrame(frame)
end)

-- maximize frame
hs.hotkey.bind(leader, "RETURN", function()
  hs.window.focusedWindow():maximize()
end)

-- center frame
hs.hotkey.bind(leader, "\\", function()
  local win, frame, screen, max = getWindowContext()
  -- base grid layout will be 6 x 5 and center frame should be 4 x 3
  frame.x = max.x + (max.w / 6) * 1
  frame.y = max.y + (max.h / 5) * 1
  frame.w = (max.w / 6) * 4
  frame.h = (max.h / 5) * 3
  win:setFrame(frame)
end)

-- move application between screens
hs.hotkey.bind(leader, "M", function()
  local app = hs.window.focusedWindow()
  app:moveToScreen(app:screen():next())
end)

Hyprland

hyperland.conf

hyprland/hyperland.conf · 232 lines

################
### MONITORS ###
################

# See https://wiki.hyprland.org/Configuring/Monitors/
monitor=,highres,auto,1

###################
### MY PROGRAMS ###
###################

# See https://wiki.hyprland.org/Configuring/Keywords/

#################
### AUTOSTART ###
#################

# Autostart necessary processes (like notifications daemons, status bars, etc.)
# Or execute your favorite apps at launch like this:

# network manager
exec-once = nm-applet --indicator
# wayland wallpapers support
exec-once = swwww init
# waybar
exec-once = waybar -c ~/.config/waybar/config.jsonc -s ~/.config/waybar/styles.css
# notification daemon
exec-once = swaync
# start clipboard manager
exec-once = wl-paste --watch cliphist store

#############################
### ENVIRONMENT VARIABLES ###
#############################

# See https://wiki.hyprland.org/Configuring/Environment-variables/

env = XCURSOR_SIZE,24
env = HYPRCURSOR_SIZE,24

#####################
### LOOK AND FEEL ###
#####################

# Refer to https://wiki.hyprland.org/Configuring/Variables/

# https://wiki.hyprland.org/Configuring/Variables/#general
general {
    gaps_in = 2
    gaps_out = 5

    border_size = 0

    # https://wiki.hyprland.org/Configuring/Variables/#variable-types for info about colors
    col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg
    col.inactive_border = rgba(595959aa)

    # Set to true enable resizing windows by clicking and dragging on borders and gaps
    resize_on_border = false

    # Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
    allow_tearing = false

    layout = dwindle
}

# https://wiki.hyprland.org/Configuring/Variables/#decoration
decoration {
    rounding = 5

    # Change transparency of focused and unfocused windows
    active_opacity = 1.0
    inactive_opacity = 0.95

    # https://wiki.hyprland.org/Configuring/Variables/#blur
    blur {
        enabled = true
        size = 3
        passes = 1
        vibrancy = 0.1696
    }
}

# https://wiki.hyprland.org/Configuring/Variables/#animations
animations {
    enabled = false
}

# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
dwindle {
    pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
    preserve_split = true # You probably want this
}

# https://wiki.hyprland.org/Configuring/Variables/#misc
misc {
    force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
    disable_hyprland_logo = false # If true disables the random hyprland logo / anime girl background. :(
}

#############
### INPUT ###
#############

# https://wiki.hyprland.org/Configuring/Variables/#input
input {
    kb_layout = us
    kb_variant =
    kb_model =
    kb_options =
    kb_rules =

    repeat_delay = 300
    repeat_rate = 60

    follow_mouse = 1

    sensitivity = 0 # -1.0 - 1.0, 0 means no modification.

    touchpad {
        natural_scroll = false
    }
}

# https://wiki.hyprland.org/Configuring/Variables/#gestures
gestures {
    workspace_swipe = false
}

# Example per-device config
# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more
device {
    name = epic-mouse-v1
    sensitivity = -0.5
}

####################
### KEYBINDINGSS ###
####################

# See https://wiki.hyprland.org/Configuring/Keywords/
$mainMod = SUPER # Sets "Windows" key as main modifier

# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
bind = $mainMod, return, exec, alacritty
bind = $mainMod, space, exec, rofi -show drun -show-icons
bind = $mainMod, Q, killactive,
bind = $mainMod SHIFT, E, exit,
bind = $mainMod, D, togglefloating,
bind = $mainMod SHIFT, R, exec, pkill -SIGUSR2 waybar
# lock screen using current wallpaper, show clock and blur background
bind = $mainMod CTRL, Q, exec, wlogout

# enter fullscreen
bind = $mainMod, F, fullscreen

# list avaiable actions
bind = $mainMod, A, exec, ~/.config/hypr/scripts/actions.sh

# move focused window
bind = $mainMod SHIFT, H, movewindow, l
bind = $mainMod SHIFT, L, movewindow, r
bind = $mainMod SHIFT, K, movewindow, u
bind = $mainMod SHIFT, J, movewindow, d

# Move focus with mainMod + arrow keys
bind = $mainMod, H, movefocus, l
bind = $mainMod, L, movefocus, r
bind = $mainMod, K, movefocus, u
bind = $mainMod, J, movefocus, d

# Switch workspaces with mainMod + [0-9]
bind = $mainMod, 1, workspace, 1
bind = $mainMod, 2, workspace, 2
bind = $mainMod, 3, workspace, 3
bind = $mainMod, 4, workspace, 4
bind = $mainMod, 5, workspace, 5
bind = $mainMod, 6, workspace, 6
bind = $mainMod, 7, workspace, 7
bind = $mainMod, 8, workspace, 8
bind = $mainMod, 9, workspace, 9
bind = $mainMod, 0, workspace, 10

# Move active window to a workspace with mainMod + SHIFT + [0-9]
bind = $mainMod SHIFT, 1, movetoworkspace, 1
bind = $mainMod SHIFT, 2, movetoworkspace, 2
bind = $mainMod SHIFT, 3, movetoworkspace, 3
bind = $mainMod SHIFT, 4, movetoworkspace, 4
bind = $mainMod SHIFT, 5, movetoworkspace, 5
bind = $mainMod SHIFT, 6, movetoworkspace, 6
bind = $mainMod SHIFT, 7, movetoworkspace, 7
bind = $mainMod SHIFT, 8, movetoworkspace, 8
bind = $mainMod SHIFT, 9, movetoworkspace, 9
bind = $mainMod SHIFT, 0, movetoworkspace, 10

# Scroll through existing workspaces with mainMod + scroll
bind = $mainMod, mouse_down, workspace, e+1
bind = $mainMod, mouse_up, workspace, e-1

# zoom handling
bind = $mainMod, Z, exec, ~/.config/hypr/scripts/zoom.sh

# Open clipboard manager
bind = $mainMod, Y, exec, cliphist list | rofi -dmenu | cliphist decode | wl-copy

# brightness
bind = , XF86MonBrightnessDown, exec, brightnessctl set 5%-
bind = , XF86MonBrightnessUp, exec, brightnessctl set 5%+

# volume
bind = , XF86AudioRaiseVolume, exec, pamixer -i 5
bind = , XF86AudioLowerVolume, exec, pamixer -d 5
bind = , XF86AudioMute, exec, pamixer --toggle-mute

# Move/resize windows with mainMod + LMB/RMB and dragging
bindm = $mainMod, mouse:272, movewindow
bindm = $mainMod, mouse:273, resizewindow

##############################
### WINDOWS AND WORKSPACES ###
##############################

# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules

# Example windowrule v1
# windowrule = float, ^(kitty)$

# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$

windowrulev2 = suppressevent maximize, class:.* # You'll probably like this.

actions.sh

hyprland/scripts/actions.sh · 14 lines

options="Screenshot region|Screenshot window|Screenshot screen"

case $(echo "$options" | rofi -sep '|' -dmenu | tr '[:upper:]' '[:lower:]' | tr ' ' '-') in
"screenshot-region")
  hyprshot -m region --clipboard && wl-paste | swappy -f -
  ;;
"screenshot-window")
  hyprshot -m window --clipboard && wl-paste | swappy -f -
  ;;
"screenshot-screen")
  # we use grim for fullscreen screenshot because for some reason hyprshot takes too long to complete
  grim - | swappy -f -
  ;;
esac

zoom.sh

hyprland/scripts/zoom.sh · 5 lines

if [ "$(hyprctl getoption cursor:zoom_factor | grep float | awk '{print $2}')" = "1.000000" ]; then
  hyprctl keyword cursor:zoom_factor 1.8
else
  hyprctl keyword cursor:zoom_factor 1.0
fi

I3

config

i3/config · 128 lines

# use cmd/win key
set $mod Mod4

# TODO: replace font with jetbrains mono
font pango:Cantarell 10

gaps outer 5px
gaps inner 0px

# Use Mouse+$mod to drag floating windows to their wanted position
floating_modifier $mod

# remove application title
default_border pixel 1
default_floating_border pixel 1

# start a terminal
bindsym $mod+Return exec alacritty

# kill focused window
bindsym $mod+q kill

# start rofi (a program launcher)
bindsym $mod+space exec rofi -show drun -show-icons

# change focus
bindsym $mod+h focus left
bindsym $mod+j focus down
bindsym $mod+k focus up
bindsym $mod+l focus right

# move focused window
bindsym $mod+Shift+h move left
bindsym $mod+Shift+j move down
bindsym $mod+Shift+k move up
bindsym $mod+Shift+l move right

# enter fullscreen mode for the focused container
bindsym $mod+f fullscreen toggle

# change container layout (stacked, tabbed, toggle split)
bindsym $mod+s layout stacking
bindsym $mod+w layout tabbed
bindsym $mod+e layout toggle split

# toggle tiling / floating
bindsym $mod+Shift+d floating toggle

# change focus between tiling / floating windows
bindsym $mod+d focus mode_toggle

# Define names for default workspaces for which we configure key bindings later on.
# We use variables to avoid repeating the names in multiple places.
set $ws1 "1"
set $ws2 "2"
set $ws3 "3"
set $ws4 "4"
set $ws5 "5"
set $ws6 "6"
set $ws7 "7"
set $ws8 "8"
set $ws9 "9"
set $ws10 "10"

# switch to workspace
bindsym $mod+1 workspace $ws1
bindsym $mod+2 workspace $ws2
bindsym $mod+3 workspace $ws3
bindsym $mod+4 workspace $ws4
bindsym $mod+5 workspace $ws5
bindsym $mod+6 workspace $ws6
bindsym $mod+7 workspace $ws7
bindsym $mod+8 workspace $ws8
bindsym $mod+9 workspace $ws9
bindsym $mod+0 workspace $ws10

# move focused container to workspace
bindsym $mod+Shift+1 move container to workspace $ws1
bindsym $mod+Shift+2 move container to workspace $ws2
bindsym $mod+Shift+3 move container to workspace $ws3
bindsym $mod+Shift+4 move container to workspace $ws4
bindsym $mod+Shift+5 move container to workspace $ws5
bindsym $mod+Shift+6 move container to workspace $ws6
bindsym $mod+Shift+7 move container to workspace $ws7
bindsym $mod+Shift+8 move container to workspace $ws8
bindsym $mod+Shift+9 move container to workspace $ws9
bindsym $mod+Shift+0 move container to workspace $ws10

# volume
bindsym XF86AudioMute exec pamixer --toggle-mute
bindsym XF86AudioRaiseVolume exec pamixer -i 5
bindsym XF86AudioLowerVolume exec pamixer -d 5

# reload the configuration file
bindsym $mod+Shift+c reload

# lock screen using a blur background
bindsym $mod+Ctrl+q exec i3lock-fancy

# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
bindsym $mod+Shift+r restart

# exit i3 (logs you out of your X session)
bindsym $mod+Shift+e exec "i3-nagbar -t warning -m 'Do you really want to exit i3? This will end your X session.' -B 'Yes, exit i3' 'i3-msg exit'"

# resize window (you can also use the mouse for that)
bindsym $mod+r mode "resize"

mode "resize" {
  bindsym h resize shrink width 10 px or 10 ppt
  bindsym j resize grow height 10 px or 10 ppt
  bindsym k resize shrink height 10 px or 10 ppt
  bindsym l resize grow width 10 px or 10 ppt

  # back to normal: Enter or Escape or $mod+r
  bindsym Return mode "default"
  bindsym Escape mode "default"
  bindsym $mod+r mode "default"
}

# start polybar
exec_always --no-startup-id $HOME/.config/polybar/launch.sh

# set up key delay and rate
exec_always xset r rate 300 60

# change styles of app window
client.focused          #2e3436 #2e3436 #ffffff #2e3436   #2e3436

Jj

config.toml

jj/config.toml · 13 lines

#:schema https://docs.jj-vcs.dev/latest/config-schema.json

[user]
name = "Erick Navarro"
email = "erick@navarro.io"

[ui]
default-command = "log"

[signing]
behavior = "own"
backend = "ssh"
key = "~/.ssh/id_ed25519.pub"

Nix Darwin

common.nix

nix-darwin/common.nix · 244 lines

{ pkgs, config, emacs-src, ... }: {
  # Allow unfree packages
  nixpkgs.config.allowUnfree = true;

  fonts.packages = with pkgs; [
    jetbrains-mono
    # this is required to show symbols properly
    nerd-fonts.symbols-only
    nerd-fonts.jetbrains-mono
    open-dyslexic
  ];
  # List packages installed in system profile. To search by name, run:
  # $ nix-env -qaP | grep wget
  environment.systemPackages = with pkgs; [
    mkalias
    btop
    htop
    tokei
    vim
    git
    jujutsu
    gh
    delta
    # cmake and glibtool are required to compile libvterm in emacs
    cmake
    just
    glibtool
    alacritty
    eza
    tree
    go
    gopls
    gotools
    air
    biome
    bat
    mise
    xz
    typst
    typstyle
    # LSP for typst
    tinymist
    tealdeer
    nerdfetch
    fastfetch
    fzf
    tmux
    ((pkgs.emacs.override {
      srcRepo = true;
      # drop libgccjit dep, no JIT at runtime
      withNativeCompilation = false;
    }).overrideAttrs (old: {
      name = "emacs-31";
      src = emacs-src;
      enableParallelBuilding = true; # parallel make
      NIX_BUILD_CORES = 0; # use all cores
      CFLAGS = "-O2 -mcpu=native"; # tune for this CPU
      # nixpkgs patches don't apply to this emacs-31 commit (already upstream)
      patches = [ ];
      postPatch = (old.postPatch or "") + ''
        # skip byte compilation of thousands of .el files (saves ~80% of build time)
        substituteInPlace lisp/Makefile.in \
          --replace-fail 'all: compile-main' 'all:'
      '';
    }))
    nixpkgs-fmt
    nixd
    shfmt
    (google-cloud-sdk.withExtraComponents [ google-cloud-sdk.components.gke-gcloud-auth-plugin ])
    ripgrep
    docker-client
    colima
    wakatime-cli
    uv
    kubectl
    kubernetes-helm
    sqlfluff
    stylua
    defaultbrowser
    xmlformat
    yamllint
    jq
    ruff
    pyright
    ty
    pyrefly
    exiftool
    gnused
    imagemagick
    librsvg
    pngpaste
    watch
    gnupg
    (aspellWithDicts (dicts: with dicts; [ en en-computers es ]))
  ];

  homebrew = {
    enable = true;
    # when removing a brew application from this file it should
    # deleted from system as well
    onActivation = {
      cleanup = "zap";
      # Homebrew 4.5+ (merged 2026-05-29) requires --force-cleanup for
      # non-interactive `brew bundle install --cleanup`.
      # https://github.com/Homebrew/brew/pull/22453
      extraFlags = [ "--force-cleanup" ];
    };
    brews = [
      # libvterm is not available in nix for aarch64 so we
      # install it from homebrew
      "libvterm"
      # this is required for mise be able to install erlang,
      # mise cannot identify openssl version installed in nix so
      # we need to use homebrew version to be able to use erlang
      # with no issues
      "openssl@3"
      "lorem"
    ];
    casks = [
      "macvim-app"
      "hammerspoon"
      "espanso"
      "homerow"
      "localsend"
      "postgres-app"
      "tailscale-app"
      "the-unarchiver"
      "keepingyouawake"
      "reflex-app"
      "handy"
    ];
  };

  # we need to specify primary user since nix-darwin 25.05
  system.primaryUser = "erick";

  # all available options are defined here https://daiderd.com/nix-darwin/manual/index.html
  system = {
    defaults = {
      dock.autohide = true;
      # Don't arrange spaces based on most recent use
      dock.mru-spaces = false;
      dock.orientation = "left";
      dock.showAppExposeGestureEnabled = true;
      finder.AppleShowAllExtensions = true;
      NSGlobalDomain.InitialKeyRepeat = 14;
      NSGlobalDomain.KeyRepeat = 2;
      NSGlobalDomain.AppleInterfaceStyle = "Dark";
      # use fn to access to media keys
      NSGlobalDomain."com.apple.keyboard.fnState" = true;
      # disable natural scrolling
      NSGlobalDomain."com.apple.swipescrolldirection" = false;
      # Use scroll gesture with the Ctrl (^) modifier key to
      # zoom, this requires to have "Full disk access" in the
      # program which run nix-darwin command
      universalaccess.closeViewScrollWheelToggle = true;
      trackpad = {
        Clicking = true;
        TrackpadRightClick = true;
        TrackpadThreeFingerVertSwipeGesture = 2;
      };
      controlcenter.Sound = true;
      controlcenter.Bluetooth = true;
      controlcenter.NowPlaying = false;
    };
    keyboard = {
      enableKeyMapping = true;
      remapCapsLockToEscape = true;
    };
  };

  system.defaults.CustomUserPreferences = {
    "com.apple.finder" = {
      ShowExternalHardDrivesOnDesktop = true;
      ShowHardDrivesOnDesktop = true;
      ShowMountedServersOnDesktop = true;
      ShowRemovableMediaOnDesktop = true;
      # When performing a search, search the current folder by default
      FXDefaultSearchScope = "SCcf";
      DisableAllAnimations = true;
      NewWindowTarget = "PfHm";
      NewWindowTargetPath = "file://$\{HOME\}/";
      AppleShowAllExtensions = true;
      FXEnableExtensionChangeWarning = false;
      ShowStatusBar = true;
      ShowPathbar = true;
      WarnOnEmptyTrash = false;
    };
    "com.apple.Safari" = {
      # Privacy: don’t send search queries to Apple
      UniversalSearchEnabled = false;
      SuppressSearchSuggestions = true;
      # Prevent Safari from opening ‘safe’ files automatically after downloading
      AutoOpenSafeDownloads = false;
      AutoFillFromAddressBook = false;
      AutoFillCreditCardData = false;
    };
    "com.apple.print.PrintingPrefs" = {
      # Automatically quit printer app once the print jobs complete
      "Quit When Finished" = true;
    };
  };

  # link nix-apps into /Applications to be indexed by spotlight
  system.activationScripts.applications.text =
    let
      env = pkgs.buildEnv {
        name = "system-applications";
        paths = config.environment.systemPackages;
        pathsToLink = [ "/Applications" ];
      };

    in
    pkgs.lib.mkForce ''
      rm -fr /Applications/Nix\ Apps
      mkdir -p /Applications/Nix\ Apps
      find ${env}/Applications -maxdepth 1 -type l -exec readlink '{}' + |
      while read -r src; do
        app_name=$(basename "$src")
        echo "copying $src" >&2
        ${pkgs.mkalias}/bin/mkalias "$src" "/Applications/Nix Apps/$app_name"
      done
    '';

  # Auto upgrade nix package and the daemon service.
  # nix.package = pkgs.nix;

  # Necessary for using flakes on this system.
  nix.settings.experimental-features = "nix-command flakes";

  # Create /etc/zshrc that loads the nix-darwin environment.
  programs.zsh.enable = true; # default shell on catalina
  # programs.fish.enable = true;

  # # Set Git commit hash for darwin-version.
  # system.configurationRevision = self.rev or self.dirtyRev or null;

  # Used for backwards compatibility, please read the changelog before changing.
  # $ darwin-rebuild changelog
  system.stateVersion = 5;

  # The platform the configuration will be used on.
  nixpkgs.hostPlatform = "aarch64-darwin";
}

flake.nix

nix-darwin/flake.nix · 35 lines

{
  description = "Darwin system flake";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";

    nix-darwin.url = "github:nix-darwin/nix-darwin/master";
    nix-darwin.inputs.nixpkgs.follows = "nixpkgs";

    emacs-src = {
      url = "github:emacs-mirror/emacs/aec7a5c73f80560373d964f0f9a03d1b9efe49d3";
      flake = false;
    };
  };

  outputs = inputs@{ self, nix-darwin, nixpkgs, emacs-src }:
    {
      # Build darwin flake using:
      # $ darwin-rebuild build --flake .#simple
      darwinConfigurations."simple" = nix-darwin.lib.darwinSystem {
        modules = [
          ./common.nix
          ./personal.nix
          {
            # Set Git commit hash for darwin-version.
            system.configurationRevision = self.rev or self.dirtyRev or null;
          }
        ];
        specialArgs = { inherit emacs-src; };
      };

      # Expose the package set, including overlays, for convenience.
      darwinPackages = self.darwinConfigurations."simple".pkgs;
    };
}

personal.nix

nix-darwin/personal.nix · 64 lines

{ pkgs, config, ... }: {
  environment.systemPackages = with pkgs; [
    xcodegen
    swift-format
    mutagen
    ffmpeg
  ];

  homebrew = {
    brews = [
      "llama.cpp"
      "odin"
      "ols"
    ];
    casks = [
      "krisp"
      "spotify"
      "alfred"
      "1password@7"
      "obs"
      "claude"
      "telegram"
      "firefox"
      "vlc"
      "utm"
      "brave-browser"
      "protonvpn"
      "signal"
      "freecad"
      "orcaslicer"
      "blender@lts"
      "imageoptim"
      "discord"
      "hiddenbar"
      "loopback"
      { "name" = "guanchzhou/tap/threemf"; trusted = true; }
      { "name" = "ruaylabs/tap/melocoton"; trusted = true; }
      "yubico-authenticator"
      "steam"
    ];
    masApps = {
      "Keynote" = 409183694;
      "Numbers" = 409203825;
      "Pages" = 409201541;
      "Pixelmator Pro" = 1289583905;
      "Things 3" = 904280696;
      "Xcode" = 497799835;
    };
  };

  # all available options are defined here https://daiderd.com/nix-darwin/manual/index.html
  system = {
    defaults = {
      dock.persistent-apps = [
        "/System/Applications/Mail.app"
        "/System/Cryptexes/App/System/Applications/Safari.app"
        "/System/Applications/Messages.app"
        "/System/Applications/Notes.app"
        "/Applications/MacVim.app"
        "${pkgs.emacs}/Applications/Emacs.app"
      ];
    };
  };
}

README.md

nix-darwin/README.md · 22 lines

1. Install upstream `nix` from `DeterminateSystems`:

```shell
curl -fsSL https://install.determinate.systems/nix | sh -s -- install
```

2. Make build build, inside `nix-darwin` folder:

> Make sure terminal.app have permissions to access full disk, this is required because we need to modify some system
> preferences

```shell
nix --extra-experimental-features 'nix-command flakes' build ".#darwinConfigurations.simple.system"
```

3. Install build

```shell
sudo ./result/sw/bin/darwin-rebuild switch --flake ".#simple"
```

4. After this we can rebuild system by using this helper function `nixdarwnin_run_install`

work.nix

nix-darwin/work.nix · 38 lines

{ pkgs, config, ... }: {
  environment.systemPackages = with pkgs; [
    lima
    mutagen
    (azure-cli.withExtensions [ azure-cli.extensions.aks-preview ])
  ];

  homebrew = {
    taps = [
      "Azure/kubelogin"
    ];
    brews = [
      "Azure/kubelogin/kubelogin"
    ];
    casks = [
      "1password"
      "brave-browser"
      "claude"
      "slack"
      "notion"
      "1password-cli"
      "maccy"
    ];
  };

  # all available options are defined here https://daiderd.com/nix-darwin/manual/index.html
  system = {
    defaults = {
      dock.persistent-apps = [
        "/System/Applications/Mail.app"
        "/Applications/Brave Browser.app/"
        "/System/Applications/Notes.app"
        "/Applications/MacVim.app"
        "${pkgs.emacs}/Applications/Emacs.app"
      ];
    };
  };
}

NixOS

configuration.nix

nixos/configuration.nix · 367 lines

# Edit this configuration file to define what should be installed on # your system.  Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running ‘nixos-help’).

{ config, pkgs, ... }:

{
  imports =
    [
      # Include the results of the hardware scan.
      /etc/nixos/hardware-configuration.nix
    ];

  nix.gc = {
    # Enable the automatic garbage collector
    automatic = true;
    # Remove generations older than two weeks once a week.
    dates = "weekly";
    options = "--delete-older-than 14d";
  };

  nix.settings.experimental-features = [ "nix-command" "flakes" ];

  # Bootloader.
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;
  # enable sound card
  boot.kernelParams = [ "snd-intel-dspcfg.dsp_driver=1" ];
  # clean tmp dir on restart
  boot.tmp.cleanOnBoot = true;

  networking.hostName = "nixos"; # Define your hostname.
  # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.

  # Configure network proxy if necessary
  # networking.proxy.default = "http://user:password@proxy:port/";
  # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";

  # Enable networking
  networking.networkmanager.enable = true;

  # Enable Bluetooth and the Blueman applet service.
  hardware.bluetooth.enable = true;
  services.blueman.enable = true;

  # Set your time zone.
  time.timeZone = "America/Mexico_City";

  # Select internationalisation properties.
  i18n.defaultLocale = "en_US.UTF-8";

  # Configure keymap in X11
  services.xserver = {
    xkb = {
      variant = "mac";
      layout = "us";
      # Use Left Alt for macOS-style accent sequences.
      options = "lv3:lalt_switch";
    };
  };

  # Remap caps lock to esc
  services.keyd = {
    enable = true;
    keyboards = {
      default = {
        ids = [ "*" ];
        settings = {
          main = {
            capslock = "esc";
            # Swap Meta and Alt for laptop keyboard layouts. This is not
            # required on desktop computers with a custom keyboard.
            # leftmeta = "layer(alt)";
            # leftalt = "layer(meta)";
          };
        };
      };
    };
  };

  services.displayManager.sddm.wayland.enable = true;
  services.displayManager.sddm.enable = true;
  services.displayManager.sddm.package = pkgs.kdePackages.sddm;
  services.displayManager.sddm.theme = "sddm-astronaut-theme";
  services.displayManager.sddm.extraPackages = with pkgs.kdePackages; [
    qtmultimedia
  ];

  # enable keyring on unlock
  security.pam.services.sddm.enableGnomeKeyring = true;
  security.pam.services."swaylock-plugin" = { };
  security.pam.services.wayvnc = { };
  # Give PipeWire realtime priority for low-latency, stable audio.
  security.rtkit.enable = true;

  services.pulseaudio.enable = false;
  services.pipewire = {
    enable = true;
    alsa.enable = true;
    alsa.support32Bit = true;
    pulse.enable = true;
    # Manage PipeWire devices, routing, and audio policy.
    wireplumber.enable = true;
  };

  # Define a user account. Don't forget to set a password with ‘passwd’.
  users.users.erick = {
    isNormalUser = true;
    description = "Erick Navarro";
    extraGroups = [ "networkmanager" "wheel" "docker" "libvirtd" ];
    packages = with pkgs; [ ];
  };

  programs.zsh.enable = true;
  programs.ssh.startAgent = true;
  services.gnome.gcr-ssh-agent.enable = false;

  users.users.erick.shell = pkgs.zsh;

  fonts.packages = with pkgs; [
    jetbrains-mono
    nerd-fonts.jetbrains-mono
    nerd-fonts.symbols-only
    noto-fonts-color-emoji
  ];

  # Allow unfree packages
  nixpkgs.config.allowUnfree = true;

  programs.sway = {
    enable = true;
    wrapperFeatures.gtk = true;
    extraPackages = with pkgs; [
      swayidle
      waybar
    ];
  };

  xdg.portal = {
    enable = true;
    wlr = {
      enable = true;
      # OBS requires an explicit output chooser for PipeWire screen capture.
      settings.screencast = {
        chooser_type = "simple";
        chooser_cmd = "${pkgs.slurp}/bin/slurp -f 'Monitor: %o' -or";
      };
    };
    extraPortals = [ pkgs.xdg-desktop-portal-wlr ];
  };

  services.dbus.enable = true;

  # Enable the gnome-keyring secrets vault.
  # Will be exposed through DBus to programs willing to store secrets.
  services.gnome.gnome-keyring.enable = true;

  programs.dconf = {
    enable = true;
    profiles.user.databases = [
      {
        settings."org/gnome/desktop/interface" = {
          color-scheme = "prefer-dark";
          gtk-theme = "adw-gtk3-dark";
          icon-theme = "Papirus-Dark";
        };
      }
    ];
  };

  virtualisation.docker.enable = true;
  virtualisation.libvirtd.enable = true;
  programs.virt-manager.enable = true;

  # Rotate wallpapers while the graphical session is active.
  systemd.user.services.wallpaper-rotate = {
    description = "Select the next desktop wallpaper";
    after = [ "graphical-session.target" ];
    path = with pkgs; [
      coreutils
      file
      findutils
      procps
      sway
      systemd
    ];
    serviceConfig = {
      Type = "oneshot";
      ExecStart = "${pkgs.bash}/bin/bash %h/.config/sway/scripts/wallpaper.sh";
    };
  };

  systemd.user.timers.wallpaper-rotate = {
    description = "Rotate the desktop wallpaper every 30 minutes";
    wantedBy = [ "graphical-session.target" ];
    partOf = [ "graphical-session.target" ];
    timerConfig = {
      OnActiveSec = "1s";
      OnUnitActiveSec = "30m";
      Unit = "wallpaper-rotate.service";
    };
  };

  # Keep video wallpaper playback under systemd supervision.
  systemd.user.services.wallpaper-video = {
    description = "Video desktop wallpaper";
    after = [ "graphical-session.target" ];
    partOf = [ "graphical-session.target" ];
    serviceConfig = {
      ExecStart = ''${pkgs.mpvpaper}/bin/mpvpaper -o "hwdec=vaapi no-audio loop" "*" %h/.local/state/wallpaper/current-video'';
      Restart = "on-failure";
      RestartSec = 2;
    };
  };

  environment.etc."wayvnc/config".text = ''
    enable_auth=true
    enable_pam=true
  '';

  # Waybar starts and stops this service on demand.
  systemd.user.services.wayvnc = {
    description = "WayVNC remote desktop server";
    after = [ "graphical-session.target" ];
    partOf = [ "graphical-session.target" ];
    environment.WAYLAND_DISPLAY = "wayland-1";
    serviceConfig = {
      # Port 5900 is reserved by libvirt/QEMU's local VNC display.
      ExecStart = "${pkgs.wayvnc}/bin/wayvnc -C /etc/wayvnc/config -Ldebug 0.0.0.0 5901";
      Restart = "on-failure";
      RestartSec = 2;
    };
  };

  environment.systemPackages = with pkgs; [
    pamixer
    i3lock-fancy
    polybar
    # available theme config file are inside
    # /run/current-system/sw/share/sddm/themes/sddm-astronaut-theme/Themes
    (sddm-astronaut.overrideAttrs (oldAttrs: {
      postInstall = (oldAttrs.postInstall or "") + ''
        substituteInPlace $out/share/sddm/themes/sddm-astronaut-theme/metadata.desktop \
          --replace "ConfigFile=Themes/astronaut.conf" "ConfigFile=Themes/pixel_sakura.conf"
      '';
    }))
    jq
    file # detect image and video wallpaper types
    bc # required for eww/net.sh calculations
    nixpkgs-fmt
    wget
    vim
    rofi
    rofimoji
    wayvnc # enable remote control
    pavucontrol
    killall
    ripgrep
    mutagen
    aspell
    aspellDicts.en
    aspellDicts.es
    hunspell
    hunspellDicts.en_US
    tree-sitter
    fzf
    shfmt
    shellcheck
    nautilus
    sushi # allow show previews in nautilus when pressing space
    tectonic
    wayland
    wlogout
    wl-clipboard
    wtype # sends paste shortcuts for the Sway clipboard menu
    glib
    adw-gtk3
    papirus-icon-theme
    nwg-look
    networkmanagerapplet
    swaylock-effects
    swaylock-plugin
    eww
    mpvpaper # for live wallpapers
    nerdfetch
    fastfetch
    # image annotation tool
    satty
    hyprshot
    playerctl # to know what song is being played
    swappy
    grim
    slurp
    cliphist
    telegram-desktop
    firefox
    brave
    alacritty
    tmux
    emacs31-gtk3
    localsend
    mise
    bat
    eza
    unzip
    git
    delta
    gtk3
    zathura
    gnumake
    gcc
    cmake
    libtool
    libvterm
    clang-tools
    ruff
    pyright
    zig
    zls
    nixd
    biome
    brightnessctl
    wlsunset
    libnotify
    swaynotificationcenter
    spotify
    tree
    btop
    htop
    obs-studio
    go
    gopls # LSP server
    gotools # includes goimports
    autotiling
  ];

  # Some programs need SUID wrappers, can be configured further or are
  # started in user sessions.
  # programs.mtr.enable = true;
  # programs.gnupg.agent = { #   enable = true; #   enableSSHSupport = true;
  # };

  # List services that you want to enable:

  # Enable the OpenSSH daemon.
  services.openssh.enable = true;

  # Enable tailscale
  services.tailscale.enable = true;

  networking.firewall = {
    enable = true;
    # Keep Tailscale exit nodes working with reverse-path filtering enabled.
    checkReversePath = "loose";
    # Allow direct Tailscale peer connections.
    allowedUDPPorts = [ config.services.tailscale.port ];
    # WayVNC listens globally but is reachable only through Tailscale.
    interfaces.${config.services.tailscale.interfaceName}.allowedTCPPorts = [ 5901 ];
  };

  # This value determines the NixOS release from which the default
  # settings for stateful data, like file locations and database versions
  # on your system were taken. It‘s perfectly fine and recommended to leave
  # this value at the release version of the first install of this system.
  # Before changing this value read the documentation for this option
  # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
  system.stateVersion = "24.05"; # Did you read the comment?
}

flake.nix

nixos/flake.nix · 14 lines

{
  description = "NixOS flake";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
  };

  outputs = { self, nixpkgs }: {
    nixosConfigurations.nixos = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [ ./configuration.nix ];
    };
  };
}

Pi

AGENTS.md

.pi/agent/AGENTS.md · 34 lines

## Interactions

- If the user asks a question, do not apply any changes — just answer.
- After every batch of edits, show a diff-stat table before any explanation:
  - One line per changed file: relative path, pipe, visual bar of `+`/`-` proportional to
    lines added/removed (roughly 1 char per changed line).
  - Include new files.
  - Show lines added and deleted counters
  - Example:
    ```
    src/lib/foo.ts      | 12 +++++++-----
    src/lib/bar.svelte  | 36 +++++++++++++++++++++++++++++
    src/lib/baz.svelte  | 88 ------------
    ```
- Use conventional commits(with scope), unless the project defines its own convention.

## Session patterns that worked well

- **Work in improvement lists.** After a feature is done, propose a numbered list of follow-up
  improvements. Let the user pick which to implement and in what order.
- **Don't abstract prematurely.** Only extract shared code/styles when there are 3+ consumers. If
  there's just one, leave it in place — it can always be extracted later.
- **Always verify after changes.** Run the project's type-check/lint command after every edit batch
  without waiting to be asked.
- **Keep commits atomic.** One change = one commit. If the user asks for a commit message, keep it
  short (≤50 chars summary line).
- **Revert fast.** If an extraction turns out to be wrong ("this is only used in one component"),
revert it immediately rather than defending it.

## Language / framework conventions

Language-specific conventions live in skill files under `~/.pi/agent/skills/`.
A `lang-directives` extension auto-detects relevant skills from the prompt and
reminds you to load them.

lang-directives.ts

.pi/agent/extensions/lang-directives.ts · 162 lines

/**
 * Lang Directives Extension
 *
 * Reads lang-directives.json which maps path and word triggers to skill names.
 * At before_agent_start, scans the user's prompt for matching triggers and
 * reminds the model to read the relevant skill files.
 *
 * The skill files and their paths come from pi's own skill loader
 * (event.systemPromptOptions.skills); this extension only owns the
 * trigger->name mapping. Keep directive content in skill files under
 * ~/.pi/agent/skills/<name>/SKILL.md, not here.
 */

import {
  CONFIG_DIR_NAME,
  type ExtensionAPI,
} from "@earendil-works/pi-coding-agent";
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { homedir } from "node:os";

interface SkillEntry {
  name: string;
  paths: string[];
  words: string[];
}

interface Config {
  skills: SkillEntry[];
}

const HOME = homedir();
const GLOBAL_CONFIG = join(HOME, ".pi/agent/lang-directives.json");

/** Path triggers match literal substrings; word triggers require word boundaries. */
type MatchMode = "path" | "word";
type Matcher = (promptLower: string) => boolean;

function buildMatcher(value: string, mode: MatchMode): Matcher {
  const key = value.toLowerCase();
  if (mode === "path") return (prompt) => prompt.includes(key);

  const re = new RegExp(
    `(?<![\\p{L}\\p{N}_])${escapeRegex(key)}(?![\\p{L}\\p{N}_])`,
    "u",
  );
  return (prompt) => re.test(prompt);
}

function escapeRegex(s: string): string {
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

class DirectiveIndex {
  /** keyword -> matcher */
  private matchers = new Map<string, Matcher>();
  /** keyword -> skill names */
  private names = new Map<string, Set<string>>();

  isEmpty(): boolean {
    return this.matchers.size === 0;
  }

  /** Rebuild from one or more configs (later configs add to, not replace). */
  rebuild(configs: Config[]): void {
    this.matchers.clear();
    this.names.clear();
    for (const config of configs) {
      for (const skill of config.skills) {
        if (!skill.name) continue;
        const groups: Array<[MatchMode, string[]]> = [
          ["path", skill.paths],
          ["word", skill.words],
        ];
        for (const [mode, values] of groups) {
          if (!Array.isArray(values)) continue;
          for (const value of values) {
            if (!value) continue;
            const key = `${mode}:${value.toLowerCase()}`;
            if (!this.matchers.has(key)) {
              this.matchers.set(key, buildMatcher(value, mode));
            }
            const set = this.names.get(key) ?? new Set<string>();
            set.add(skill.name);
            this.names.set(key, set);
          }
        }
      }
    }
  }

  /** Return de-duplicated skill names whose keywords appear in the prompt. */
  matchedSkillNames(prompt: string): string[] {
    const lower = prompt.toLowerCase();
    const result = new Set<string>();
    for (const [kw, matcher] of this.matchers) {
      if (matcher(lower)) {
        for (const name of this.names.get(kw) ?? []) result.add(name);
      }
    }
    return [...result];
  }
}

function loadConfig(filePath: string): Config | null {
  try {
    if (!existsSync(filePath)) return null;
    const config = JSON.parse(readFileSync(filePath, "utf-8")) as Config;
    return config.skills?.length ? config : null;
  } catch {
    return null;
  }
}

export default function (pi: ExtensionAPI) {
  const index = new DirectiveIndex();
  let lastCwd = "";

  function rebuild(cwd: string) {
    if (cwd === lastCwd && !index.isEmpty()) return; // cheap no-op re-entry
    const configs: Config[] = [];
    const global = loadConfig(GLOBAL_CONFIG);
    if (global) configs.push(global);
    const project = loadConfig(
      join(cwd, CONFIG_DIR_NAME, "lang-directives.json"),
    );
    if (project) configs.push(project);
    index.rebuild(configs);
    lastCwd = cwd;
  }

  pi.on("session_start", async (_event, ctx) => rebuild(ctx.cwd));
  pi.on("resources_discover", async (_event, ctx) => rebuild(ctx.cwd));

  pi.on("before_agent_start", async (event) => {
    if (index.isEmpty()) return;
    const wanted = index.matchedSkillNames(event.prompt);
    if (wanted.length === 0) return;

    // Resolve against pi's already-loaded skills so paths/discovery stay in
    // sync with the rest of the system. Skills flagged
    // disableModelInvocation are hidden from the model by pi itself; skip
    // them here too so we never remind the model of an un-invokable skill.
    const loaded = event.systemPromptOptions.skills ?? [];
    const byName = new Map(loaded.map((s) => [s.name, s]));
    const out = new Set<string>();
    for (const name of wanted) {
      const skill = byName.get(name);
      if (skill && !skill.disableModelInvocation) {
        out.add(`- \`${skill.filePath}\` — \`read\` for ${name} conventions`);
      }
    }
    if (out.size === 0) return;

    const reminder =
      `\n\n## Relevant conventions\n` +
      `Read these before writing or reviewing code:\n` +
      [...out].join("\n");

    return { systemPrompt: event.systemPrompt + reminder };
  });
}

prompt-prefix.ts

.pi/agent/extensions/prompt-prefix.ts · 36 lines

/**
 * Prompt Prefix Extension - Shows ❯ before text in the editor, like a shell prompt.
 *
 * Place in ~/.pi/agent/extensions/ for auto-discovery.
 */

import {
  CustomEditor,
  type ExtensionAPI,
} from "@earendil-works/pi-coding-agent";
import { truncateToWidth } from "@earendil-works/pi-tui";

class PromptEditor extends CustomEditor {
  render(width: number): string[] {
    const lines = super.render(width);
    if (lines.length < 3) return lines;

    // Only show ❯ when the actual first line of text is visible (not scrolled)
    if (this.scrollOffset > 0) return lines;

    // First content line is at index 1 (after the top border)
    const prompt = this.borderColor("❯ ");
    // Consume 2 columns from the right (padding) so total width stays within limits
    lines[1] = prompt + truncateToWidth(lines[1]!, Math.max(0, width - 2), "");

    return lines;
  }
}

export default function (pi: ExtensionAPI) {
  pi.on("session_start", (_event, ctx) => {
    ctx.ui.setEditorComponent(
      (tui, theme, kb) => new PromptEditor(tui, theme, kb),
    );
  });
}

lang-directives.json

.pi/agent/lang-directives.json · 24 lines

{
  "skills": [
    {
      "name": "rust",
      "paths": [
        ".rs",
        "Cargo.toml",
        "Cargo.lock",
        "build.rs",
        "rust-toolchain.toml",
        "rustfmt.toml",
        "clippy.toml",
        ".cargo/"
      ],
      "words": [
        "rust",
        "rustc",
        "rustup",
        "rust-analyzer",
        "cargo"
      ]
    }
  ]
}

SKILL.md

.pi/agent/skills/rust/SKILL.md · 13 lines

---
name: rust
description: General Rust conventions and best practices. Use when writing or reviewing Rust code.
---

# Rust Conventions

- Run `cargo fmt --check`, `cargo check`, and `cargo clippy` after edits.
- Run `cargo test` after logic changes.
- Prefer clear ownership and borrowing over unnecessary cloning.
- Use `Result` and `?` for recoverable errors.
- Avoid `unwrap()` and `expect()` outside tests or proven invariants.
- Use `anyhow` for application errors and `thiserror` for typed library errors.

tomorrow.json

.pi/agent/themes/tomorrow.json · 78 lines

{
  "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
  "name": "tomorrow",
  "vars": {
    "bg": "#000000",
    "currentLine": "#2a2a2a",
    "selection": "#424242",
    "fg": "#d6d6d6",
    "comment": "#969896",
    "red": "#d54e53",
    "orange": "#e78c45",
    "yellow": "#e7c547",
    "green": "#b9ca4a",
    "aqua": "#70c0b1",
    "blue": "#7aa6da",
    "purple": "#c397d8"
  },
  "colors": {
    "accent": "blue",
    "border": "#4a4a4a",
    "borderAccent": "blue",
    "borderMuted": "#424242",
    "success": "green",
    "error": "red",
    "warning": "orange",
    "muted": "comment",
    "dim": "#707070",
    "text": "fg",
    "thinkingText": "comment",
    "selectedBg": "selection",
    "scrollbarThumb": "#4a4a4a",
    "userMessageBg": "currentLine",
    "userMessageText": "fg",
    "customMessageBg": "currentLine",
    "customMessageText": "fg",
    "customMessageLabel": "blue",
    "toolPendingBg": "#1a1a1a",
    "toolSuccessBg": "#1a2a1a",
    "toolErrorBg": "#2a1a1a",
    "toolTitle": "fg",
    "toolOutput": "fg",
    "mdHeading": "purple",
    "mdLink": "blue",
    "mdLinkUrl": "comment",
    "mdCode": "orange",
    "mdCodeBlock": "fg",
    "mdCodeBlockBorder": "#4a4a4a",
    "mdQuote": "comment",
    "mdQuoteBorder": "#424242",
    "mdHr": "#424242",
    "mdListBullet": "aqua",
    "toolDiffAdded": "green",
    "toolDiffRemoved": "red",
    "toolDiffContext": "fg",
    "syntaxComment": "comment",
    "syntaxKeyword": "purple",
    "syntaxFunction": "blue",
    "syntaxVariable": "red",
    "syntaxString": "green",
    "syntaxNumber": "orange",
    "syntaxType": "yellow",
    "syntaxOperator": "fg",
    "syntaxPunctuation": "comment",
    "thinkingOff": "#4a4a4a",
    "thinkingMinimal": "blue",
    "thinkingLow": "purple",
    "thinkingMedium": "aqua",
    "thinkingHigh": "yellow",
    "thinkingXhigh": "red",
    "thinkingMax": "red",
    "bashMode": "yellow"
  },
  "export": {
    "pageBg": "#000000",
    "cardBg": "#2a2a2a",
    "infoBg": "#424242"
  }
}

Polybar

config.ini

polybar/config.ini · 116 lines

[colors]
background = #282A2E
background-alt = #373B41
foreground = #C5C8C6
primary = #F0C674
secondary = #8ABEB7
alert = #A54242
disabled = #707880

[bar/home]
width = 100%
height = 24pt
radius = 3
background = ${colors.background}
foreground = ${colors.foreground}
line-size = 5pt
border-size = 5px
border-color = #00000000
padding-left = 0
padding-right = 1
module-margin = 1
separator = |
separator-foreground = ${colors.disabled}
font-0 = monospace;2
modules-left = xworkspaces xwindow
modules-right = filesystem pulseaudio memory cpu wlan eth battery date
cursor-click = pointer
cursor-scroll = ns-resize
enable-ipc = true

[module/xworkspaces]
type = internal/xworkspaces
label-active = %name%
label-active-background = ${colors.background-alt}
label-active-underline= ${colors.primary}
label-active-padding = 1
label-occupied = %name%
label-occupied-padding = 1
label-urgent = %name%
label-urgent-background = ${colors.alert}
label-urgent-padding = 1
label-empty = %name%
label-empty-foreground = ${colors.disabled}
label-empty-padding = 1

[module/xwindow]
type = internal/xwindow
label = %title:0:60:...%

[module/filesystem]
type = internal/fs
interval = 25
mount-0 = /
label-mounted = %{F#F0C674}%mountpoint%%{F-} %percentage_used%%
label-unmounted = %mountpoint% not mounted
label-unmounted-foreground = ${colors.disabled}

[module/pulseaudio]
type = internal/pulseaudio
format-volume-prefix = "VOL "
format-volume-prefix-foreground = ${colors.primary}
format-volume = <label-volume>
label-volume = %percentage%%
label-muted = muted
label-muted-foreground = ${colors.disabled}

[module/memory]
type = internal/memory
interval = 2
format-prefix = "RAM "
format-prefix-foreground = ${colors.primary}
label = %percentage_used:2%%

[module/cpu]
type = internal/cpu
interval = 2
format-prefix = "CPU "
format-prefix-foreground = ${colors.primary}
label = %percentage:2%%

[network-base]
type = internal/network
interval = 5
format-connected = <label-connected>
format-disconnected = <label-disconnected>
label-disconnected = %{F#F0C674}%ifname%%{F#707880} disconnected

[module/wlan]
inherit = network-base
interface-type = wireless
label-connected = %{F#F0C674}%ifname%%{F-} %essid% %local_ip%

[module/eth]
inherit = network-base
interface-type = wired
label-connected = %{F#F0C674}%ifname%%{F-} %local_ip%

[module/date]
type = internal/date
interval = 1
date= %Y-%m-%d %H:%M:%S
label = %date%
label-foreground = ${colors.primary}

[module/battery]
type = internal/battery
# in case for some reason battery never gets 100%
full-at = 99
low-at = 10
battery = BAT10
adapter = ADP1
poll-interval = 5

[settings]
screenchange-reload = true
pseudo-transparency = true

launch.sh

polybar/launch.sh · 5 lines

#!/usr/bin/env bash
set -euo pipefail

killall -q polybar
polybar -c ~/.config/polybar/config.ini

Rofi

config.rasi

rofi/config.rasi · 91 lines

configuration {
  modi: "drun,run,window";
  show-icons: true;
  display-drun: "Applications";
  display-run: "Run";
  display-window: "Windows";
}

* {
  background: rgba(15, 15, 15, 92%);
  surface: rgba(255, 255, 255, 6%);
  surface-hover: rgba(126, 186, 228, 12%);
  border-color: rgba(255, 255, 255, 12%);
  foreground: #eeeeee;
  muted: rgba(255, 255, 255, 55%);
  accent: #7ebae4;
  critical: #f53c3c;
  background-color: transparent;
  text-color: @foreground;
  font: "JetBrainsMono Nerd Font 12";
}

window {
  width: 600px;
  padding: 12px;
  border: 1px;
  border-color: @border-color;
  border-radius: 10px;
  background-color: @background;
}

mainbox {
  spacing: 10px;
}

inputbar {
  padding: 10px 12px;
  spacing: 10px;
  border-radius: 8px;
  background-color: @surface;
}

prompt {
  text-color: @accent;
}

entry {
  placeholder: "Search…";
  placeholder-color: @muted;
}

listview {
  lines: 8;
  columns: 1;
  spacing: 4px;
  scrollbar: false;
  fixed-height: false;
}

element {
  padding: 8px 10px;
  spacing: 10px;
  border-radius: 8px;
  background-color: transparent;
}

element normal normal,
element alternate normal {
  background-color: transparent;
  text-color: @foreground;
}

element selected normal {
  background-color: @surface-hover;
  text-color: @accent;
}

element urgent normal {
  text-color: @critical;
}

element-icon {
  size: 24px;
  background-color: transparent;
}

element-text {
  vertical-align: 0.5;
  background-color: transparent;
  text-color: inherit;
}

Scripts

build_mac.sh

scripts/build_mac.sh · 63 lines

#!/usr/bin/env bash
set -euo pipefail

CWD=$(pwd)

if [ -z "$1" ]; then
  echo "Please provide the directory where emacs source is located"
  exit 1
fi

EMACS_REPO_DIR=$1

if [ ! -d "$EMACS_REPO_DIR" ]; then
  echo "$EMACS_REPO_DIR is not a valid directory"
  exit 1
fi

cd "$EMACS_REPO_DIR" || exit

# clean past build data
git clean -fdx

make clean

echo "Making sure all requirements are installed..."

# harfbuzz adds support for ligatures, this is required by ligature.el package
brew install autoconf automake libxml2 gnutls cmake librsvg texinfo libgccjit

echo "Seting up variables..."

make configure
./configure --with-ns --with-xwidgets --with-tree-sitter
make -j install

echo "Compilation done!"

echo "Move compiled application into Applications folder?"
read -r answer

# we ask for confirmation so we can check compilation stdout before replacing a working version of emacs
if [ "$answer" != "${answer#[Yy]}" ]; then
  echo "Compiled application was moved into Applications folder"
  # Copy app in Applications folder
  cp -rf nextstep/Emacs.app /Applications/

  # Set custom icon that fits Bug Sur design
  # copied from https://github.com/mklement0/fileicon/blob/master/bin/fileicon
  osascript <<EOF
    use framework "Cocoa"

    set sourcePath to "$CWD/Emacs.icns"
    set destPath to "/Applications/Emacs.app"

    set imageData to (current application's NSImage's alloc()'s initWithContentsOfFile:sourcePath)
    (current application's NSWorkspace's sharedWorkspace()'s setIcon:imageData forFile:destPath options:2)
EOF
else
  echo "Compiled application wasn't moved"
  exit 1
fi

cd - || exit

config-site.css

scripts/config-site.css · 91 lines

:root { color-scheme: dark; --bg: #111318; --panel: #191c23; --sidebar: #15171d; --muted: #969dab; --text: #e6e9ef; --accent: #8aadf4; --border: #2c313c; }
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { margin: 0; background: var(--bg); color: var(--text); font: 15px/1.6 system-ui, sans-serif; }
a { color: inherit; text-decoration: none; }

/* Dotfiles index */
aside { position: fixed; inset: 0 auto 0 0; width: 270px; padding: 28px 20px; overflow: auto; border-right: 1px solid var(--border); background: var(--sidebar); }
aside h1 { margin: 0; font-size: 1.4rem; }
aside > p { margin: 4px 0 20px; color: var(--muted); font-size: .85rem; }
nav a { display: flex; justify-content: space-between; padding: 7px 9px; border-radius: 6px; color: var(--muted); }
nav a:hover { color: var(--text); background: var(--panel); }
nav b { color: var(--accent); font-size: .75rem; }
.nav-toggle { display: none; width: 100%; margin-bottom: 10px; padding: 8px 10px; color: var(--text); border: 1px solid var(--border); border-radius: 6px; background: var(--panel); text-align: left; cursor: pointer; }
main { max-width: 1100px; margin-left: 270px; padding: 50px 5vw 100px; }
.intro h1 { margin: 0; font-size: clamp(2rem, 5vw, 4rem); letter-spacing: -.05em; }
.intro p { color: var(--muted); }
.group { padding-top: 28px; }
.group > h2 { margin: 0 0 14px; font-size: 1.5rem; }
.group > h2 small { margin-left: 8px; color: var(--accent); font-size: .75rem; font-weight: 500; }
.file { margin-bottom: 22px; overflow: hidden; border: 1px solid var(--border); border-radius: 10px; background: var(--panel); scroll-margin-top: 18px; }
.file-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid var(--border); }
.file-header h3, .file-header p { margin: 0; }
.file-header h3 { font: 600 .9rem ui-monospace, monospace; }
.file-header p { color: var(--muted); font-size: .75rem; }
.file-actions { display: flex; align-items: center; gap: 10px; }
.source-link { color: var(--accent); font-size: .75rem; white-space: nowrap; }
.source-link:hover { color: var(--text); }
.copy { padding: 5px 10px; color: var(--muted); border: 1px solid var(--border); border-radius: 6px; background: transparent; cursor: pointer; }
.copy:hover { color: var(--text); border-color: var(--accent); }
pre[class*="language-"] { max-height: 650px; margin: 0 !important; border-radius: 0; font-size: .8rem; }

/* Literate Emacs config */
#content { max-width: 1100px; margin-left: 270px; padding: 50px 5vw 100px; }
#content a { color: var(--accent); }
#content a:hover { color: var(--text); }
h1.title { margin: 0 0 32px; color: var(--text); font-size: clamp(2rem, 5vw, 4rem); letter-spacing: -.05em; text-align: left; }
.page-links { position: fixed; z-index: 2; top: 24px; left: 20px; display: flex; width: 230px; justify-content: space-between; font-size: .8rem; }
.page-links a { color: var(--accent); }
#table-of-contents { position: fixed; z-index: 1; inset: 0 auto 0 0; width: 270px; padding: 62px 20px 28px; overflow: auto; border-right: 1px solid var(--border); background: var(--sidebar); }
#table-of-contents h2 { margin: 0 0 14px; color: var(--text); font-size: 1.4rem; }
#text-table-of-contents ul { margin: 0; padding-left: 14px; list-style: none; }
#text-table-of-contents > ul { padding-left: 0; }
#text-table-of-contents a { display: block; padding: 4px 7px; border-radius: 6px; color: var(--muted); font-size: .82rem; line-height: 1.35; }
#text-table-of-contents a:hover { color: var(--text); background: var(--panel); }
.outline-2 { padding-top: 22px; }
h2 { margin: 0 0 14px; color: var(--text); font-size: 1.5rem; }
h3 { margin-top: 32px; color: var(--text); }
p, li { color: var(--text); }
.org-src-container { position: relative; }
.org-src-container > .copy { position: absolute; z-index: 1; top: 8px; right: 8px; background: var(--panel); }
pre.src, pre.example { max-height: 650px; margin: 16px 0; padding: 16px; overflow: auto; border: 1px solid var(--border); border-radius: 10px; background: var(--panel); color: var(--text); font: .8rem/1.5 ui-monospace, monospace; }
.org-comment, .org-comment-delimiter { color: #6c7086; font-style: italic; }
.org-keyword, .org-negation-char { color: #cba6f7; }
.org-string, .org-doc { color: #a6e3a1; }
.org-function-name { color: #89b4fa; }
.org-variable-name { color: #f5c2e7; }
.org-builtin, .org-constant, .org-type { color: #f9e2af; }
.org-regexp-grouping-backslash, .org-regexp-grouping-construct { color: #94e2d5; }
.org-warning { color: #f38ba8; font-weight: 600; }
code { padding: 2px 5px; border-radius: 4px; background: var(--panel); font-family: ui-monospace, monospace; }
pre code { padding: 0; background: transparent; }
table { border-collapse: collapse; }
th, td { padding: 8px 12px; border: 1px solid var(--border); }
#postamble { margin-left: 270px; padding: 20px 5vw; color: var(--muted); border-top: 1px solid var(--border); font-size: .75rem; }

@media (prefers-color-scheme: light) {
  :root { color-scheme: light; --bg: #f6f7f9; --panel: #ffffff; --sidebar: #eef1f5; --muted: #667085; --text: #1d2433; --accent: #315fa8; --border: #d8dee8; }
  .org-comment, .org-comment-delimiter { color: #6a737d; }
  .org-keyword, .org-negation-char { color: #8250df; }
  .org-string, .org-doc { color: #116329; }
  .org-function-name { color: #0550ae; }
  .org-variable-name { color: #953800; }
  .org-builtin, .org-constant, .org-type { color: #9a6700; }
  .org-regexp-grouping-backslash, .org-regexp-grouping-construct { color: #0a7c72; }
  .org-warning { color: #cf222e; }
}

@media (max-width: 760px) {
  aside { position: static; width: auto; border-right: 0; border-bottom: 1px solid var(--border); }
  .nav-toggle { display: block; }
  aside nav { display: none; }
  aside.nav-open nav { display: block; }
  main { margin: 0; padding: 30px 18px 70px; }
  .file-header { align-items: flex-start; gap: 8px; }
  .file-actions { flex-direction: column; align-items: flex-end; gap: 6px; }
  .page-links { position: static; width: auto; padding: 18px 18px 0; }
  #table-of-contents { position: static; width: auto; max-height: 45vh; padding: 18px; border-right: 0; border-bottom: 1px solid var(--border); }
  #content { margin: 0; padding: 30px 18px 70px; }
  #postamble { margin: 0; padding: 20px 18px; }
}

generate-config-site.py

scripts/generate-config-site.py · 378 lines

#!/usr/bin/env python3
"""Generate a single-page site from the repository's config files."""

import argparse
import fnmatch
import hashlib
import html
import os
import subprocess
import sys
import tempfile
from collections import defaultdict
from pathlib import Path
from urllib.parse import quote

ROOT = Path(__file__).resolve().parent.parent
EMACS_SOURCE = Path(".emacs.d/bootstrap.org")
EMACS_RENDERER = ROOT / "scripts" / "render-emacs-config-to-html.sh"
STYLESHEET = ROOT / "scripts" / "config-site.css"
MAX_FILE_BYTES = 1_000_000
DEFAULT_EXCLUDES = (
    ".DS_Store",
    "*.lock",
    "*.db",
    "*.elc",
    "*.png",
    "*.jpg",
    "*.jpeg",
    "*.gif",
    "*.webp",
    "*.icns",
    "*.woff*",
    "*.ttf",
    "*.pdf",
    ".env*",
    "*credentials*",
    "*secret*",
    "id_rsa*",
    "site/**",
    "scripts/output/**",
    "**/__pycache__/**",
)

LANGUAGES = {
    ".css": "css",
    ".el": "lisp",
    ".html": "markup",
    ".ini": "ini",
    ".json": "json",
    ".jsonc": "json",
    ".lua": "lua",
    ".md": "markdown",
    ".nix": "nix",
    ".org": "markup",
    ".rasi": "css",
    ".scss": "scss",
    ".sh": "bash",
    ".toml": "toml",
    ".xml": "markup",
    ".yaml": "yaml",
    ".yml": "yaml",
    ".yuck": "lisp",
}

SPECIAL_LANGUAGES = {
    "Brewfile": "ruby",
    ".gitconfig": "ini",
    ".ideavimrc": "vim",
    ".tmux.conf": "bash",
    ".vimrc": "vim",
    ".Xmodmap": "text",
}

GROUP_NAMES = {
    ".emacs.d": "Emacs",
    ".github": "GitHub",
    ".pi": "Pi",
    "nix-darwin": "Nix Darwin",
    "nixos": "NixOS",
    "zsh": "Zsh",
}


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "-o",
        "--output",
        type=Path,
        default=ROOT / "site" / "index.html",
        help="output HTML path (default: site/index.html)",
    )
    parser.add_argument(
        "--exclude",
        action="append",
        default=[],
        metavar="GLOB",
        help="additional repository-relative glob to exclude; repeatable",
    )
    return parser.parse_args()


def git_output(*arguments: str) -> str:
    try:
        result = subprocess.run(
            ["git", *arguments],
            cwd=ROOT,
            check=True,
            capture_output=True,
            text=True,
        )
    except FileNotFoundError as error:
        raise RuntimeError("Git is required to generate the site") from error
    except subprocess.CalledProcessError as error:
        detail = error.stderr.strip()
        raise RuntimeError(detail or f"git {' '.join(arguments)} failed") from error
    return result.stdout.strip()


def github_source_base() -> str:
    repository = os.environ.get("GITHUB_REPOSITORY")
    if repository is None:
        remote = git_output("remote", "get-url", "origin")
        marker = "github.com"
        if marker not in remote:
            raise RuntimeError("the origin remote is not hosted on GitHub")
        repository = remote.split(marker, 1)[1].lstrip(":/").removesuffix(".git")

    revision = os.environ.get("GITHUB_SHA") or git_output("rev-parse", "HEAD")
    return f"https://github.com/{repository}/blob/{revision}/"


def repository_files() -> list[Path]:
    """Return tracked files, failing closed outside a Git checkout."""
    try:
        result = subprocess.run(
            ["git", "ls-files", "-z"],
            cwd=ROOT,
            check=True,
            capture_output=True,
        )
    except FileNotFoundError as error:
        raise RuntimeError("Git is required to discover publishable files") from error
    except subprocess.CalledProcessError as error:
        detail = error.stderr.decode(errors="replace").strip()
        message = "not inside a Git repository"
        raise RuntimeError(f"could not list tracked files: {detail or message}") from error

    return [ROOT / os.fsdecode(item) for item in result.stdout.split(b"\0") if item]


def is_excluded(relative: Path, patterns: tuple[str, ...]) -> bool:
    name = relative.as_posix()
    return any(
        fnmatch.fnmatch(name, pattern) or fnmatch.fnmatch(relative.name, pattern)
        for pattern in patterns
    )


def read_text(path: Path) -> str | None:
    try:
        data = path.read_bytes()
    except OSError as error:
        print(f"warning: skipping {path}: {error}", file=sys.stderr)
        return None
    if len(data) > MAX_FILE_BYTES:
        print(f"warning: skipping {path}: larger than {MAX_FILE_BYTES:,} bytes", file=sys.stderr)
        return None
    if b"\0" in data:
        return None
    try:
        return data.decode("utf-8")
    except UnicodeDecodeError:
        return None


def group_name(relative: Path) -> str:
    if len(relative.parts) == 1:
        return "General"
    first = relative.parts[0]
    return GROUP_NAMES.get(first, first.lstrip(".").replace("-", " ").title())


def language_for(path: Path, source: str) -> str:
    if path.name in SPECIAL_LANGUAGES:
        return SPECIAL_LANGUAGES[path.name]
    if path.suffix in LANGUAGES:
        return LANGUAGES[path.suffix]
    if source.startswith("#!") and "sh" in source.splitlines()[0]:
        return "bash"
    return "text"


def file_id(relative: Path) -> str:
    path = relative.as_posix()
    slug = "".join(char if char.isalnum() else "-" for char in path).strip("-")
    digest = hashlib.sha256(path.encode()).hexdigest()[:10]
    return f"file-{slug}-{digest}"


def render_file(relative: Path, source: str, source_base: str) -> str:
    path = relative.as_posix()
    source_url = source_base + quote(path, safe="/")
    line_count = len(source.splitlines())
    language = language_for(relative, source)
    return f"""
<article class="file" id="{file_id(relative)}">
  <header class="file-header">
    <div>
      <h3>{html.escape(relative.name)}</h3>
      <p>{html.escape(path)} · {line_count:,} lines</p>
    </div>
    <div class="file-actions">
      <a class="source-link" href="{html.escape(source_url, quote=True)}">View on GitHub</a>
      <button class="copy" type="button" aria-label="Copy {html.escape(path, quote=True)}">Copy</button>
    </div>
  </header>
  <pre><code class="language-{language}">{html.escape(source)}</code></pre>
</article>"""


def render_site(
    files: list[tuple[Path, str]], stylesheet: str, source_base: str
) -> str:
    grouped: dict[str, list[tuple[Path, str]]] = defaultdict(list)
    for relative, source in files:
        grouped[group_name(relative)].append((relative, source))

    navigation = []
    sections = []
    for group in sorted(grouped, key=str.casefold):
        entries = sorted(grouped[group], key=lambda item: item[0].as_posix().casefold())
        group_id = "group-" + group.lower().replace(" ", "-")
        if group == "Emacs":
            navigation.append(
                '<a href="emacs.html"><span>Emacs guide</span><b>↗</b></a>'
            )
            heading = '<a href="emacs.html">Emacs <small>Open documented config →</small></a>'
        else:
            navigation.append(
                f'<a href="#{group_id}"><span>{html.escape(group)}</span><b>{len(entries)}</b></a>'
            )
            heading = html.escape(group)
        cards = "\n".join(
            render_file(path, source, source_base) for path, source in entries
        )
        sections.append(
            f'<section class="group" id="{group_id}"><h2>{heading}</h2>{cards}</section>'
        )

    return TEMPLATE.replace("{{NAVIGATION}}", "\n".join(navigation)).replace(
        "{{SECTIONS}}", "\n".join(sections)
    ).replace("105", str(len(files))).replace(
        "23", str(len(grouped))
    ).replace(":root { color-scheme: dark; --bg: #111318; --panel: #191c23; --sidebar: #15171d; --muted: #969dab; --text: #e6e9ef; --accent: #8aadf4; --border: #2c313c; }
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { margin: 0; background: var(--bg); color: var(--text); font: 15px/1.6 system-ui, sans-serif; }
a { color: inherit; text-decoration: none; }

/* Dotfiles index */
aside { position: fixed; inset: 0 auto 0 0; width: 270px; padding: 28px 20px; overflow: auto; border-right: 1px solid var(--border); background: var(--sidebar); }
aside h1 { margin: 0; font-size: 1.4rem; }
aside > p { margin: 4px 0 20px; color: var(--muted); font-size: .85rem; }
nav a { display: flex; justify-content: space-between; padding: 7px 9px; border-radius: 6px; color: var(--muted); }
nav a:hover { color: var(--text); background: var(--panel); }
nav b { color: var(--accent); font-size: .75rem; }
.nav-toggle { display: none; width: 100%; margin-bottom: 10px; padding: 8px 10px; color: var(--text); border: 1px solid var(--border); border-radius: 6px; background: var(--panel); text-align: left; cursor: pointer; }
main { max-width: 1100px; margin-left: 270px; padding: 50px 5vw 100px; }
.intro h1 { margin: 0; font-size: clamp(2rem, 5vw, 4rem); letter-spacing: -.05em; }
.intro p { color: var(--muted); }
.group { padding-top: 28px; }
.group > h2 { margin: 0 0 14px; font-size: 1.5rem; }
.group > h2 small { margin-left: 8px; color: var(--accent); font-size: .75rem; font-weight: 500; }
.file { margin-bottom: 22px; overflow: hidden; border: 1px solid var(--border); border-radius: 10px; background: var(--panel); scroll-margin-top: 18px; }
.file-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid var(--border); }
.file-header h3, .file-header p { margin: 0; }
.file-header h3 { font: 600 .9rem ui-monospace, monospace; }
.file-header p { color: var(--muted); font-size: .75rem; }
.file-actions { display: flex; align-items: center; gap: 10px; }
.source-link { color: var(--accent); font-size: .75rem; white-space: nowrap; }
.source-link:hover { color: var(--text); }
.copy { padding: 5px 10px; color: var(--muted); border: 1px solid var(--border); border-radius: 6px; background: transparent; cursor: pointer; }
.copy:hover { color: var(--text); border-color: var(--accent); }
pre[class*="language-"] { max-height: 650px; margin: 0 !important; border-radius: 0; font-size: .8rem; }

/* Literate Emacs config */
#content { max-width: 1100px; margin-left: 270px; padding: 50px 5vw 100px; }
#content a { color: var(--accent); }
#content a:hover { color: var(--text); }
h1.title { margin: 0 0 32px; color: var(--text); font-size: clamp(2rem, 5vw, 4rem); letter-spacing: -.05em; text-align: left; }
.page-links { position: fixed; z-index: 2; top: 24px; left: 20px; display: flex; width: 230px; justify-content: space-between; font-size: .8rem; }
.page-links a { color: var(--accent); }
#table-of-contents { position: fixed; z-index: 1; inset: 0 auto 0 0; width: 270px; padding: 62px 20px 28px; overflow: auto; border-right: 1px solid var(--border); background: var(--sidebar); }
#table-of-contents h2 { margin: 0 0 14px; color: var(--text); font-size: 1.4rem; }
#text-table-of-contents ul { margin: 0; padding-left: 14px; list-style: none; }
#text-table-of-contents > ul { padding-left: 0; }
#text-table-of-contents a { display: block; padding: 4px 7px; border-radius: 6px; color: var(--muted); font-size: .82rem; line-height: 1.35; }
#text-table-of-contents a:hover { color: var(--text); background: var(--panel); }
.outline-2 { padding-top: 22px; }
h2 { margin: 0 0 14px; color: var(--text); font-size: 1.5rem; }
h3 { margin-top: 32px; color: var(--text); }
p, li { color: var(--text); }
.org-src-container { position: relative; }
.org-src-container > .copy { position: absolute; z-index: 1; top: 8px; right: 8px; background: var(--panel); }
pre.src, pre.example { max-height: 650px; margin: 16px 0; padding: 16px; overflow: auto; border: 1px solid var(--border); border-radius: 10px; background: var(--panel); color: var(--text); font: .8rem/1.5 ui-monospace, monospace; }
.org-comment, .org-comment-delimiter { color: #6c7086; font-style: italic; }
.org-keyword, .org-negation-char { color: #cba6f7; }
.org-string, .org-doc { color: #a6e3a1; }
.org-function-name { color: #89b4fa; }
.org-variable-name { color: #f5c2e7; }
.org-builtin, .org-constant, .org-type { color: #f9e2af; }
.org-regexp-grouping-backslash, .org-regexp-grouping-construct { color: #94e2d5; }
.org-warning { color: #f38ba8; font-weight: 600; }
code { padding: 2px 5px; border-radius: 4px; background: var(--panel); font-family: ui-monospace, monospace; }
pre code { padding: 0; background: transparent; }
table { border-collapse: collapse; }
th, td { padding: 8px 12px; border: 1px solid var(--border); }
#postamble { margin-left: 270px; padding: 20px 5vw; color: var(--muted); border-top: 1px solid var(--border); font-size: .75rem; }

@media (prefers-color-scheme: light) {
  :root { color-scheme: light; --bg: #f6f7f9; --panel: #ffffff; --sidebar: #eef1f5; --muted: #667085; --text: #1d2433; --accent: #315fa8; --border: #d8dee8; }
  .org-comment, .org-comment-delimiter { color: #6a737d; }
  .org-keyword, .org-negation-char { color: #8250df; }
  .org-string, .org-doc { color: #116329; }
  .org-function-name { color: #0550ae; }
  .org-variable-name { color: #953800; }
  .org-builtin, .org-constant, .org-type { color: #9a6700; }
  .org-regexp-grouping-backslash, .org-regexp-grouping-construct { color: #0a7c72; }
  .org-warning { color: #cf222e; }
}

@media (max-width: 760px) {
  aside { position: static; width: auto; border-right: 0; border-bottom: 1px solid var(--border); }
  .nav-toggle { display: block; }
  aside nav { display: none; }
  aside.nav-open nav { display: block; }
  main { margin: 0; padding: 30px 18px 70px; }
  .file-header { align-items: flex-start; gap: 8px; }
  .file-actions { flex-direction: column; align-items: flex-end; gap: 6px; }
  .page-links { position: static; width: auto; padding: 18px 18px 0; }
  #table-of-contents { position: static; width: auto; max-height: 45vh; padding: 18px; border-right: 0; border-bottom: 1px solid var(--border); }
  #content { margin: 0; padding: 30px 18px 70px; }
  #postamble { margin: 0; padding: 20px 18px; }
}
", stylesheet)


TEMPLATE = """<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Dotfiles</title>
  <link rel="preconnect" href="https://cdnjs.cloudflare.com">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css" media="(prefers-color-scheme: light)">
  <style>
:root { color-scheme: dark; --bg: #111318; --panel: #191c23; --sidebar: #15171d; --muted: #969dab; --text: #e6e9ef; --accent: #8aadf4; --border: #2c313c; }
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { margin: 0; background: var(--bg); color: var(--text); font: 15px/1.6 system-ui, sans-serif; }
a { color: inherit; text-decoration: none; }

/* Dotfiles index */
aside { position: fixed; inset: 0 auto 0 0; width: 270px; padding: 28px 20px; overflow: auto; border-right: 1px solid var(--border); background: var(--sidebar); }
aside h1 { margin: 0; font-size: 1.4rem; }
aside > p { margin: 4px 0 20px; color: var(--muted); font-size: .85rem; }
nav a { display: flex; justify-content: space-between; padding: 7px 9px; border-radius: 6px; color: var(--muted); }
nav a:hover { color: var(--text); background: var(--panel); }
nav b { color: var(--accent); font-size: .75rem; }
.nav-toggle { display: none; width: 100%; margin-bottom: 10px; padding: 8px 10px; color: var(--text); border: 1px solid var(--border); border-radius: 6px; background: var(--panel); text-align: left; cursor: pointer; }
main { max-width: 1100px; margin-left: 270px; padding: 50px 5vw 100px; }
.intro h1 { margin: 0; font-size: clamp(2rem, 5vw, 4rem); letter-spacing: -.05em; }
.intro p { color: var(--muted); }
.group { padding-top: 28px; }
.group > h2 { margin: 0 0 14px; font-size: 1.5rem; }
.group > h2 small { margin-left: 8px; color: var(--accent); font-size: .75rem; font-weight: 500; }
.file { margin-bottom: 22px; overflow: hidden; border: 1px solid var(--border); border-radius: 10px; background: var(--panel); scroll-margin-top: 18px; }
.file-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid var(--border); }
.file-header h3, .file-header p { margin: 0; }
.file-header h3 { font: 600 .9rem ui-monospace, monospace; }
.file-header p { color: var(--muted); font-size: .75rem; }
.file-actions { display: flex; align-items: center; gap: 10px; }
.source-link { color: var(--accent); font-size: .75rem; white-space: nowrap; }
.source-link:hover { color: var(--text); }
.copy { padding: 5px 10px; color: var(--muted); border: 1px solid var(--border); border-radius: 6px; background: transparent; cursor: pointer; }
.copy:hover { color: var(--text); border-color: var(--accent); }
pre[class*="language-"] { max-height: 650px; margin: 0 !important; border-radius: 0; font-size: .8rem; }

/* Literate Emacs config */
#content { max-width: 1100px; margin-left: 270px; padding: 50px 5vw 100px; }
#content a { color: var(--accent); }
#content a:hover { color: var(--text); }
h1.title { margin: 0 0 32px; color: var(--text); font-size: clamp(2rem, 5vw, 4rem); letter-spacing: -.05em; text-align: left; }
.page-links { position: fixed; z-index: 2; top: 24px; left: 20px; display: flex; width: 230px; justify-content: space-between; font-size: .8rem; }
.page-links a { color: var(--accent); }
#table-of-contents { position: fixed; z-index: 1; inset: 0 auto 0 0; width: 270px; padding: 62px 20px 28px; overflow: auto; border-right: 1px solid var(--border); background: var(--sidebar); }
#table-of-contents h2 { margin: 0 0 14px; color: var(--text); font-size: 1.4rem; }
#text-table-of-contents ul { margin: 0; padding-left: 14px; list-style: none; }
#text-table-of-contents > ul { padding-left: 0; }
#text-table-of-contents a { display: block; padding: 4px 7px; border-radius: 6px; color: var(--muted); font-size: .82rem; line-height: 1.35; }
#text-table-of-contents a:hover { color: var(--text); background: var(--panel); }
.outline-2 { padding-top: 22px; }
h2 { margin: 0 0 14px; color: var(--text); font-size: 1.5rem; }
h3 { margin-top: 32px; color: var(--text); }
p, li { color: var(--text); }
.org-src-container { position: relative; }
.org-src-container > .copy { position: absolute; z-index: 1; top: 8px; right: 8px; background: var(--panel); }
pre.src, pre.example { max-height: 650px; margin: 16px 0; padding: 16px; overflow: auto; border: 1px solid var(--border); border-radius: 10px; background: var(--panel); color: var(--text); font: .8rem/1.5 ui-monospace, monospace; }
.org-comment, .org-comment-delimiter { color: #6c7086; font-style: italic; }
.org-keyword, .org-negation-char { color: #cba6f7; }
.org-string, .org-doc { color: #a6e3a1; }
.org-function-name { color: #89b4fa; }
.org-variable-name { color: #f5c2e7; }
.org-builtin, .org-constant, .org-type { color: #f9e2af; }
.org-regexp-grouping-backslash, .org-regexp-grouping-construct { color: #94e2d5; }
.org-warning { color: #f38ba8; font-weight: 600; }
code { padding: 2px 5px; border-radius: 4px; background: var(--panel); font-family: ui-monospace, monospace; }
pre code { padding: 0; background: transparent; }
table { border-collapse: collapse; }
th, td { padding: 8px 12px; border: 1px solid var(--border); }
#postamble { margin-left: 270px; padding: 20px 5vw; color: var(--muted); border-top: 1px solid var(--border); font-size: .75rem; }

@media (prefers-color-scheme: light) {
  :root { color-scheme: light; --bg: #f6f7f9; --panel: #ffffff; --sidebar: #eef1f5; --muted: #667085; --text: #1d2433; --accent: #315fa8; --border: #d8dee8; }
  .org-comment, .org-comment-delimiter { color: #6a737d; }
  .org-keyword, .org-negation-char { color: #8250df; }
  .org-string, .org-doc { color: #116329; }
  .org-function-name { color: #0550ae; }
  .org-variable-name { color: #953800; }
  .org-builtin, .org-constant, .org-type { color: #9a6700; }
  .org-regexp-grouping-backslash, .org-regexp-grouping-construct { color: #0a7c72; }
  .org-warning { color: #cf222e; }
}

@media (max-width: 760px) {
  aside { position: static; width: auto; border-right: 0; border-bottom: 1px solid var(--border); }
  .nav-toggle { display: block; }
  aside nav { display: none; }
  aside.nav-open nav { display: block; }
  main { margin: 0; padding: 30px 18px 70px; }
  .file-header { align-items: flex-start; gap: 8px; }
  .file-actions { flex-direction: column; align-items: flex-end; gap: 6px; }
  .page-links { position: static; width: auto; padding: 18px 18px 0; }
  #table-of-contents { position: static; width: auto; max-height: 45vh; padding: 18px; border-right: 0; border-bottom: 1px solid var(--border); }
  #content { margin: 0; padding: 30px 18px 70px; }
  #postamble { margin: 0; padding: 20px 18px; }
}

  </style>
</head>
<body>
  <aside>
    <h1>Dotfiles</h1>
    <p>105 tracked files across 23 groups</p>
    <button class="nav-toggle" type="button" aria-expanded="false" aria-controls="site-navigation">Browse sections</button>
    <nav id="site-navigation">{{NAVIGATION}}</nav>
  </aside>
  <main>
    <header class="intro">
      <h1>Configuration,<br>documented by itself.</h1>
      <p>Generated from the repository source.</p>
    </header>
    {{SECTIONS}}
  </main>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
  <script>
    const sidebar = document.querySelector('aside');
    const navToggle = document.querySelector('.nav-toggle');
    navToggle.addEventListener('click', () => {
      const open = sidebar.classList.toggle('nav-open');
      navToggle.setAttribute('aria-expanded', String(open));
    });
    document.querySelectorAll('.copy').forEach(button => button.addEventListener('click', async () => {
      await navigator.clipboard.writeText(button.closest('.file').querySelector('code').textContent);
      button.textContent = 'Copied';
      setTimeout(() => button.textContent = 'Copy', 1200);
    }));
  </script>
</body>
</html>
"""


def main() -> int:
    args = parse_args()
    patterns = DEFAULT_EXCLUDES + tuple(args.exclude)
    try:
        candidates = repository_files()
        source_base = github_source_base()
    except RuntimeError as error:
        print(f"error: {error}", file=sys.stderr)
        return 1

    files: list[tuple[Path, str]] = []

    for path in candidates:
        try:
            relative = path.relative_to(ROOT)
        except ValueError:
            continue
        if is_excluded(relative, patterns) or relative == EMACS_SOURCE:
            continue
        source = read_text(path)
        if source is not None:
            files.append((relative, source))

    if not files:
        print("error: no publishable files found", file=sys.stderr)
        return 1

    output = args.output.expanduser().resolve()
    emacs_output = output.with_name("emacs.html")
    if output == emacs_output:
        print("error: the main output cannot be named emacs.html", file=sys.stderr)
        return 1

    try:
        stylesheet = STYLESHEET.read_text(encoding="utf-8")
    except OSError as error:
        print(f"error: could not read {STYLESHEET}: {error}", file=sys.stderr)
        return 1

    output.parent.mkdir(parents=True, exist_ok=True)
    try:
        with tempfile.TemporaryDirectory(
            prefix=".config-site-", dir=output.parent
        ) as staging_directory:
            staging = Path(staging_directory)
            staged_index = staging / output.name
            staged_emacs = staging / emacs_output.name
            emacs_source_url = source_base + quote(EMACS_SOURCE.as_posix(), safe="/")
            subprocess.run(
                [
                    "bash",
                    str(EMACS_RENDERER),
                    str(staged_emacs),
                    emacs_source_url,
                ],
                cwd=ROOT,
                check=True,
            )
            staged_index.write_text(
                render_site(files, stylesheet, source_base), encoding="utf-8"
            )
            os.replace(staged_emacs, emacs_output)
            os.replace(staged_index, output)
    except (OSError, subprocess.CalledProcessError) as error:
        print(f"error: could not generate site: {error}", file=sys.stderr)
        return 1

    print(f"Generated {output} from {len(files)} files")
    print(f"Generated {emacs_output} from {EMACS_SOURCE}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

org-render-html-minimal.el

scripts/org-render-html-minimal.el · 55 lines

(require 'package)

(setq package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
                         ("melpa" . "https://melpa.org/packages/")))

(package-initialize)

;; This is required to highlight code blocks properly.
(package-refresh-contents)
(package-install 'htmlize)

(require 'htmlize)
(require 'org)

(defconst dotfiles-html-head
  (let ((stylesheet
         (expand-file-name "config-site.css"
                           (file-name-directory load-file-name))))
    (with-temp-buffer
      (insert-file-contents stylesheet)
      (concat "<style>\n" (buffer-string) "</style>"))))

(defconst dotfiles-html-scripts
  "<script>
document.addEventListener('DOMContentLoaded', () => {
  document.querySelectorAll('pre.src').forEach(block => {
    const button = document.createElement('button');
    button.className = 'copy';
    button.type = 'button';
    button.textContent = 'Copy';
    button.setAttribute('aria-label', 'Copy code snippet');
    button.addEventListener('click', async () => {
      await navigator.clipboard.writeText(block.textContent);
      button.textContent = 'Copied';
      setTimeout(() => button.textContent = 'Copy', 1200);
    });
    block.parentElement.prepend(button);
  });
});
</script>")

(defconst dotfiles-html-preamble
  (format
   "<nav class=\"page-links\"><a href=\"index.html\">Dotfiles</a><a href=\"%s\">View on GitHub</a></nav>"
   (getenv "DOTFILES_SOURCE_URL")))

;; Keep source highlighting in CSS classes and use the same visual language as
;; the generated dotfiles index instead of an external Org theme.
(setq org-export-time-stamp-file nil
      org-html-htmlize-output-type 'css
      org-html-head dotfiles-html-head
      org-html-head-extra dotfiles-html-scripts
      org-html-preamble dotfiles-html-preamble
      org-html-head-include-default-style nil
      org-html-head-include-scripts nil)

render-emacs-config-to-html.sh

scripts/render-emacs-config-to-html.sh · 32 lines

#!/usr/bin/env bash
set -euo pipefail

script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
root_dir=$(cd -- "$script_dir/.." && pwd)
if (($# != 2)); then
  echo "Usage: $0 OUTPUT SOURCE_URL" >&2
  exit 64
fi
output=$1
source_url=$2
work_dir=$(mktemp -d)
trap 'rm -rf "$work_dir"' EXIT

# Isolate package.el and all Emacs state from the user's configuration. This
# makes the batch export reproducible and prevents installed packages or an
# existing init file from affecting it.
export HOME="$work_dir/home"
export XDG_CONFIG_HOME="$work_dir/config"
export DOTFILES_SOURCE_URL="$source_url"
mkdir -p "$HOME" "$XDG_CONFIG_HOME"

cp "$root_dir/.emacs.d/bootstrap.org" "$work_dir/index.org"

emacs --batch \
  -Q \
  --load "$script_dir/org-render-html-minimal.el" \
  "$work_dir/index.org" \
  -f org-html-export-to-html

mkdir -p "$(dirname -- "$output")"
mv "$work_dir/index.html" "$output"

Swappy

config

swappy/config · 10 lines

[Default]
save_dir=$HOME/Desktop
save_filename_format=swappy-%Y%m%d-%H%M%S.png
show_panel=true
line_size=5
text_size=20
text_font=sans-serif
paint_mode=brush
early_exit=false
fill_shape=false

Sway

config

sway/config · 226 lines

# Logo key. Use Mod1 for Alt.
set $mod Mod4
# Home row direction keys, like vim
set $left h
set $down j
set $up k
set $right l
# Your preferred terminal emulator
set $term alacritty

# Hide tiled window titles and show them on floating windows.
default_border none
default_floating_border normal 0

gaps inner 5

### Output configuration
#
# Default wallpaper (more resolutions are available in /run/current-system/sw/share/backgrounds/sway/)
# output * bg /run/current-system/sw/share/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill
#
# Example configuration:
#
#   output HDMI-A-1 resolution 1920x1080 position 1920,0
#
# You can get the names of your outputs by running: swaymsg -t get_outputs

# Make the active Sway sockets available to graphical systemd user services.
exec_always systemctl --user import-environment WAYLAND_DISPLAY SWAYSOCK

exec waybar -c ~/.config/waybar/config.jsonc -s ~/.config/waybar/styles.css
exec swaync

# Shift from 6500K to 4000K based on local sunrise and sunset times.
exec wlsunset -l 19.4326 -L -99.1332 -t 4000 -T 6500

exec_always eww daemon
exec_always eww open widgets
exec_always eww open calendar-window

exec wl-paste --type text --watch cliphist store
exec wl-paste --type image --watch cliphist store

exec autotiling

### Idle configuration
#
# Example configuration:
#
# exec swayidle -w \
#          timeout 300 'swaylock -f -c 000000' \
#          timeout 600 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' \
#          before-sleep 'swaylock -f -c 000000'
#
# This will lock your screen after 300 seconds of inactivity, then turn off
# your displays after another 300 seconds, and turn your screens back on when
# resumed. It will also lock your screen before your computer goes to sleep.

### Input configuration
#
# Example configuration:
#
#   input "2:14:SynPS/2_Synaptics_TouchPad" {
#       dwt enabled
#       tap enabled
#       natural_scroll enabled
#       middle_emulation enabled
#   }
#
# You can get the names of your inputs by running: swaymsg -t get_inputs
# Read `man 5 sway-input` for more information about this section.

input * {
    xkb_layout us
    xkb_variant mac
    # Use Left Alt for macOS-style accent sequences.
    xkb_options lv3:lalt_switch
    repeat_delay 250
    repeat_rate 50
}

### Key bindings
#
# Basics:
#
    # Start a terminal
    bindsym $mod+Return exec $term

    # Kill focused window
    bindsym $mod+q kill

    # lock screen using a video as wallpaper
    bindsym $mod+Ctrl+q exec ~/.config/sway/scripts/lockscreen.sh

    # Start rofi (a program launcher)
    bindsym $mod+space exec rofi -show drun -show-icons

    # Open the emoji picker with the macOS shortcut: Control+Command+Space
    bindsym Ctrl+$mod+space exec rofimoji --selector rofi --action type --typer wtype

    # Open clipboard history
    bindsym $mod+y exec ~/.config/sway/scripts/clipboard-menu.sh

    # Drag floating windows by holding down $mod and left mouse button.
    # Resize them with right mouse button + $mod.
    # Despite the name, also works for non-floating windows.
    # Change normal to inverse to use left mouse button for resizing and right
    # mouse button for dragging.
    floating_modifier $mod normal

    # Reload the configuration file
    bindsym $mod+Shift+c reload

    # Exit sway (logs you out of your Wayland session)
    bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit'
#
# Moving around:
#
    # Move your focus around
    bindsym $mod+$left focus left
    bindsym $mod+$down focus down
    bindsym $mod+$up focus up
    bindsym $mod+$right focus right

    # Move the focused window with the same, but add Shift
    bindsym $mod+Shift+$left move left
    bindsym $mod+Shift+$down move down
    bindsym $mod+Shift+$up move up
    bindsym $mod+Shift+$right move right
#
# Workspaces:
#
    # Switch to workspace
    bindsym $mod+1 workspace number 1
    bindsym $mod+2 workspace number 2
    bindsym $mod+3 workspace number 3
    bindsym $mod+4 workspace number 4
    bindsym $mod+5 workspace number 5
    bindsym $mod+6 workspace number 6
    bindsym $mod+7 workspace number 7
    bindsym $mod+8 workspace number 8
    bindsym $mod+9 workspace number 9
    bindsym $mod+0 workspace number 10
    # Move focused container to workspace
    bindsym $mod+Shift+1 move container to workspace number 1
    bindsym $mod+Shift+2 move container to workspace number 2
    bindsym $mod+Shift+3 move container to workspace number 3
    bindsym $mod+Shift+4 move container to workspace number 4
    bindsym $mod+Shift+5 move container to workspace number 5
    bindsym $mod+Shift+6 move container to workspace number 6
    bindsym $mod+Shift+7 move container to workspace number 7
    bindsym $mod+Shift+8 move container to workspace number 8
    bindsym $mod+Shift+9 move container to workspace number 9
    bindsym $mod+Shift+0 move container to workspace number 10
    # Note: workspaces can have any name you want, not just numbers.
    # We just use 1-10 as the default.
#
# Layout stuff:
#
    # You can "split" the current object of your focus with
    # $mod+b or $mod+v, for horizontal and vertical splits
    # respectively.
    bindsym $mod+b splith
    bindsym $mod+v splitv

    # Switch the current container between different layout styles
    bindsym $mod+s layout stacking
    bindsym $mod+w layout tabbed
    bindsym $mod+e layout toggle split

    # Make the current focus fullscreen
    bindsym $mod+f fullscreen

    # Toggle the current focus between tiling and floating mode
    bindsym $mod+Shift+space floating toggle

    # Swap focus between the tiling area and the floating area
    # bindsym $mod+space focus mode_toggle

    # Move focus to the parent container
    bindsym $mod+a focus parent

# Resizing containers:
#
mode "resize" {
    bindsym $left resize shrink width 10px
    bindsym $down resize grow height 10px
    bindsym $up resize shrink height 10px
    bindsym $right resize grow width 10px

    # Return to default mode
    bindsym Return mode "default"
    bindsym Escape mode "default"
}
bindsym $mod+r mode "resize"
#
# Utilities:
#
    # Special keys to adjust volume via PulseAudio
    bindsym --locked XF86AudioMute exec pactl set-sink-mute \@DEFAULT_SINK@ toggle
    bindsym --locked XF86AudioLowerVolume exec pactl set-sink-volume \@DEFAULT_SINK@ -5%
    bindsym --locked XF86AudioRaiseVolume exec pactl set-sink-volume \@DEFAULT_SINK@ +5%
    bindsym --locked XF86AudioMicMute exec pactl set-source-mute \@DEFAULT_SOURCE@ toggle
    # Special keys to adjust brightness via brightnessctl
    bindsym --locked XF86MonBrightnessDown exec brightnessctl set 5%-
    bindsym --locked XF86MonBrightnessUp exec brightnessctl set 5%+
    # Capture screenshots and open them in Satty for annotation
    bindsym $mod+Shift+s exec ~/.config/sway/scripts/screenshot.sh

# Enable services so sway can run with no errors, this is required to be able to run waybar
# because we need to have xdg-portal service enabled
include /etc/sway/config.d/*

for_window [app_id="org.gnome.NautilusPreviewer"] floating enable, move position center, resize set 900 700
for_window [app_id="^blueman-manager$"] floating enable, move position center
for_window [class="^Blueman-manager$"] floating enable, move position center
for_window [app_id="^nm-connection-editor$"] floating enable, move position center
for_window [class="^Nm-connection-editor$"] floating enable, move position center
for_window [app_id="^org.pulseaudio.pavucontrol$"] floating enable, border normal 0, move position center
for_window [app_id="^pavucontrol$"] floating enable, border normal 0, move position center
for_window [class="^Pavucontrol$"] floating enable, border normal 0, move position center
for_window [app_id="^org.satty.satty$"] floating enable, border normal 0, resize set 1200 800, move position center

for_window [app_id="^firefox$"] move to workspace 2
for_window [class="^Emacs$"] move to workspace 3
for_window [class="^Spotify$"] move to workspace 4

clipboard-menu.sh

sway/scripts/clipboard-menu.sh · 41 lines

#!/usr/bin/env bash
set -euo pipefail

preview_dir=$(mktemp -d)
menu_file="$preview_dir/menu"
: >"$menu_file"
trap 'rm -rf "$preview_dir"' EXIT

while IFS= read -r entry; do
  display=${entry#*$'\t'}
  if [[ "$display" == "[[ binary data"* ]]; then
    id=${entry%%$'\t'*}
    preview="$preview_dir/$id"
    if printf '%s' "$entry" | cliphist decode >"$preview"; then
      details=${display#"[[ binary data "}
      details=${details%"]]"}
      printf '%s\0icon\x1f%s\x1fdisplay\x1fImage · %s\n' "$entry" "$preview" "$details" >>"$menu_file"
      continue
    fi
  fi
  printf '%s\0display\x1f%s\n' "$entry" "$display" >>"$menu_file"
done < <(cliphist list)

selection=$(rofi -dmenu -i -show-icons -p "Clipboard" \
  -theme-str 'element-icon { size: 80px; }' <"$menu_file") || exit 0
[[ -n "$selection" ]] || exit 0

printf '%s' "$selection" | cliphist decode | wl-copy
sleep 0.05

app_id=$(swaymsg -t get_tree | jq -r \
  'first(.. | objects | select(.focused? == true) | (.app_id // .window_properties.class // empty)) // empty')

case "${app_id,,}" in
*alacritty* | foot | footclient | *kitty* | *wezterm* | *ghostty* | *terminal* | konsole | xterm | urxvt)
  wtype -M ctrl -M shift v -m shift -m ctrl
  ;;
*)
  wtype -M ctrl v -m ctrl
  ;;
esac

lockscreen.sh

sway/scripts/lockscreen.sh · 21 lines

#!/usr/bin/env bash
set -euo pipefail

VIDEO_DIR="$HOME/Wallpapers/"

if [[ ! -d "$VIDEO_DIR" ]]; then
  echo "Error: '$VIDEO_DIR' is not a directory" >&2
  exit 1
fi

mapfile -d '' -t videos < <(find "$VIDEO_DIR" -type f -iname '*.mp4' -print0)

if [[ ${#videos[@]} -eq 0 ]]; then
  echo "No .mp4 files found in '$VIDEO_DIR'" >&2
  exit 1
fi

random_index=$((RANDOM % ${#videos[@]}))
selected="${videos[$random_index]}"

swaylock-plugin --command "mpvpaper -o 'hwdec=vaapi no-audio loop' '*' $selected"

screenshot.sh

sway/scripts/screenshot.sh · 45 lines

#!/usr/bin/env bash
set -euo pipefail

mode=${1:-}
if [[ -z "$mode" ]]; then
  selection=$(printf '%s\n' "Full screen" "Select region" "Focused window" |
    rofi -dmenu -i -p "Screenshot") || exit 0
  case "$selection" in
  "Full screen") mode="full" ;;
  "Select region") mode="region" ;;
  "Focused window") mode="window" ;;
  *) exit 0 ;;
  esac
fi

screenshot_dir="$HOME/Pictures/Screenshots"
capture=$(mktemp "${XDG_RUNTIME_DIR:-/tmp}/screenshot.XXXXXX.png")
trap 'rm -f "$capture"' EXIT
mkdir -p "$screenshot_dir"

case "$mode" in
full)
  grim "$capture"
  ;;
region)
  geometry=$(slurp) || exit 0
  [[ -n "$geometry" ]] || exit 0
  grim -g "$geometry" "$capture"
  ;;
window)
  geometry=$(swaymsg -t get_tree | jq -r \
    'first(.. | objects | select(.focused? == true) | .rect | "\(.x),\(.y) \(.width)x\(.height)") // empty')
  [[ -n "$geometry" ]] || exit 1
  grim -g "$geometry" "$capture"
  ;;
*)
  printf 'Usage: %s {full|region|window}\n' "${0##*/}" >&2
  exit 2
  ;;
esac

satty --filename "$capture" \
  --app-id org.satty.satty \
  --copy-command wl-copy \
  --output-filename "$screenshot_dir/screenshot-%Y-%m-%d_%H-%M-%S.png"

wallpaper.sh

sway/scripts/wallpaper.sh · 65 lines

#!/usr/bin/env bash
set -euo pipefail

WALLPAPER_DIR="$HOME/Wallpapers"
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/wallpaper"
LAST_WALLPAPER_FILE="$STATE_DIR/last-wallpaper"
VIDEO_WALLPAPER_LINK="$STATE_DIR/current-video"

if [[ ! -d "$WALLPAPER_DIR" ]]; then
  echo "Error: '$WALLPAPER_DIR' is not a directory" >&2
  exit 1
fi

media=()
while IFS= read -r -d '' candidate; do
  mime_type=$(file --brief --mime-type -- "$candidate")
  case "$mime_type" in
  image/* | video/*) media+=("$candidate") ;;
  esac
done < <(find "$WALLPAPER_DIR" -type f -print0)

if ((${#media[@]} == 0)); then
  echo "No image or video files found in '$WALLPAPER_DIR'" >&2
  exit 1
fi

last_wallpaper=""
[[ -r "$LAST_WALLPAPER_FILE" ]] && last_wallpaper=$(<"$LAST_WALLPAPER_FILE")

choices=("${media[@]}")
if ((${#media[@]} > 1)) && [[ -n "$last_wallpaper" ]]; then
  choices=()
  for candidate in "${media[@]}"; do
    [[ "$candidate" != "$last_wallpaper" ]] && choices+=("$candidate")
  done
fi

selected=${choices[RANDOM % ${#choices[@]}]}
mkdir -p "$STATE_DIR"
printf '%s\n' "$selected" >"$LAST_WALLPAPER_FILE"
mime_type=$(file --brief --mime-type -- "$selected")

case "$mime_type" in
image/*)
  systemctl --user stop wallpaper-video.service
  swaymsg output '*' bg "$selected" fill
  ;;
video/*)
  # Sway starts swaybg for static images; stop it before using mpvpaper.
  mapfile -t swaybg_pids < <(pgrep -f '[s]waybg' || true)
  if ((${#swaybg_pids[@]})); then
    kill "${swaybg_pids[@]}" 2>/dev/null || true
    for _ in {1..10}; do
      swaybg_running=false
      for pid in "${swaybg_pids[@]}"; do
        kill -0 "$pid" 2>/dev/null && swaybg_running=true
      done
      "$swaybg_running" || break
      sleep 0.05
    done
  fi
  ln -sfn -- "$selected" "$VIDEO_WALLPAPER_LINK"
  systemctl --user restart wallpaper-video.service
  ;;
esac

wlogout-menu.sh

sway/scripts/wlogout-menu.sh · 33 lines

#!/usr/bin/env bash
set -euo pipefail

read -r width height monitor < <(
  swaymsg --type get_outputs --raw |
    jq --raw-output '
      to_entries as $outputs
      | ($outputs | map(select(.value.focused))[0] // $outputs[0])
      | "\(.value.rect.width) \(.value.rect.height) \(.key)"
    '
)

buttons=6
spacing=12
button_width=140
menu_height=160
menu_width=$((buttons * button_width + (buttons - 1) * spacing))
horizontal_margin=$(((width - menu_width) / 2))
vertical_margin=$(((height - menu_height) / 2))

((horizontal_margin < 16)) && horizontal_margin=16
((vertical_margin < 16)) && vertical_margin=16

exec wlogout \
  --protocol layer-shell \
  --buttons-per-row "$buttons" \
  --column-spacing "$spacing" \
  --margin-left "$horizontal_margin" \
  --margin-right "$horizontal_margin" \
  --margin-top "$vertical_margin" \
  --margin-bottom "$vertical_margin" \
  --primary-monitor "$monitor" \
  --no-span

Swaync

config.json

swaync/config.json · 49 lines

{
  "positionX": "right",
  "positionY": "top",
  "layer": "overlay",
  "control-center-layer": "top",
  "layer-shell": true,
  "cssPriority": "user",
  "control-center-margin-top": 8,
  "control-center-margin-bottom": 8,
  "control-center-margin-right": 8,
  "control-center-margin-left": 8,
  "notification-2fa-action": true,
  "notification-inline-replies": false,
  "notification-body-image-height": 100,
  "notification-body-image-width": 200,
  "timeout": 8,
  "timeout-low": 5,
  "timeout-critical": 0,
  "fit-to-screen": true,
  "relative-timestamps": true,
  "control-center-width": 380,
  "control-center-height": 600,
  "notification-window-width": 380,
  "keyboard-shortcuts": true,
  "notification-grouping": true,
  "image-visibility": "when-available",
  "transition-time": 180,
  "hide-on-clear": false,
  "hide-on-action": true,
  "text-empty": "No notifications",
  "widgets": [
    "title",
    "dnd",
    "notifications"
  ],
  "widget-config": {
    "notifications": {
      "vexpand": true
    },
    "title": {
      "text": "Notifications",
      "clear-all-button": true,
      "button-text": "Clear all"
    },
    "dnd": {
      "text": "Do not disturb"
    }
  }
}

style.css

swaync/style.css · 155 lines

* {
  font-family: "JetBrainsMono Nerd Font";
  color: #eeeeee;
}

notificationwindow,
blankwindow,
.notification-window,
.blank-window,
.floating-notifications {
  background: transparent;
}

.notification-row {
  background: transparent;
  outline: none;
}

.notification-background {
  padding: 6px 10px;
}

.notification {
  border: 1px solid rgba(126, 186, 228, 0.48);
  border-radius: 10px;
  background-color: rgba(15, 15, 15, 0.94);
  box-shadow: none;
}

.notification.low {
  border-color: rgba(255, 255, 255, 0.1);
  background-color: rgba(15, 15, 15, 0.82);
}

.notification.low .summary,
.notification.low .body,
.notification.low .time {
  color: rgba(255, 255, 255, 0.5);
}

.notification.normal {
  border-color: rgba(126, 186, 228, 0.48);
}

.notification.critical {
  border-color: #f53c3c;
  background-color: rgba(45, 15, 15, 0.96);
}

.notification.critical .summary {
  color: #f53c3c;
}

.notification.critical .body {
  color: rgba(255, 255, 255, 0.86);
}

.notification-default-action {
  margin: 0;
  padding: 8px;
  border: none;
  border-radius: 10px;
  background: transparent;
  box-shadow: none;
}

.notification-default-action:hover,
.notification-action > button:hover {
  background-color: rgba(126, 186, 228, 0.12);
}

.notification-content {
  padding: 2px;
  background: transparent;
}

.summary {
  color: #7ebae4;
  font-size: 14px;
  font-weight: bold;
  text-shadow: none;
}

.time {
  color: rgba(255, 255, 255, 0.45);
  font-size: 11px;
  text-shadow: none;
}

.body {
  color: rgba(255, 255, 255, 0.72);
  font-size: 12px;
  text-shadow: none;
}

.close-button {
  min-width: 24px;
  min-height: 24px;
  margin: 8px;
  padding: 0;
  border: none;
  border-radius: 50%;
  color: #eeeeee;
  background-color: rgba(255, 255, 255, 0.1);
  box-shadow: none;
}

.close-button:hover {
  color: #ffffff;
  background-color: rgba(245, 60, 60, 0.8);
}

.notification-action > button {
  margin: 4px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 8px;
  color: #eeeeee;
  background-color: rgba(255, 255, 255, 0.06);
}

.control-center {
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  color: #eeeeee;
  background-color: rgba(15, 15, 15, 0.94);
}

.control-center .notification {
  border-color: rgba(255, 255, 255, 0.12);
}

.widget {
  margin: 8px;
  padding: 8px;
  border-radius: 10px;
  background-color: rgba(255, 255, 255, 0.04);
}

.widget-title > label {
  color: #7ebae4;
  font-size: 16px;
  font-weight: bold;
}

.widget-title > button,
.widget-dnd > switch {
  border: 1px solid rgba(126, 186, 228, 0.35);
  border-radius: 8px;
  color: #eeeeee;
  background-color: rgba(126, 186, 228, 0.1);
}

.widget-title > button:hover {
  background-color: rgba(126, 186, 228, 0.2);
}

Waybar

config.jsonc

waybar/config.jsonc · 208 lines

{
  // "layer": "top", // Waybar at top layer
  "position": "top", // Waybar position (top|bottom|left|right)
  "height": 30, // Waybar height (to be removed for auto height)
  "spacing": 4, // Gaps between modules (4px)
  // Choose the order of the modules
  "modules-left": [
    "custom/power",
    "sway/workspaces",
    "sway/mode",
    "sway/window",
  ],
  "modules-center": ["custom/spotify"],
  "modules-right": [
    "tray",
    "idle_inhibitor",
    "pulseaudio",
    "pulseaudio#microphone",
    "privacy",
    "custom/vnc-session",
    "bluetooth",
    "custom/vpn",
    "network",
    // "cpu",
    // "memory",
    // "custom/gpu",
    "temperature",
    "battery",
    "custom/wallpaper",
    "clock",
    "custom/notification"
  ],
  "sway/workspaces": {
    "format": "{name}",
    "all-outputs": true,
    "persistent-workspaces": {
      "1": [],
      "2": [],
      "3": [],
      "4": [],
      "5": []
    }
  },
  "idle_inhibitor": {
    "format": "{icon}",
    "format-icons": {
      "activated": "",
      "deactivated": ""
    }
  },
  "tray": {
    // "icon-size": 21,
    "spacing": 10
  },
  "clock": {
    "format": "{:%a %d %b %I:%M %p}",
    "tooltip-format": "<tt><small>{calendar}</small></tt>",
    "calendar": {
      "format": {
        "today": "<span color='#75f1fa'><b>{}</b></span>"
      }
    },
  },
  "cpu": {
    "format": "{usage}% ",
    "tooltip": true
  },
  "memory": {
    "format": "{}% "
  },
  "custom/gpu": {
    "exec": "~/.config/waybar/scripts/gpu.sh",
    "return-type": "json",
    "interval": 2,
    "format": "🎮 {}"
  },
  "custom/spotify": {
    "exec": "~/.config/waybar/scripts/spotify.sh",
    "return-type": "json",
    "interval": 2,
    "max-length": 40,
    "on-click": "playerctl -p spotify play-pause",
    "on-scroll-up": "playerctl -p spotify next",
    "on-scroll-down": "playerctl -p spotify previous"
  },
  "custom/power": {
    "format": "\uf313",
    "tooltip-format": "Power menu",
    "on-click": "~/.config/sway/scripts/wlogout-menu.sh"
  },
  "custom/wallpaper": {
    "format": "󰸉",
    "tooltip-format": "Change wallpaper",
    "on-click": "systemctl --user start wallpaper-rotate.service"
  },
  "custom/notification": {
    "tooltip": true,
    "format": "{icon}",
    "format-icons": {
      "notification": "󱅫",
      "none": "󰂜",
      "dnd-notification": "󰂠",
      "dnd-none": "󰪓",
      "inhibited-notification": "󰂛",
      "inhibited-none": "󰪑",
      "dnd-inhibited-notification": "󰂛",
      "dnd-inhibited-none": "󰪑"
    },
    "return-type": "json",
    "exec": "swaync-client -swb",
    "on-click": "swaync-client -t -sw",
    "on-click-right": "swaync-client -d -sw",
    "escape": true
  },
  "temperature": {
    "critical-threshold": 80,
    "format": "{temperatureC}°C {icon}",
    "format-icons": ["", "", ""]
  },
  "battery": {
    "states": {
      "good": 95,
      "warning": 30,
      "critical": 15
    },
    "format": "{capacity}% {icon}",
    "format-full": "{capacity}% {icon}",
    "format-charging": "{capacity}% ",
    "format-plugged": "{capacity}% ",
    "format-alt": "{time} {icon}",
    "format-icons": ["", "", "", "", ""]
  },
  "bluetooth": {
    "format": "",
    "format-disabled": "",
    "format-off": "",
    "format-connected": " {num_connections}",
    "on-click": "blueman-manager",
    "tooltip-format": "{controller_alias}: {status}",
    "tooltip-format-connected": "{controller_alias}: {status}\n\n{device_enumerate}",
    "tooltip-format-enumerate-connected": "{device_alias}"
  },
  "custom/vpn": {
    "exec": "~/.config/waybar/scripts/vpn.sh",
    "return-type": "json",
    "interval": 5,
    "format": "{}"
  },
  "custom/vnc-session": {
    "exec": "~/.config/waybar/scripts/vnc-session.sh",
    "return-type": "json",
    "restart-interval": 2,
    "format": "{}",
    "on-click": "~/.config/waybar/scripts/vnc-toggle.sh"
  },
  "network": {
    // "interface": "wlp2*", // (Optional) To force the use of this interface
    "format-wifi": "",
    "format-ethernet": "󰈀",
    "tooltip-format": "{ipaddr}",
    "tooltip-format-wifi": "{ipaddr}",
    "format-linked": "󰈀",
    "format-disconnected": "󰖪",
    "format-alt": "",
    "on-click": "nm-connection-editor"
  },
  "pulseaudio#microphone": {
    "format": "{format_source}",
    "format-source": "",
    "format-source-muted": "",
    "tooltip": false,
    "on-click": "pamixer --default-source --toggle-mute"
  },
  "privacy": {
    "icon-spacing": 4,
    "icon-size": 14,
    "transition-duration": 250,
    "modules": [
      {
        "type": "screenshare",
        "tooltip": true,
        "tooltip-icon-size": 24
      },
      {
        "type": "audio-in",
        "tooltip": true,
        "tooltip-icon-size": 24
      }
    ]
  },
  "pulseaudio": {
    // "scroll-step": 1, // %, can be a float
    "format": "{volume}% {icon}",
    "format-bluetooth": "{volume}% {icon}",
    "format-bluetooth-muted": "󰝟 {icon}",
    "format-muted": "󰝟",
    "format-icons": {
      "headphone": "",
      "hands-free": "󰋎",
      "headset": "󰋎",
      "phone": "",
      "portable": "",
      "car": "",
      "default": ["", "", ""]
    },
    "on-click": "pavucontrol"
  }
}

gpu.sh

waybar/scripts/gpu.sh · 9 lines

#!/usr/bin/env bash
if [ -f /sys/class/drm/card1/device/gpu_busy_percent ]; then
  u=$(cat /sys/class/drm/card1/device/gpu_busy_percent)
elif command -v nvidia-smi >/dev/null; then
  u=$(nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits)
else
  u=0
fi
echo "{\"text\": \"${u}%\", \"tooltip\": \"GPU: ${u}%\"}"

spotify.sh

waybar/scripts/spotify.sh · 26 lines

#!/usr/bin/env bash

player="spotify"

status=$(playerctl -p "$player" status 2>/dev/null)

if [[ -z "$status" ]]; then
  echo '{"text": "", "class": "stopped"}'
  exit 0
fi

artist=$(playerctl -p "$player" metadata artist 2>/dev/null)
title=$(playerctl -p "$player" metadata title 2>/dev/null)

text="${artist} - ${title}"
text="${text//&/&amp;}"

if [[ "$status" == "Playing" ]]; then
  icon=""
  class="playing"
else
  icon=""
  class="paused"
fi

printf '{"text": "%s %s", "class": "%s", "alt": "%s"}\n' "$icon" "$text" "$class" "$status"

vnc-session.sh

waybar/scripts/vnc-session.sh · 38 lines

#!/usr/bin/env bash
set -euo pipefail

emit_status() {
  local clients count tooltip

  if ! clients=$(wayvncctl --json client-list 2>/dev/null); then
    jq -cn '{text: "󰢹", tooltip: "WayVNC stopped — click to start", class: "stopped"}'
    return
  fi
  if ! count=$(jq -r 'if type == "array" then length else 0 end' <<<"$clients" 2>/dev/null); then
    count=0
  fi

  if ((count > 0)); then
    tooltip=$(jq -r '
      .[]
      | if .username then
          "\(.address // "Unknown address") (\(.username))"
        else
          (.address // "Unknown client")
        end
    ' <<<"$clients")
    jq -cn --arg count "$count" --arg tooltip "$tooltip" \
      '{text: ("󰢹 " + $count), tooltip: ("Active VNC sessions\n" + $tooltip + "\nClick to stop"), class: "active"}'
  else
    jq -cn '{text: "󰢹", tooltip: "WayVNC running — no active sessions\nClick to stop", class: "running"}'
  fi
}

emit_status
while IFS= read -r event; do
  case $(jq -r '.method // empty' <<<"$event" 2>/dev/null) in
  client-connected | client-disconnected | wayvnc-startup | wayvnc-shutdown)
    emit_status
    ;;
  esac
done < <(wayvncctl --wait --reconnect --json event-receive 2>/dev/null)

vnc-toggle.sh

waybar/scripts/vnc-toggle.sh · 23 lines

#!/usr/bin/env bash
set -euo pipefail

if wayvncctl --json version >/dev/null 2>&1; then
  if systemctl --user is-active --quiet wayvnc.service; then
    systemctl --user stop wayvnc.service
  else
    # Also stop WayVNC instances started manually before the service existed.
    wayvncctl wayvnc-exit >/dev/null
  fi
  notify-send -a "WayVNC" -i preferences-desktop-remote-desktop -u low -t 3000 \
    "WayVNC stopped" "Remote desktop access is disabled."
  exit 0
fi

if ! systemctl --user start wayvnc.service; then
  notify-send -a "WayVNC" -i dialog-error -u critical \
    "WayVNC could not start" "Check the WayVNC user service logs."
  exit 1
fi

notify-send -a "WayVNC" -i preferences-desktop-remote-desktop -u normal -t 4000 \
  "WayVNC started" "Listening on 0.0.0.0:5901 using wayland-1."

vpn.sh

waybar/scripts/vpn.sh · 50 lines

#!/usr/bin/env bash
set -uo pipefail

STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/waybar"
STATE_FILE="$STATE_DIR/vpn-state"
connections=()

while IFS=: read -r type name; do
  case "$type" in
  vpn | wireguard)
    [[ -n "$name" ]] && connections+=("$name")
    ;;
  esac
done < <(nmcli -t --escape no -f TYPE,NAME connection show --active 2>/dev/null)

if command -v tailscale >/dev/null 2>&1; then
  tailscale_state=$(timeout 2s tailscale status --json 2>/dev/null | jq -r '.BackendState // "Stopped"' 2>/dev/null)
  if [[ "$tailscale_state" == "Running" ]]; then
    connections+=("Tailscale")
  fi
fi

if ((${#connections[@]})); then
  tooltip=$(printf '%s\n' "${connections[@]}")
  current_state="connected:$tooltip"
else
  tooltip=""
  current_state="disconnected"
fi

previous_state=""
[[ -r "$STATE_FILE" ]] && previous_state=$(<"$STATE_FILE")
if [[ -n "$previous_state" && "$previous_state" != "$current_state" ]]; then
  if ((${#connections[@]})); then
    notify-send -a "VPN" -i network-vpn -u normal -t 4000 \
      "VPN connected" "$tooltip"
  else
    notify-send -a "VPN" -i network-vpn-disconnected -u normal -t 4000 \
      "VPN disconnected" "No VPN connection is active."
  fi
fi
mkdir -p "$STATE_DIR"
printf '%s\n' "$current_state" >"$STATE_FILE"

if ((${#connections[@]})); then
  jq -cn --arg tooltip "$tooltip" \
    '{text: "", tooltip: ("VPN connected\n" + $tooltip), class: "connected"}'
else
  jq -cn '{text: "", tooltip: "VPN disconnected", class: "disconnected"}'
fi

styles.css

waybar/styles.css · 374 lines

* {
  /* `otf-font-awesome` is required to be installed for icons */
  font-family: "JetBrainsMono Nerd Font", FontAwesome, Roboto, Helvetica, Arial, sans-serif;
  font-size: 14px;
  padding: 1px;
  margin: 2px 0;
}

#workspaces button.active {
  color: peru;
  border-bottom: 3px solid peru;
}

#workspaces button.empty {
  color: lightgray;
}

window#waybar {
  background-color: rgba(43, 48, 59, 0.5);
  border-bottom: 3px solid rgba(100, 114, 125, 0.5);
  color: #ffffff;
  transition-property: background-color;
  transition-duration: .5s;
}

window#waybar.hidden {
  opacity: 0.2;
}

button {
  /* Use box-shadow instead of border so the text isn't offset */
  box-shadow: inset 0 -3px transparent;
  /* Avoid rounded borders under each button name */
  border: none;
  border-radius: 0;
}

/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
button:hover {
  background: inherit;
  box-shadow: inset 0 -3px #ffffff;
}

/* you can set a style on hover for any module like this */
#pulseaudio:hover {
  background-color: transparent;
}

#workspaces button {
  padding: 0 5px;
  background-color: transparent;
  color: #ffffff;
}

#workspaces button:hover {
  background: rgba(0, 0, 0, 0.2);
}

#workspaces button.focused {
  background-color: #64727d;
  box-shadow: inset 0 -3px #ffffff;
}

#workspaces button.urgent {
  background-color: #eb4d4b;
}

#mode {
  background-color: #64727d;
  box-shadow: inset 0 -3px #ffffff;
}

#clock,
#battery,
#cpu,
#memory,
#disk,
#temperature,
#backlight,
#bluetooth,
#network,
#pulseaudio,
#wireplumber,
#custom-media,
#custom-vpn,
#tray,
#mode,
#idle_inhibitor,
#scratchpad,
#power-profiles-daemon,
#mpd {
  padding: 0 10px;
  color: #ffffff;
}

#window,
#workspaces {
  margin: 0 4px;
}

/* If workspaces is the leftmost module, omit left margin */
.modules-left > widget:first-child > #workspaces {
  margin-left: 0;
}

/* If workspaces is the rightmost module, omit right margin */
.modules-right > widget:last-child > #workspaces {
  margin-right: 0;
}

#clock {
  background-color: transparent;
  color: #ffffff;
}

#battery {
  background-color: transparent;
  color: #ffffff;
}

#battery.charging,
#battery.plugged {
  color: #ffffff;
  background-color: #26a65b;
}

@keyframes blink {
  to {
    background-color: #ffffff;
    color: #000000;
  }
}

/* Using steps() instead of linear as a timing function to limit cpu usage */
#battery.critical:not(.charging) {
  background-color: #f53c3c;
  color: #ffffff;
  animation-name: blink;
  animation-duration: 0.5s;
  animation-timing-function: steps(12);
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

#power-profiles-daemon {
  padding-right: 15px;
}

#power-profiles-daemon.performance {
  background-color: #f53c3c;
  color: #ffffff;
}

#power-profiles-daemon.balanced {
  background-color: #2980b9;
  color: #ffffff;
}

#power-profiles-daemon.power-saver {
  background-color: #2ecc71;
  color: #000000;
}

label:focus {
  background-color: #000000;
}

#cpu {
  background-color: transparent;
  color: #ffffff;
}

#memory {
  background-color: transparent;
  color: #ffffff;
}

#disk {
  background-color: transparent;
  color: #ffffff;
}

#backlight {
  background-color: transparent;
  color: #ffffff;
}

#bluetooth {
  background-color: transparent;
  color: #ffffff;
}

#bluetooth.disabled,
#bluetooth.off {
  color: rgba(255, 255, 255, 0.35);
}

#bluetooth.connected {
  color: #75f1fa;
}

#network {
  background-color: transparent;
  color: #ffffff;
}

#network.disconnected {
  background-color: #f53c3c;
}

/* Session states: blue is active, muted is inactive, red needs attention. */
#custom-vpn,
#idle_inhibitor,
#custom-notification {
  padding: 0 8px;
  background-color: transparent;
  color: rgba(255, 255, 255, 0.35);
}

#custom-vpn.connected,
#idle_inhibitor.activated,
#custom-notification.notification,
#custom-notification.dnd-notification,
#custom-notification.dnd-none,
#custom-notification.inhibited-notification,
#custom-notification.inhibited-none,
#custom-notification.dnd-inhibited-notification,
#custom-notification.dnd-inhibited-none {
  color: #7ebae4;
}

#pulseaudio {
  background-color: transparent;
  color: #fff;
}

#pulseaudio.muted {
  background-color: #90b1b1;
  color: #2a5c45;
}

#pulseaudio.microphone.source-muted {
  background-color: transparent;
  color: #f53c3c;
}

#wireplumber {
  background-color: #fff0f5;
  color: #000000;
}

#wireplumber.muted {
  background-color: #f53c3c;
}

#custom-media {
  background-color: #66cc99;
  color: #2a5c45;
  min-width: 100px;
}

#custom-media.custom-spotify {
  background-color: #66cc99;
}

#custom-media.custom-vlc {
  background-color: #ffa000;
}

#temperature {
  background-color: transparent;
  color: #ffffff;
}

#temperature.critical {
  background-color: #eb4d4b;
}

#tray {
  background-color: transparent;
  color: #ffffff;
}

#tray > .passive {
  -gtk-icon-effect: dim;
}

#tray > .needs-attention {
  -gtk-icon-effect: highlight;
  background-color: #eb4d4b;
}

#language {
  background: #00b093;
  color: #740864;
  padding: 0 5px;
  margin: 0 5px;
  min-width: 16px;
}

#keyboard-state {
  background: #97e1ad;
  color: #000000;
  padding: 0 0px;
  margin: 0 5px;
  min-width: 16px;
}

#keyboard-state > label {
  padding: 0 5px;
}

#keyboard-state > label.locked {
  background: rgba(0, 0, 0, 0.2);
}

#scratchpad {
  background: rgba(0, 0, 0, 0.2);
}

#scratchpad.empty {
  background-color: transparent;
}

#privacy {
  padding: 0;
}

#privacy-item {
  padding: 0 8px;
  color: #f53c3c;
  background-color: transparent;
}

#custom-vnc-session {
  padding: 0 8px;
  color: rgba(255, 255, 255, 0.35);
  background-color: transparent;
}

#custom-vnc-session.running {
  color: #7ebae4;
}

#custom-vnc-session.active {
  color: #2ecc71;
}

#custom-spotify {
  color: #fff;
}
#custom-spotify.paused {
  color: #888888;
}

#custom-power {
  font-family: "Symbols Nerd Font";
  font-size: 16px;
  padding: 0 10px;
  color: #7ebae4; /* NixOS blue */
}

#custom-wallpaper {
  font-family: "Symbols Nerd Font";
  font-size: 16px;
  padding: 0 8px;
  color: #7ebae4;
}

#custom-notification {
  font-family: "Symbols Nerd Font";
  font-size: 16px;
  padding: 0 8px;
}

Wlogout

layout

wlogout/layout · 36 lines

{
    "label" : "lock",
    "action" : "~/.config/sway/scripts/lockscreen.sh",
    "text" : "Lock",
    "keybind" : "l"
}
{
    "label" : "hibernate",
    "action" : "systemctl hibernate",
    "text" : "Hibernate",
    "keybind" : "h"
}
{
    "label" : "logout",
    "action" : "swaymsg exit",
    "text" : "Logout",
    "keybind" : "e"
}
{
    "label" : "shutdown",
    "action" : "systemctl poweroff",
    "text" : "Shutdown",
    "keybind" : "s"
}
{
    "label" : "suspend",
    "action" : "systemctl suspend",
    "text" : "Suspend",
    "keybind" : "u"
}
{
    "label" : "reboot",
    "action" : "systemctl reboot",
    "text" : "Reboot",
    "keybind" : "r"
}

style.css

wlogout/style.css · 58 lines

* {
  background-image: none;
  box-shadow: none;
  font-family: "JetBrainsMono Nerd Font";
}

window {
  background-color: rgba(10, 10, 10, 0.68);
}

button {
  margin: 6px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  color: #eeeeee;
  background-color: rgba(15, 15, 15, 0.9);
  background-repeat: no-repeat;
  background-position: center 38%;
  background-size: 42px;
  font-size: 13px;
  font-weight: 500;
}

button:hover,
button:focus {
  border-color: #7ebae4;
  color: #7ebae4;
  background-color: rgba(24, 38, 50, 0.96);
  outline-style: none;
}

button:active {
  background-color: rgba(36, 58, 76, 0.98);
}

#lock {
  background-image: -gtk-icontheme("system-lock-screen-symbolic");
}

#logout {
  background-image: -gtk-icontheme("system-log-out-symbolic");
}

#suspend {
  background-image: -gtk-icontheme("media-playback-pause-symbolic");
}

#hibernate {
  background-image: -gtk-icontheme("weather-clear-night-symbolic");
}

#shutdown {
  background-image: -gtk-icontheme("system-shutdown-symbolic");
}

#reboot {
  background-image: -gtk-icontheme("system-reboot-symbolic");
}

Zsh

linux.sh

zsh/linux.sh · 18 lines

#!/usr/bin/env bash

# Alias to use xclip like macOS
if [ "$XDG_SESSION_TYPE" = "wayland" ]; then
  alias pbcopy="wl-copy"
  alias pbpaste="wl-paste"
else
  alias pbcopy="xclip -selection clipboard"
  alias pbpaste="xclip -selection clipboard -o"
fi

function nixos_run_install {
  cd ~/dotfiles/nixos/ || exit 1
  # we need to use --impure because we're referencing absolute path
  # for hardware-configuration.nix file
  sudo nixos-rebuild --impure switch --flake .
  cd - || exit 1
}

mac.sh

zsh/mac.sh · 63 lines

#!/usr/bin/env bash
# postgres.app setup
export PATH=/usr/local/bin:$PATH:/Applications/Postgres.app/Contents/Versions/latest/bin

# Avoid auto update homwbrew packages
export HOMEBREW_NO_AUTO_UPDATE=1

# Setup gettext binaries
export PATH="/usr/local/opt/gettext/bin:$PATH"

# autojump setup
# intel
[ -f /usr/local/etc/profile.d/autojump.sh ] && . /usr/local/etc/profile.d/autojump.sh

# arm
[ -f /opt/homebrew/etc/profile.d/autojump.sh ] && . /opt/homebrew/etc/profile.d/autojump.sh

# fix emoji and symbol pallete when it doesn't show up
fix_emoji_palette() {
  preferences_path="$HOME/Library/Preferences/com.apple.HIToolbox.plist"

  if [ -e "$preferences_path" ]; then
    rm "$preferences_path"
  fi
}

function rae() {
  open "https://dle.rae.es/$1"
}

# use native macOS network check tool
alias speedtest=networkQuality

function reset_cache {
  sudo dscacheutil -flushcache
  sudo killall -HUP mDNSResponder
}

function homebrew-dump {
  cd ~/dotfiles/ || exit
  brew bundle dump --force
  git diff Brewfile
  cd - || exit
}

function nixdarwin_run_install {
  cd ~/dotfiles/nix-darwin/ || exit 1
  mkdir -p ~/.config/nix-darwin/
  cp *.nix ~/.config/nix-darwin/
  cp flake.lock ~/.config/nix-darwin/
  sudo darwin-rebuild switch --flake ~/.config/nix-darwin#simple
  # now we copy lock file back to dotfiles
  cp ~/.config/nix-darwin/flake.lock .
  cd - || exit 1
}

nixdarwin_free_space() {
  echo "collecting garbage..."
  nix-collect-garbage -d
  sudo nix-collect-garbage -d
  echo "optimizing storage, it might take a while..."
  nix-store --optimise
}

main.sh

zsh/main.sh · 205 lines

#!/usr/bin/env bash
# disable oh my zsh theme so pure prompt can work properly
export ZSH_THEME=""

# ZSH history file config
export HISTFILE="$HOME/.zsh_history"
export HISTSIZE=1000000
export SAVEHIST=1000000

# Ignore duplicates
setopt HIST_IGNORE_ALL_DUPS
# Ignore when prefixed with space
setopt HIST_IGNORE_SPACE
# Share history between sessions
setopt SHARE_HISTORY

# Load the default .profile
[[ -s "$HOME/.profile" ]] && source "$HOME/.profile"

# setup fzf
source <(fzf --zsh)
# Disable C-t which search into all files for the current folder to
# avoid press it by mistake
bindkey -r "^T"

# helper functions

function reload {
  source "$HOME/.zshrc"
}

function t() {
  tree -I '.git|node_modules|bower_components|.DS_store' --dirsfirst -L "${1:-3}" -aC "$2"
}

function docker_remove_containers() {
  docker rm "$(docker ps -a -q)"
}

function random_password() {
  openssl rand -base64 "$1"
}

# Estimate the compressed size of a file (GZIP)
function gzip_estimation() {
  gzip -9 -c "$1" | wc -c | awk '{$1=$1/1024; print "Estimated size:", $1, "Kb";}'
}

function docker_remove_images() {
  docker rmi "$(docker images -q)"
}

function git_search() {
  git grep "$1" "$(git rev-list --all)"
}

# Use ~/.ssh/config to prompt a list of host and ssh to the chosen one
ssh_connect() {
  local host
  host=$(grep "^Host " ~/.ssh/config | awk '{print $2}' | fzf --prompt="SSH Host: ")
  if [[ -n "$host" ]]; then
    echo "Connecting to $host..."
    ssh "$host"
  fi
}

# custom alias
alias got="ps aux | grep"
alias grep="grep --color=auto"
alias json="python -m json.tool"
alias pipgrep="pip freeze | grep"
alias py3="python3"
alias py="python"
alias vi="vim"
alias weather="curl http://wttr.in/Lima"
alias g="git"
alias wow="git status"
alias cat="bat"
alias ls="eza --icons"
alias k="kubectl"
# Use z zsh plugin as autojump
alias j="z"

# enable mise
eval "$(mise activate zsh)"

# load custom OS code
if [ "$(uname -s)" = "Darwin" ]; then
  source "$(dirname "$0")/mac.sh"
else
  source "$(dirname "$0")/linux.sh"
fi

function gitpr {
  git fetch upstream "refs/pull/$1/head:PR$1"
  git checkout "PR$1"
}

function video_to_gif {
  ffmpeg -i "$1" "$2" -hide_banner
}

# k8s helpers

function pick_namespace() {
  kubectl get ns | sed 1d | awk '{print $1}' | fzf --header="Select namespace"
}

function pick_pod() {
  kubectl get pods -n "$1" | sed 1d | awk '/Running/ {print $1}' | fzf --header="Select pod"
}

function pod_shell {
  local namespace
  namespace=$(pick_namespace)
  local pod
  pod=$(pick_pod "$namespace")
  echo "Connecting to $pod"

  if [ -z "${1:-}" ]; then
    kubectl -n "$namespace" exec -ti "$pod" -- bash 2>/dev/null || kubectl -n "$namespace" exec -ti "$pod" -- sh
  else
    kubectl -n "$namespace" exec -ti "$pod" -- "$1"
  fi
}

testpod() {
  kubectl run "testpod-$(date +%s)" --rm -it --image=debian:13 -- bash
}

function pod_proxy {
  local namespace
  namespace=$(pick_namespace)
  local pod
  pod=$(pick_pod "$namespace")
  local port_mapping
  echo "Enter port mapping using the form local_port:pod_port"
  read -r port_mapping
  echo "Setting up proxy to $pod on $port_mapping..."

  kubectl port-forward -n "$namespace" "$pod" "$port_mapping"
}

function pod_logs {
  local namespace
  namespace=$(pick_namespace)
  local pod
  pod=$(pick_pod "$namespace")
  echo "Showing logs for $pod"

  kubectl -n "$namespace" logs -f "$pod"
}

function k8s_change_context {
  local context
  context=$(kubectl config get-contexts --output='name' | fzf --header="Select context")
  echo "Changing to $context"
  kubectl config use-context "$context"
}

ck8s() {
  kubectl config current-context
}

alias tn="tmuxifier new-session"

function tl {
  local selection
  selection=$(ls ~/.tmuxifier/layouts | fzf --header="Select session")
  if [[ $selection != "" ]]; then
    local session_name
    session_name=$(echo "$selection" | awk '{split($0, a, "."); print a[1]}')

    tmuxifier load-session "$session_name"
  fi
}

# my jump, it's a similar concept of jump command but search into ~/Code folder
# and create a new tmux session with the selected project folder
function mj {
  local project_path
  project_path=$(tree ~/Code -L 2 --noreport -d -f -i | fzf)
  local org
  org=$(echo "$project_path" | awk '{split($0, a, "/"); print a[5]}')
  local project
  project=$(echo "$project_path" | awk '{split($0, a, "/"); print a[6]}')
  local session_name="$org/$project"

  # prevent tmux complain about being already inside a tmux session
  export TMUX=

  if [ "$session_name" = "" ]; then
    exit 1
  fi

  if ! tmux has-session -t "$session_name"; then
    tmux new-session -s "$session_name" -d
  fi

  # switch to session
  tmux switch-client -t "$session_name"

  window=${session_name}:0
  tmux send-keys -t "$window" "cd $project_path" Enter
}

zshenv.sh

zsh/zshenv.sh · 58 lines

#!/usr/bin/env sh
# This file is always executed, even for non interactive shells so it should only contains environment variables
# Setup lang
export LANG="en_US.UTF-8"
export LC_COLLATE="en_US.UTF-8"
export LC_CTYPE="en_US.UTF-8"
export LC_MESSAGES="en_US.UTF-8"
export LC_MONETARY="en_US.UTF-8"
export LC_NUMERIC="en_US.UTF-8"
export LC_TIME="en_US.UTF-8"
export LC_ALL=
export EDITOR=vim

# Add homebrew binaries PATH
export PATH="$PATH":/usr/local/bin/

# Go binaries
export PATH="$PATH":"$HOME/go/bin"

# Homebrew binaries
export PATH="/opt/homebrew/bin/:$PATH"

# Use binaries from ~/.local/bin
# haskell stack install some binaries here: hindent, hlint, etc
export PATH="$PATH:$HOME/.local/bin"

# --without-javac will disable java requirement for erlang compilation
# --enable-m64-build will pass a flag to gcc about how to build 64 bits binaries, fixes an error with make while compiling erlang using kerl
export KERL_CONFIGURE_OPTIONS="--disable-debug --without-javac --enable-m64-build"

# Tell kerl to build documentation when compiling erlang
export KERL_BUILD_DOCS="yes"

# Always save iex session history
export ERL_AFLAGS="-kernel shell_history enabled"

# enhance man pages using bat
export MANPAGER="sh -c 'col -bx | bat -l man -p'"

if [ -d "$HOME/.ghcup/" ]; then
  export PATH="$HOME/.cabal/bin:$HOME/.ghcup/bin:$PATH"
fi

# opam configuration
test -r "$HOME/.opam/opam-init/init.zsh" && . "$HOME/.opam/opam-init/init.zsh" >/dev/null 2>/dev/null

# this will load credentials from a private file what won't be commited
test -r "$HOME/.zshenv.private" && . "$HOME/.zshenv.private"

if [ -d "$HOME/.tmuxifier/" ]; then
  export PATH="$HOME/.tmuxifier/bin:$PATH"
fi

# Disable telemetry in homebrew
export HOMEBREW_NO_ANALYTICS=1

# disable on-my-zsh auto update
zstyle ':omz:update' mode disabled

zshrc.sh

zsh/zshrc.sh · 32 lines

#!/usr/bin/env bash
mkdir -p ~/.config/zsh

function load_zsh_plugin() {
  url=$1
  loader=$2
  plugin_dir=$(echo "$url" | awk -F'[/.]' '{print $(NF-1)}')

  if [ ! -d "$HOME/.config/zsh/$plugin_dir" ]; then
    git clone --depth 1 "$url" "$HOME/.config/zsh/$plugin_dir" || return
  fi

  . "$HOME/.config/zsh/$plugin_dir/$loader"
}

load_zsh_plugin "https://github.com/ohmyzsh/ohmyzsh.git" "oh-my-zsh.sh"
load_zsh_plugin "https://github.com/zsh-users/zsh-syntax-highlighting.git" "zsh-syntax-highlighting.zsh"
load_zsh_plugin "https://github.com/mafredri/zsh-async.git" "async.zsh"
load_zsh_plugin "https://github.com/sindresorhus/pure.git" "pure.zsh"
load_zsh_plugin "https://github.com/agkozak/zsh-z.git" "zsh-z.plugin.zsh"
load_zsh_plugin "https://github.com/Aloxaf/fzf-tab.git" "fzf-tab.plugin.zsh"

fpath+=("$HOME/.config/zsh/pure")
# setup pure-prompt
autoload -U promptinit
promptinit
prompt pure

# load custom stuff per OS
source ~/.config/zsh/main.sh

nerdfetch