Emacs-Configuration-Modern/init.el

1421 lines
60 KiB
EmacsLisp

;; -*- lexical-binding: t; -*-
(menu-bar-mode 0)
(tool-bar-mode 0)
(scroll-bar-mode 0)
(column-number-mode 1)
(setq package-native-compile t)
(setq use-package-verbose t)
(setq use-package-compute-statistics t)
;; Word wrapping
(global-visual-line-mode 1)
(setq-default visual-line-fringe-indicators '(left-curly-arrow right-curly-arrow))
;; Minibuffer key chord hints should show up instantly
(setq echo-keystrokes 0.01)
;; Auto delete trailing whitespace - Emacs 31+
(add-hook 'prog-mode-hook #'delete-trailing-whitespace-mode)
(add-hook 'text-mode-hook #'delete-trailing-whitespace-mode)
;; Set custom tab width here
(defvar custom-tab-width 4)
;; Disable bell/messages sound on windows
(when (eq window-system 'w32)
(set-message-beep 'silent))
;; Temporary file config
(setq backup-directory-alist
`((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms
`((".*" ,temporary-file-directory t)))
;; Fixes to stop weird window geometry issues
(setq frame-inhibit-implied-resize t)
(setq frame-resize-pixelwise t)
;; Paren matching
(setq show-paren-context-when-offscreen 'overlay)
(show-paren-mode 1)
;; Ensure that tabs are default over spaces
(setq-default tab-width custom-tab-width)
(setq tab-width custom-tab-width)
(setq-default indent-tabs-mode 't)
(setq indent-tabs-mode 't)
(advice-add 'indent-to :around
(lambda (orig-fun column &rest args)
(when (eq indent-tabs-mode 'only)
(setq column (* tab-width (round column tab-width))))
(apply orig-fun column args)))
(defvaralias 'c-basic-offset 'tab-width)
(electric-indent-mode 0)
;; Hide all minor modes from the modeline
(setq mode-line-modes
(mapcar (lambda (elem)
(pcase elem
(`(:propertize (,_ minor-mode-alist . ,_) . ,_) "")
(t elem)))
mode-line-modes))
; Hide other items
(line-number-mode -1)
(column-number-mode -1)
(setq mode-line-percent-position nil)
(with-eval-after-load 'evil
(setq evil-mode-line-format nil))
; Emacs 31 feature only - Collapse minor modes into one
(setq mode-line-collapse-minor-modes '(not))
;; Clipboard config
(setopt select-active-regions nil)
(setq select-enable-clipboard t) ; Syncs Emacs 'kill' with system clipboard
(setq select-enable-primary t) ; Syncs with the primary selection (middle-click)
(setq save-interprogram-paste-before-kill t) ; Save existing clipboard to kill ring before overwriting
(setopt interprogram-cut-function #'gui-select-text)
;; Midnight Mode
; Automatically clean up old unused buffers
(midnight-mode 1)
;; Font configuration
; Global font
(set-frame-font "MesloLGL Nerd Font 20")
; Buffer fonts
(defun set-buffer-local-font ()
"Set font for normal buffers."
(face-remap-add-relative 'default :height 260))
(add-hook 'prog-mode-hook #'set-buffer-local-font)
(add-hook 'text-mode-hook #'set-buffer-local-font)
; Emacsclient new frame fonts
(setq default-frame-alist '((font . "MesloLGL Nerd Font 20")))
;; Emacsclient Configuration
(defun client-config ()
"Ran on new emacslient frame creations."
(scroll-bar-mode 0))
(add-hook 'after-make-frame-functions #'(lambda (frame)
(select-frame frame)
(client-config)))
;; Ensure TAB key ACTUALLY works like a TAB key
(defun my/complete-or-tab ()
"Complete at point or insert a tab if no characters before point."
(interactive)
(if (and (char-before) (not (member (char-before) '(?\s ?\t ?\n)))) ; Check for whitespace, tabs or newline
(completion-at-point) ; If yes, do completion at point
(insert-tab))) ; If no, insert a tab character
(global-set-key (kbd "TAB") 'my/complete-or-tab)
(define-key global-map [remap backward-delete-char-untabify] 'backward-delete-char)
;; Track recently opened files
(recentf-mode 1)
;; Stop weird C-9 keybindings causing conflicts on typo
(define-key global-map [remap digit-argument] "")
;; Optimisation
; GCMH
;Enforce a sneaky Garbage Collection strategy to minimize GC interference with user activity.
;During normal use a high GC threshold is set.
;When idling GC is triggered and a low threshold is set.
;This greatly improves performance/startup time of emacs.
(use-package gcmh
:ensure t
:init
(gcmh-mode 1)
;; Ensure garbage collector does not run TOO frequently which causes random freezes
(defun gcmh-mode-optimisation-setup-hook ()
(setq gc-cons-threshold most-positive-fixnum))
(defun gcmh-mode-optimisation-exit-hook ()
(setq gc-cons-threshold 800000))
(add-hook 'minibuffer-setup-hook 'gcmh-mode-optimisation-setup-hook)
(add-hook 'minibuffer-exit-hook 'gcmh-mode-optimisation-exit-hook))
; Fontification
(jit-lock-mode 1)
(jit-lock-debug-mode 1)
(setq jit-lock-stealth-time 1.25)
(setq jit-lock-stealth-nice 0.5) ;; Seconds between font locking.
(setq jit-lock-chunk-size 4096)
(setq jit-lock-defer-time 0)
(with-eval-after-load 'evil
(add-hook 'evil-insert-state-entry-hook
(lambda ()
(setq jit-lock-defer-time 0.25)) nil t)
(add-hook 'evil-insert-state-exit-hook
(lambda ()
(setq jit-lock-defer-time 0)) nil t))
;; TRAMP
;TRAMP edit files over SSH configuration
(use-package tramp
:ensure t
:config
;; To speed up connections
(setq tramp-verbose 0
tramp-chunksize 2000
tramp-use-ssh-controlmaster-options nil
tramp-default-method "ssh"
tramp-verbose 1
tramp-default-remote-shell "/bin/sh"
tramp-connection-local-default-shell-variables
'((shell-file-name . "/bin/bash")
(shell-command-switch . "-c")))
(add-to-list 'tramp-remote-path 'tramp-own-remote-path)
(when (eq window-system 'w32)
(setq tramp-default-method "plink")))
; Tramp RPC - Alternate backend promising more speed
(use-package tramp-rpc
:ensure (:host github :repo "ArthurHeymans/emacs-tramp-rpc")
:after tramp
:config
(setq tramp-rpc-deploy-local-cache-directory
"~/.cache/emacs/tramp-rpc-binaries"))
;; Org Mode Config
; Agenda
(defun org-agenda-toggle-day-view ()
"Toggle between daily and weekly view in org agenda."
(interactive)
(if (eq org-agenda-current-span 'day)
(org-agenda-week-view)
(org-agenda-day-view)))
;For Org-Agenda, you can set a location of your Org Agenda file here. Set Agenda Directory:
(use-package org
:ensure nil
;:demand t ; Setting org mode to be deferred absolutely breaks everything, with org-agenda-mode-map error. Please switch to eval after load maybe instead.
:config
;Fold settings
;Org Mode historically used overlays to hide text, but newer versions moved toward text properties for better performance in large files. This change occasionally causes "stuck" visibility where sub-headers remain hidden until the parent is fully cycled.
(setq org-fold-core-style 'overlays)
(setq org-startup-folded t)
(setq org-hide-emphasis-markers nil))
(with-eval-after-load 'org
(with-eval-after-load 'evil
; Unbind stubborn org mode bindings
(define-key org-mode-map (kbd "M-h") nil)
(define-key org-mode-map (kbd "M-l") nil)
(evil-define-key* '(normal) org-mode-map (kbd "M-h") nil)
(evil-define-key* '(normal) org-mode-map (kbd "M-l") nil)))
(with-eval-after-load 'org-agenda
(with-eval-after-load 'evil
(setq org-agenda-files '("~/Nextcloud/Agenda"))
;This is will integrate the Calendar/Diary into Org-Agenda, so you can get access to dates on public holidays etc. Set diary to true:
(setq org-agenda-include-diary t)
;Ensure done date/closed timestamps are logged
(setq org-log-done 'time)
;Ensure state changes are logged into logbook
(setq org-log-into-drawer "LOGBOOK")
;Ensure agenda still shows DONE items
(setq org-agenda-skip-scheduled-if-done nil)
(setq agenda-skip-deadline-if-done nil)
;Autostart in day view
(setq org-agenda-span 'day)
;Auto save agenda buffers on refresh
(advice-add 'org-agenda-redo :before #'org-save-all-org-buffers)
(add-hook 'org-agenda-mode-hook #'(lambda ()
(org-agenda-entry-text-mode 1)))
(define-key org-agenda-mode-map (kbd "C-j") 'org-agenda-goto-date)
(define-key org-agenda-mode-map "j" 'evil-next-line)
(define-key org-agenda-mode-map "k" 'evil-previous-line)
(define-key org-agenda-mode-map (kbd "V") nil)
(define-key org-agenda-mode-map (kbd "V") 'evil-visual-line)
(define-key org-agenda-mode-map "g" nil)
(define-key org-agenda-mode-map (kbd "g g") 'evil-goto-first-line)
(define-key org-agenda-mode-map (kbd "G") 'evil-goto-line)
(define-key org-agenda-mode-map "s" 'org-agenda-schedule)
(define-key org-agenda-mode-map "d" 'org-agenda-schedule)
(define-key org-agenda-mode-map (kbd "C-d") 'org-agenda-toggle-day-view)
(define-key org-agenda-mode-map "p" 'org-pomodoro)))
; For Org Pomodoro notification sound
(use-package sound-wav
:ensure t
:demand t)
(with-eval-after-load 'sound-wav
(defun sound-alert (alert)
"Play a sound notification and show message ALERT."
(sound-wav-play (expand-file-name (concat user-emacs-directory "elpaca/builds/org-pomodoro/resources/bell.wav")))
(message alert))
; Org timer custom alert
(defun org-timer-sound-alert ()
"Sound notification on org timer finish."
(sound-alert "Timer done!"))
(add-hook 'org-timer-done-hook 'org-timer-sound-alert))
; Org Pomodoro
(use-package org-pomodoro
:ensure t
:after (org org-agenda sound-wav)
:config
(setq org-pomodoro-manual-break t)
(setq org-pomodoro-keep-killed-pomodoro-time t)
(setq org-pomodoro-play-sounds t)
(setq org-pomodoro-ticking-sound-p nil)
(setq org-pomodoro-audio-player "mpv"))
(use-package alert
:ensure t
:config
(alert-define-style 'custom-org-alert-notification
:title "Custom Notification"
:notifier
(lambda (info)
; 'info' is a plist containing :title, :message, :severity, etc.
(notifications-notify
:title "Emacs Alert"
:body (plist-get info :message))
(sound-alert (concat "ORG ALERT: "
;(plist-get info :title)
(plist-get info :message)))))
(setq alert-default-style 'custom-org-alert-notification))
; System notifications of org agenda items
(use-package org-wild-notifier
:ensure t
:after org-agenda
:config
;; Notifications fire N minutes before an event
(setq org-wild-notifier-alert-time '(1 10 30))
;; Notification title and icon
(setq org-wild-notifier-notification-title "Org Reminder")
;(setq org-wild-notifier-notification-icon "/path/to/icon.png")
;; Remind about day-wide events at 10am and 8pm
(setq org-wild-notifier-day-wide-alert-times '("10:00" "20:00"))
;; Alert severity: 'high, 'medium, or 'low
(setq org-wild-notifier--alert-severity 'high)
;; Pass additional arguments to alert
(setq org-wild-notifier-extra-alert-plist '(:persistent t))
(org-wild-notifier-mode))
; Upcoming agenda items in modeline
(use-package org-upcoming-modeline
:ensure t
:after org-agenda
:config
(org-upcoming-modeline-mode))
; Org Mode Journalling
(use-package org-journal
:ensure t
:after org-agenda
:config
(setq org-journal-dir "~/Nextcloud/Journal/")
(setq org-journal-file-type 'daily)
(setq org-journal-file-format "%Y%m%d.org"))
;; Org Latex Preview Scale
; Automatically toggle Org mode LaTeX fragment previews as the cursor enters and exits them
(use-package org-fragtog
:ensure t
:after org
:config
(add-hook 'org-mode-hook 'org-latex-preview)
(add-hook 'org-mode-hook 'org-fragtog-mode)
(setq org-format-latex-options (plist-put org-format-latex-options :scale 3.0)))
;; Emacs minibuffer configurations.
(use-package emacs
:ensure nil
:custom
(tab-always-indent 'complete)
;; Enable context menu. `vertico-multiform-mode' adds a menu in the minibuffer
;; to switch display modes.
(context-menu-mode t)
;; Support opening new minibuffers from inside existing minibuffers.
(enable-recursive-minibuffers t)
;; Hide commands in M-x which do not work in the current mode. Vertico
;; commands are hidden in normal buffers. This setting is useful beyond
;; Vertico.
(read-extended-command-predicate #'command-completion-default-include-p)
;; Do not allow the cursor in the minibuffer prompt
(minibuffer-prompt-properties
'(read-only t cursor-intangible t face minibuffer-prompt)))
;; Doom Theme
(use-package doom-themes
:ensure t
:config
;; Global settings (defaults)
(setcdr (assoc 'gnus-group-news-low-empty doom-themes-base-faces)
'(:inherit 'gnus-group-mail-1-empty :weight 'normal))
(setq
doom-themes-enable-bold t ; if nil, bold is universally disabled
doom-themes-enable-italic t) ; if nil, italics is universally disabled
(load-theme 'doom-gruvbox-light t))
;; Autocompletion configuration
;(use-package ido-vertical-mode
; :ensure t
; :config (ido-vertical-mode 1))
;; Enable Vertico.
(use-package vertico
:ensure t
;;:custom
;; (vertico-scroll-margin 0) ;; Different scroll margin
;; (vertico-count 20) ;; Show more candidates
;; (vertico-resize t) ;; Grow and shrink the Vertico minibuffer
;; (vertico-cycle t) ;; Enable cycling for `vertico-next/previous'
:config
(define-key vertico-map (kbd "C-k") 'vertico-previous)
(define-key vertico-map (kbd "C-j") 'vertico-next)
:init
(vertico-mode))
;; Fuzzy matching for completion frameworks like Vertico
(use-package orderless
:ensure t
:custom
(completion-styles '(orderless flex basic))
(completion-category-overrides '((file (styles partial-completion))))
(completion-pcm-leading-wildcard t)) ;; Emacs 31: partial-completion behaves like substring
;; Enhanced commands that Vertico can make use of
(use-package consult
:ensure t
:config
(define-key global-map [remap isearch-forward] 'consult-ripgrep)
(define-key global-map [remap switch-to-buffer] 'consult-buffer)
(define-key global-map [remap list-buffers] 'consult-buffer)
(define-key global-map [remap set-fill-column] 'consult-fd)
(define-key global-map [remap org-agenda-filter] 'consult-line)
(define-key global-map [remap eshell-previous-matching-input] 'consult-history))
;; Persist history over Emacs restarts. Vertico sorts by history position.
(use-package savehist
:ensure nil
:init
(savehist-mode))
;; Corfu Autocomplete in buffer
(use-package corfu
:ensure t
:hook
(prog-mode . corfu-mode)
(html-ts-mode . corfu-mode)
(css-ts-mode . corfu-mode)
;; (shell-mode . corfu-mode)
;; (eshell-mode . corfu-mode))
:config
(setq
auto nil
corfu-preselect 'first))
;;(global-corfu-mode)
;; Evil Config
; Vim Bindings
(use-package evil
:ensure t
:bind
(("<escape>" . keyboard-escape-quit)
:map evil-normal-state-map
("U" . undo-redo)
:map evil-insert-state-map
("C-g" . evil-force-normal-state)
:map evil-motion-state-map
([remap evil-search-forward] . 'consult-line))
:init
(setq evil-want-keybinding nil)
(evil-mode 1))
; Evil Collection, configs evil bindings for more packages
(use-package evil-collection
:after evil
:ensure t
:config
(setq evil-collection-setup-minibuffer t)
(evil-collection-init))
;; Proced config
(with-eval-after-load 'proced
(define-key proced-mode-map [remap proced-undo] 'proced-update))
;; Eshell config
(with-eval-after-load 'eshell-mode
(with-eval-after-load 'evil
; Unbind stubborn eshell bindings
(define-key eshell-mode-map (kbd "M-o") nil)
(define-key eshell-mode-map (kbd "M-p") nil)
(define-key eshell-mode-map (kbd "M-h") nil)
(define-key eshell-mode-map (kbd "M-l") nil)
(evil-define-key* '(normal) eshell-mode-map (kbd "M-o") nil)
(evil-define-key* '(normal) eshell-mode-map (kbd "M-p") nil)
(evil-define-key* '(normal) eshell-mode-map (kbd "M-h") nil)
(evil-define-key* '(normal) eshell-mode-map (kbd "M-l") nil)))
; Like zoxide
(use-package eshell-z
:ensure t
:after tramp
:config
(add-hook 'eshell-mode-hook
(defun my-eshell-mode-hook ()
(require 'eshell-z))))
; Bash completion commands in eshell
(use-package bash-completion
:ensure t
:init
(autoload 'bash-completion-dynamic-complete
"bash-completion"
"BASH completion hook")
(add-hook 'shell-dynamic-complete-functions
'bash-completion-dynamic-complete)
(add-hook 'eshell-mode-hook
(lambda ()
(add-hook 'completion-at-point-functions
'bash-completion-capf-nonexclusive nil t))))
; Import bash aliases to eshell
(defun eshell-load-bash-aliases ()
"Read Bash aliases and add them to the list of eshell aliases."
;; Bash needs to be run - temporarily - interactively
;; in order to get the list of aliases.
(with-temp-buffer
(call-process "bash" nil '(t nil) nil "-ci" "alias")
(goto-char (point-min))
; Bash aliases to exclude
(flush-lines "^alias magit\\|^alias oc\\|^alias z\\|^alias zi\\|^alias cd\\|^alias cdi\\|^alias sudo")
(while (re-search-forward "alias \\(.+\\)='\\(.+\\)'$" nil t)
(add-to-list 'eshell-command-aliases-list (list (match-string 1) (match-string 2))))))
;; We only want Bash aliases to be loaded when Eshell loads its own aliases,
;; rather than every time `eshell-mode' is enabled.
(add-hook 'eshell-first-time-mode-hook 'eshell-load-bash-aliases t)
; Eat is a terminal emulator that can integrate nicely with eshell, allows us to run full ncurses applications inside eshell.
(use-package eat
:ensure t
:init
;; For `eat-eshell-mode'.
(add-hook 'eshell-load-hook #'eat-eshell-mode)
;; For `eat-eshell-visual-command-mode'.
(add-hook 'eshell-load-hook #'eat-eshell-visual-command-mode))
; New create new eshell instance
(defun eshell/new ()
(interactive)
(eshell t))
;; Sudo edit command to escalate priv if need be
(use-package sudo-edit
:ensure t
:config
(add-hook 'before-save-hook 'sudo-before-save-hook))
(setq tramp-auto-save-directory "~/.emacs.d/tramp-autosave")
;; Magit
(use-package transient
:ensure t)
(use-package magit
:ensure t
:after transient)
;; Auth Source Pass - Use pass for authentication with mail
(use-package auth-source-pass
:ensure nil
:init
(auth-source-pass-enable))
;; Mu4e Mail Config
(when (executable-find "mu")
(use-package mu4e
:ensure nil
:after (auth-source-pass consult)
:config
(define-key global-map [remap mu4e-search-narrow] 'consult-line)
(setq mu4e-mu-binary (executable-find "mu"))
;; This is set to 't' to avoid mail syncing issues when using mbsync
(setq mu4e-change-filenames-when-moving t)
;; Refresh mail using isync every 10 minutes
;(setq mu4e-update-interval (* 10 60))
(setq mu4e-get-mail-command "mbsync -a")
(setq mu4e-maildir "~/Mail")
;; Sending mail function
(setq message-send-mail-function 'smtpmail-send-it)
;; Make sure plain text mails flow correctly for recipients
(setq mu4e-compose-format-flowed t)
;; Use completing read AKA vertico
(setq mu4e-completing-read-function #'completing-read)
;; Immediately send SMTP with credentials
(setq smtpmail-servers-requiring-authorization "*")
(setq mu4e-contexts
(list
;; Work account
(make-mu4e-context
:name "A CSpark Work"
:match-func
(lambda (msg)
(when msg
(string-prefix-p "/work-cspark" (mu4e-message-field msg :maildir))))
:vars '((user-mail-address . "work@cspark.dev")
(user-full-name . "Curt Spark (Work)")
(smtpmail-smtp-server . "mail.cspark.dev")
(smtpmail-smtp-user . "work@cspark.dev")
(smtpmail-smtp-service . 587)
(smtpmail-stream-type . starttls)
(mu4e-compose-signature . "- Curt")
(mu4e-inbox-folder . "/work-cspark/Inbox")
(mu4e-drafts-folder . "/work-cspark/Drafts")
(mu4e-sent-folder . "/work-cspark/Sent Mail")
(mu4e-trash-folder . "/work-cspark/Trash")))
;; Services account
(make-mu4e-context
:name "B CSpark Services"
:match-func
(lambda (msg)
(when msg
(string-prefix-p "/services-cspark" (mu4e-message-field msg :maildir))))
:vars '((user-mail-address . "services@cspark.dev")
(user-full-name . "Curt Spark (services)")
(smtpmail-smtp-server . "mail.cspark.dev")
(smtpmail-smtp-user . "services@cspark.dev")
(smtpmail-smtp-service . 587)
(smtpmail-stream-type . starttls)
(mu4e-compose-signature . "- Curt")
(mu4e-inbox-folder . "/services-cspark/Inbox")
(mu4e-drafts-folder . "/services-cspark/Drafts")
(mu4e-sent-folder . "/services-cspark/Sent Mail")
(mu4e-trash-folder . "/services-cspark/Trash")))
;; Personal account
(make-mu4e-context
:name "C CSpark Personal"
:match-func
(lambda (msg)
(when msg
(string-prefix-p "/personal-cspark" (mu4e-message-field msg :maildir))))
:vars '((user-mail-address . "personal@cspark.dev")
(user-full-name . "Curt Spark (personal)")
(smtpmail-smtp-server . "mail.cspark.dev")
(smtpmail-smtp-user . "personal@cspark.dev")
(smtpmail-smtp-service . 587)
(smtpmail-stream-type . starttls)
(mu4e-compose-signature . "- Curt")
(mu4e-inbox-folder . "/personal-cspark/Inbox")
(mu4e-drafts-folder . "/personal-cspark/Drafts")
(mu4e-sent-folder . "/personal-cspark/Sent Mail")
(mu4e-trash-folder . "/personal-cspark/Trash")))
;; Alerts account
(make-mu4e-context
:name "D CSpark Alerts"
:match-func
(lambda (msg)
(when msg
(string-prefix-p "/alerts-cspark" (mu4e-message-field msg :maildir))))
:vars '((user-mail-address . "alerts@cspark.dev")
(user-full-name . "Curt Spark (alerts)")
(smtpmail-smtp-server . "mail.cspark.dev")
(smtpmail-smtp-user . "alerts@cspark.dev")
(smtpmail-smtp-service . 587)
(smtpmail-stream-type . starttls)
(mu4e-compose-signature . "- Curt")
(mu4e-inbox-folder . "/alerts-cspark/Inbox")
(mu4e-drafts-folder . "/alerts-cspark/Drafts")
(mu4e-sent-folder . "/alerts-cspark/Sent Mail")
(mu4e-trash-folder . "/alerts-cspark/Trash")))
;; Tuxtank Services account
(make-mu4e-context
:name "E Tuxtank Services"
:match-func
(lambda (msg)
(when msg
(string-prefix-p "/personal-tuxtank" (mu4e-message-field msg :maildir))))
:vars '((user-mail-address . "personal@tuxtank.dev")
(user-full-name . "Tuxtank (personal)")
(smtpmail-smtp-server . "mail.tuxtank.dev")
(smtpmail-smtp-user . "personal@tuxtank.dev")
(smtpmail-smtp-service . 587)
(smtpmail-stream-type . starttls)
(mu4e-compose-signature . "- Tuxtank")
(mu4e-inbox-folder . "/personal-tuxtank/Inbox")
(mu4e-drafts-folder . "/personal-tuxtank/Drafts")
(mu4e-sent-folder . "/personal-tuxtank/Sent Mail")
(mu4e-trash-folder . "/personal-tuxtank/Trash")))
;; Tuxtank Alerts account
(make-mu4e-context
:name "F Tuxtank Alerts"
:match-func
(lambda (msg)
(when msg
(string-prefix-p "/alerts-tuxtank" (mu4e-message-field msg :maildir))))
:vars '((user-mail-address . "alerts@tuxtank.dev")
(user-full-name . "Tuxtank (alerts)")
(smtpmail-smtp-server . "mail.tuxtank.dev")
(smtpmail-smtp-user . "alerts@tuxtank.dev")
(smtpmail-smtp-service . 587)
(smtpmail-stream-type . starttls)
(mu4e-compose-signature . "- Tuxtank")
(mu4e-inbox-folder . "/alerts-tuxtank/Inbox")
(mu4e-drafts-folder . "/alerts-tuxtank/Drafts")
(mu4e-sent-folder . "/alerts-tuxtank/Sent Mail")
(mu4e-trash-folder . "/alerts-tuxtank/Trash")))))))
;(setq mu4e-maildir-shortcuts
; '(("/Gmail/Inbox" . ?i)
; ("/Gmail/[Gmail]/Sent Mail" . ?s)
; ("/Gmail/[Gmail]/Trash" . ?t)
; ("/Gmail/[Gmail]/Drafts" . ?d)
; ("/Gmail/[Gmail]/All Mail" . ?a)))
; Elfeed RSS Reader
(use-package elfeed
:ensure t
:config
(define-key global-map [remap elfeed-search-untag-all-unread] 'elfeed-update)
(setq elfeed-feeds
'(("https://www.reddit.com/r/Clamworks.rss" reddit clamworks)
("https://www.reddit.com/r/Losercity.rss" reddit losercity)
("https://www.reddit.com/r/Shark_Park.rss" reddit sharkpark)
("https://www.reddit.com/r/emacs.rss" reddit emacs))))
(use-package elfeed-goodies
:ensure t
:after elfeed
:config
(elfeed-goodies/setup)
(setq elfeed-goodies/entry-pane-position 'bottom)
(setq elfeed-goodies/entry-pane-size 0.6))
;; empv - YouTube search frontend, media library manager
(use-package empv
:ensure t
:config
(add-to-list 'empv-mpv-args "--ytdl-format=bestvideo+bestaudio/best[ext=mp4]/best")
(setq empv-youtube-use-tabulated-results t)
(setq empv-invidious-instance 'ivjs)
(define-key empv-youtube-results-mode-map (kbd "<return>") 'empv-play-media-at-point))
;; EMMS - Emacs Music Player
(use-package emms
:ensure t
:after (evil evil-collection)
:bind*
(([remap emms-isearch-buffer] . consult-line)
:map emms-browser-mode-map
("g g" . evil-goto-first-line)
("G" . evil-goto-line)
("w" . evil-forward-word-begin)
("b" . evil-backward-word-begin)
("h" . evil-backward-char)
("j" . evil-next-line)
("k" . evil-previous-line)
("l" . evil-forward-char)
("x" . emms-pause))
:config
(setq emms-source-file-default-directory (expand-file-name "~/Spool2_Secret/Music/Flac"))
(setq emms-player-mpd-server-name "localhost")
(setq emms-player-mpd-server-port "6600")
(setq emms-player-mpd-music-directory "~/Spool2_Secret/Music/Flac")
(setq emms-source-file-directory-tree-function 'emms-source-file-directory-tree-find)
(setq emms-cache-set-all-from-mpd t)
(setq emms-browser-makes-thumbnails t)
(setq emms-browser-thumbnail-small-width 100)
(setq emms-mode-line-length-limit 20)
(emms-all)
(emms-player-mpd-connect)
(add-hook 'emms-playlist-cleared-hook 'emms-player-mpd-clear)
(setq emms-player-list '(emms-player-mpd))
(setq emms-player-mpd-supported-regexp "\\`http[s]?://\\|\\.\\([Mm]3[Uu]\\|[Oo][Gg][Gg]\\|[Ff][Ll][Aa][Cc]\\|[Mm][Pp]3\\|[Ww][Aa][Vv]\\|[Mm][Oo][Dd]\\|[Aa][Uu]\\|[Aa][Ii][Ff][Ff]\\|[Oo][Pp][Uu][Ss]\\)\\'")
(add-to-list 'emms-info-functions 'emms-info-mpd)
(define-key emms-playlist-mode-map (kbd "d") nil)
(evil-define-key* '(normal) emms-playlist-mode-map (kbd "d") nil)
(evil-define-key* '(normal) emms-playlist-mode-map (kbd "d d") #'emms-playlist-mode-kill-track)
(evil-define-key* '(visual) emms-playlist-mode-map (kbd "d") #'emms-playlist-mode-kill)
(evil-define-key* '(normal) emms-playlist-mode-map (kbd "q") #'emms-browser-bury-buffer))
;; Embr.el Emacs web browser
;(use-package embr
; :defer t
; :after evil
; :ensure (:host github
; :repo "emacs-os/embr.el"
; :files ("*.el" "*.py" "*.sh" "native/*.c" "native/Makefile"))
; :hook (embr-mode . embr-vimium-mode)
; (evil-mode . (lambda () (turn-off-evil-mode)))
; :config
; (setq embr-hover-rate 30
; embr-default-width 1919
; embr-default-height 950
; embr-screen-width 1920
; embr-screen-height 1080
; embr-color-scheme 'dark
; embr-search-engine 'google
; embr-scroll-method 'instant
; embr-scroll-step 100
; embr-frame-source 'screencast
; embr-render-backend 'canvas
; embr-display-method 'headless
; embr-home-url "about:blank"
; embr-session-restore t
; embr-tab-bar t
; embr-proxy-rules nil))
;(with-eval-after-load 'embr
; (defun embr--start-daemon-overridden ()
; "Start the Python daemon process OVERRIDDEN for NixOS support.
; Respects `embr-display-method' for display modes."
; (when (and embr--process (process-live-p embr--process))
; (delete-process embr--process))
; (setq embr--response-buffer "")
; (let* ((inner (list embr-python embr-script))
; (xvfb (and (eq embr-display-method 'headed-offscreen)
; (executable-find "xvfb-run")))
; (command
; (if xvfb
; (append (list xvfb "--auto-servernum"
; "--server-args=-screen 0 1920x1080x24")
; inner)
; (when (and (eq embr-display-method 'headed-offscreen)
; (not (executable-find "xvfb-run")))
; (message "embr: xvfb-run not found, falling back to headless"))
; inner))
; (process-environment
; (append (list "LD_LIBRARY_PATH=/run/current-system/sw/share/nix-ld/lib"
; (format "EMBR_DISPLAY=%s"
; (if xvfb "headed-offscreen"
; (symbol-name embr-display-method))))
; process-environment)))
; (setq embr--process
; (make-process
; :name "embr"
; :command command
; :connection-type 'pipe
; :noquery t
; :stderr (get-buffer-create "*embr-stderr*")
; :filter #'embr--process-filter
; :sentinel #'embr--process-sentinel))
; (process-put embr--process 'embr-buffer (current-buffer))))
; (advice-add 'embr--start-daemon :override #'embr--start-daemon-overridden))
;; Emacs everywhere - Use emacs for any text input
(use-package emacs-everywhere
:ensure t)
;; Parinfer mode - We will use conventional 2 spaces instead of tabs for lisp
; Causing issues so will switch
;(use-package parinfer-rust-mode
; :ensure (:host github :repo "justinbarclay/parinfer-rust-mode")
; :hook
; ((emacs-lisp-mode . (lambda()
; (setq-local tab-width 2)
; (indent-tabs-mode -1)
; (parinfer-rust-mode)))
; (common-lisp-mode . (lambda()
; (setq-local tab-width 2)
; (indent-tabs-mode -1)
; (parinfer-rust-mode)))))
;; Evil cleverparens - We will use conventional 2 spaces instead of tabs for lisp
;(use-package evil-cleverparens
; :ensure t
; :bind
; (:map evil-cleverparens-mode-map
; ("M-d" . nil))
; :hook
; ((emacs-lisp-mode . (lambda ()
; (setq-local tab-width 2)
; (indent-tabs-mode -1)
; (electric-indent-mode 1)
; (evil-cleverparens-mode)))
; (common-lisp-mode . (lambda ()
; (setq-local tab-width 2)
; (indent-tabs-mode -1)
; (electric-indent-mode 1)))))
;; Use puni-mode globally and disable it for term-mode.
(defun emacs-lisp-config-init ()
"My personal initial configuration for Emacs Lisp buffers."
(setq-local tab-width 4)
(setq-local lisp-indent-offset 4)
(setq-local lisp-body-indent 4)
(indent-tabs-mode -1)
(electric-indent-mode 1))
(add-hook 'emacs-lisp-mode-hook #'emacs-lisp-config-init)
;; Rainbow delimiters
(use-package rainbow-delimiters
:ensure t
:hook
(prog-mode . rainbow-delimiters-mode))
;; Treesit Config
(use-package treesit
:ensure nil
:commands (treesit-install-language-grammar nf/treesit-install-all-languages)
:init
(setq treesit-language-source-alist
'((bash . ("https://github.com/tree-sitter/tree-sitter-bash"))
(c . ("https://github.com/tree-sitter/tree-sitter-c"))
(cpp . ("https://github.com/tree-sitter/tree-sitter-cpp"))
(css . ("https://github.com/tree-sitter/tree-sitter-css"))
(cmake . ("https://github.com/uyha/tree-sitter-cmake"))
(go . ("https://github.com/tree-sitter/tree-sitter-go"))
(html . ("https://github.com/tree-sitter/tree-sitter-html"))
(javascript . ("https://github.com/tree-sitter/tree-sitter-javascript"))
(json . ("https://github.com/tree-sitter/tree-sitter-json"))
(julia . ("https://github.com/tree-sitter/tree-sitter-julia"))
(lua . ("https://github.com/Azganoth/tree-sitter-lua"))
(make . ("https://github.com/alemuller/tree-sitter-make"))
(ocaml . ("https://github.com/tree-sitter/tree-sitter-ocaml" "master" "ocaml/src"))
(python . ("https://github.com/tree-sitter/tree-sitter-python"))
(php . ("https://github.com/tree-sitter/tree-sitter-php"))
(typescript . ("https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src"))
(tsx . ("https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src"))
(ruby . ("https://github.com/tree-sitter/tree-sitter-ruby"))
(rust . ("https://github.com/tree-sitter/tree-sitter-rust"))
(swift . ("https://github.com/tree-sitter/swift-tree-sitter"))
(nix . ("https://github.com/nix-community/tree-sitter-nix"))
(sql . ("https://github.com/m-novikov/tree-sitter-sql"))
(toml . ("https://github.com/tree-sitter/tree-sitter-toml"))
(zig . ("https://github.com/GrayJack/tree-sitter-zig"))))
:config ;; Credit to https://github.com/Nathan-Furnal/dotemacs/blob/df9b845563a84a927ff762e172334cf772253a44/init.el#L1154
(defun nf/treesit-install-all-languages ()
"Install all languages specified by `treesit-language-source-alist'."
(interactive)
(let ((languages (mapcar 'car treesit-language-source-alist)))
(dolist (lang languages)
(treesit-install-language-grammar lang)
(message "`%s' parser was installed." lang)
(sit-for 0.75)))))
(setq treesit-font-lock-level 4)
; Remap basic modes to treesit equivalent
(use-package bash-ts-mode
:ensure nil
:mode "\\.sh\\'")
(use-package c-ts-mode
:ensure nil
:mode "\\.c\\'")
(use-package rust-ts-mode
:ensure nil
:mode "\\.rs\\'")
(use-package swift-ts-mode
:ensure t
:mode "\\.swift\\'")
(use-package nix-ts-mode
:ensure t
:mode "\\.nix\\'")
(use-package html-ts-mode
:ensure nil
:mode "\\.html\\'")
(use-package css-ts-mode
:ensure nil
:mode "\\.css\\'")
(use-package js-ts-mode
:ensure nil
:mode "\\.js\\'")
(use-package typescript-ts-mode
:ensure nil
:mode "\\.ts\\'")
(use-package tsx-ts-mode
:ensure nil
:mode "\\.tsx\\'")
;(use-package emacs-lisp-ts-mode
; :ensure t)
; Remap base modes to their treesitter counterpart
(setq major-mode-remap-alist
'(
;(emacs-lisp-mode . emacs-lisp-ts-mode)
(c-mode . c-ts-mode)
(rust-mode . rust-ts-mode)
(nix-mode . nix-ts-mode)
(rustic-mode . rust-ts-mode)
(python-mode . python-ts-mode)
(html-mode . html-ts-mode)
(css-mode . css-ts-mode)
(js-mode . js-ts-mode)
(javascript-mode . js-ts-mode)))
;; Programming treesit style config
; Enforcing tabs as programming style
(add-hook 'prog-mode-hook
(lambda ()
(setq-local tab-width custom-tab-width)
(setq-local indent-tabs-mode 't)))
(add-hook 'nix-ts-mode-hook
(lambda ()
(setq-local nix-ts-mode-indent-offset custom-tab-width)))
(add-hook 'c-ts-mode-hook
(lambda ()
(setq-local c-ts-mode-indent-style 'linux)
(setq-local c-ts-mode-indent-offset custom-tab-width)
(c-ts-mode-toggle-comment-style -1)))
(add-hook 'rust-ts-mode-hook
(lambda ()
(setq-local rust-ts-mode-indent-offset custom-tab-width)))
;; Flymake Configuration
(use-package flymake
:ensure nil
:hook
(prog-mode . flymake-mode)
(emacs-lisp-mode . flymake-mode))
; Flyover for nice in buffer flymake error/warning messages.
(use-package flyover
:ensure t
:hook
(flymake-mode . flyover-mode)
:custom
;; Checker settings
(flyover-checkers '(flymake))
;; Display mode (controls cursor-based visibility)
(flyover-display-mode 'show-only-on-same-line)
;; Completion integration
(flyover-hide-during-completion t))
;; LSP Configuration
(use-package eglot
:ensure nil
:hook
(((
bash-ts-mode
c-ts-mode
rust-ts-mode
swift-ts-mode
nix-ts-mode
html-ts-mode
css-ts-mode
js-ts-mode
typescript-ts-mode
tsx-ts-mode)
. eglot-ensure))
:custom
(eglot-ignored-server-capabilities '(:documentOnTypeFormattingProvider)))
;; HTML Emmet Snippets
(use-package emmet-mode
:ensure t
:hook
(typescript-ts-mode . emmet-mode)
(tsx-ts-mode . emmet-mode)
(html-ts-mode . emmet-mode)
(css-ts-mode . emmet-mode)
:config
(setq emmet-indentation custom-tab-width))
;; JS Prettier Mode
(use-package prettier-js
:ensure t
:hook
(js-ts-mode . prettier-js-mode)
(typescript-ts-mode . prettier-js-mode)
(tsx-ts-mode . prettier-js-mode)
:config
(setq prettier-js-use-modules-bin t))
;; Emacs desktop environment
; Emacs Wayland Manager (EWM)
(when (and (executable-find "ewm-launch") (string= (getenv "XDG_SESSION_DESKTOP") "ewm"))
; Dynamic Tab (Workspace) Setup
(use-package tab-line
:ensure nil)
(tab-bar-mode)
(setq tab-bar-show 1) ; Hide the bar if only 1 tab exists
(setq tab-bar-new-tab-choice "*scratch*")
(defun dynamic-tab--ensure-scratch-tab (&rest _)
"Ensure dynamic scratch tab available if there are active buffers."
(let* ((tabs (funcall tab-bar-tabs-function))
(tab-names (mapcar (lambda (tab) (alist-get 'name tab)) tabs))
(spare-exists (car (member "dynamic *scratch*" tab-names)))
(is-child-buffer (frame-parent))
(is-mini-buffer (minibufferp))
(is-dynamic-tab-focused (string= (alist-get 'name (tab-bar--current-tab)) "dynamic *scratch*"))
(is-scratch-buffer-focused (string= (buffer-name) "*scratch*"))
(is-new-tab-needed
(cond
(is-child-buffer nil)
(is-mini-buffer nil)
((not spare-exists) t)
((and is-dynamic-tab-focused (not is-scratch-buffer-focused)) t)
((and (not is-dynamic-tab-focused) is-scratch-buffer-focused) (tab-close))
(t nil))))
(when is-new-tab-needed
(tab-bar-rename-tab "")
(let ((current-tab (tab-bar--current-tab-index)))
(tab-bar-new-tab 999)
(tab-bar-rename-tab "dynamic *scratch*")
;; Return to original tab if we were just initializing
(tab-bar-select-tab (1+ current-tab))))))
(defvar dynamic-tab--is-already-redirecting-p nil
"Whether we are already redirecting to scratch tab or not.")
(defun dynamic-tab--redirect-to-scratch-tab ()
"Redirect to dedicated scratch tab if scratch buffer accessed on other tab."
(when (and (not dynamic-tab--is-already-redirecting-p)
(string= (buffer-name) "*scratch*")
(string= (buffer-name) "dynamic *scratch*"))
(setq dynamic-tab--is-already-redirecting-p t)
(unwind-protect
(switch-to-buffer nil)
(tab-bar-select-tab-by-name "dynamic *scratch*")
(setq dynamic-tab--is-already-redirecting-p nil))))
(defvar dynamic-tab--soft-kill-p nil
"When t, do not kill buffers only send them to background unless one tab open.")
(defun dynamic-tab--handle-buffer-check-and-close-tab ()
"Check if we need to close the tab and action accordingly."
(let* ((is-last-tab (<= (length (tab-bar-tabs)) 2))
(is-last-buffer (<= (length (tab-line-tabs-window-buffers)) 1)))
(when (and (not is-last-tab) is-last-buffer)
(tab-close))))
(defun dynamic-tab--handle-tab-cleanup-kill ()
"Check if we can kill buffer and free tab if no more reserved buffers in tab."
(let* ((is-last-tab (<= (length (tab-bar-tabs)) 2))
(is-last-buffer (<= (length (tab-line-tabs-window-buffers)) 2))
(is-marked-to-kill (cond
;((and is-last-tab is-last-buffer) t)
;((and is-last-tab (not is-last-buffer)) t)
;((and is-last-tab (not is-last-buffer) (bury-buffer)))
((and (not is-last-tab) (not is-last-buffer) dynamic-tab--soft-kill-p) (bury-buffer))
(t t))))
(dynamic-tab--handle-buffer-check-and-close-tab)
is-marked-to-kill))
(defun dynamic-tab--block-buffer-kill ()
"Block buffer from being killed."
(message "You cannot kill the dynamic buffer")
nil)
(defun dynamic-tab--mark-buffer-unkillable ()
"Mark buffer as unkillable."
(add-hook 'kill-buffer-query-functions #'dynamic-tab--block-buffer-kill nil t))
(defun dynamic-tab--mark-buffer-reserved ()
"Mark buffer as reserved."
(add-hook 'kill-buffer-query-functions #'dynamic-tab--handle-tab-cleanup-kill nil t))
(defun dynamic-tab--unmark-buffer-reserved ()
"Unmark buffer as reserved."
(remove-hook 'kill-buffer-query-functions #'dynamic-tab--handle-tab-cleanup-kill t))
(defun dynamic-tab--refresh-listeners ()
"Refresh hooks to listen on reserved buffers."
(dolist (buf (tab-line-tabs-window-buffers))
(with-current-buffer buf
(let* ((is-child-buffer (frame-parent))
(is-mini-buffer (minibufferp))
(is-dynamic-buffer (string= (buffer-name) "dynamic *scratch*"))
(is-scratch-buffer (string= (buffer-name) "*scratch*")))
(cond
(is-child-buffer nil)
(is-mini-buffer nil)
(is-dynamic-buffer (dynamic-tab--mark-buffer-unkillable))
(is-scratch-buffer (dynamic-tab--mark-buffer-unkillable))
(t (dynamic-tab--mark-buffer-reserved)))))))
(defun dynamic-tab--remove-listeners ()
"Remove all hooks on reserved buffers."
(dolist (buf (buffer-list))
(with-current-buffer buf
(dynamic-tab--unmark-buffer-reserved))))
(defvar dynamic-tab--last-buffer nil)
(defun dynamic-tab--handle-buffer-switch (&rest _)
"Checks to run when buffer is switched."
(unless (eq (current-buffer) dynamic-tab--last-buffer)
(setq dynamic-tab--last-buffer (current-buffer))
;; Put your "on switch" logic here
(dynamic-tab--ensure-scratch-tab)
(dynamic-tab--redirect-to-scratch-tab)))
(defun dynamic-tab--handle-buffer-create (&rest _)
"Checks to run when buffer is created."
(dynamic-tab--ensure-scratch-tab)
(dynamic-tab--refresh-listeners))
(defun dynamic-tab-init ()
"Initialise dynamic tabs."
(add-hook 'window-buffer-change-functions #'dynamic-tab--handle-buffer-switch)
(add-hook 'after-change-major-mode-hook #'dynamic-tab--handle-buffer-create))
(defun dynamic-tab-move-tab-left ()
"Move tab left, no wrapping or moving the dedicated dynamic tab."
(let* ((is-first-tab (= (tab-bar--current-tab-index) 0))
(is-dynamic-tab-focused (string= (alist-get 'name (tab-bar--current-tab)) "dynamic *scratch*")))
(when (and (not is-first-tab)
(not is-dynamic-tab-focused))
(tab-bar-move-tab-backward))))
(defun dynamic-tab-move-tab-right ()
"Move tab right, no wrapping or moving past the dedicated dynamic tab."
(let* ((is-last-tab (= (tab-bar--current-tab-index) (- (length (tab-bar-tabs)) 2)))
(is-dynamic-tab-focused (string= (alist-get 'name (tab-bar--current-tab)) "dynamic *scratch*")))
(when (and (not is-last-tab)
(not is-dynamic-tab-focused))
(tab-bar-move-tab 1))))
; Generic tab helper functions
(defun delete-window-override (&rest _)
"Override for delete window, close tab if only one window instead."
(if (<= (length (window-list)) 1)
(progn
(tab-close)
nil)
t))
;; Helper functions for EWM
; Switch monitor
(defun ewm-switch-to-monitor (target)
"Switch focus to target monitor."
(interactive)
(select-frame-set-input-focus (car (last (seq-filter (lambda (f)
(string= target
(cdr (assq 'name (frame-monitor-attributes f)))))
(frame-list))))))
; Autostart
(defun ewm-autostart-application (target &optional args)
"Autostart application if not already started."
(unless (zerop (shell-command (concat "pidof " target)))
(start-process-shell-command (concat target "-autostart") nil (concat target " " args))))
; EWM - Emacs Wayland Manager
(use-package ewm
:ensure nil
:after evil
:custom
(ewm-output-config
'(("DP-1"
:width 1920
:height 1080
:scale 1.0
:x 0
:y 0
:refresh 240)
("HDMI-A-1"
:width 1920
:height 1080
:scale 1.0
:x 1920
:y 0
:transform 3
:refresh 75)))
(ewm-input-config
'((touchpad :natural-scroll t :tap t :dwt t)
(mouse :accel-profile "flat")
(keyboard :repeat-delay 150 :repeat-rate 35
:xkb-layouts "gb")))
;; Per-device override (exact name from libinput)
;("ELAN0676:00 04F3:3195 Touchpad" :tap nil :accel-speed -0.2)))
; Main bindings
:bind*
(:map ewm-mode-map
("M-d" . ewm-launch-app)
("s-l" . ewm-lock-session)
("M-<return>" . eshell)
("<print>" . (lambda ()
(interactive)
(start-process-shell-command
"screenshot-process"
nil
"grim -g \"$(slurp)\" - | wl-copy")))
("M-w" . (lambda ()
(interactive)
(setq dynamic-tab--soft-kill-p t)
(kill-buffer)
(setq dynamic-tab--soft-kill-p nil)))
("C-x k" . (lambda ()
(interactive)
(setq dynamic-tab--soft-kill-p nil)
(call-interactively 'kill-buffer)))
; Monitor switching, find current focused monitor name via
;(cdr (assq 'name (frame-monitor-attributes))
("M-o" . (lambda ()
(interactive)
(ewm-switch-to-monitor "XG2431")))
("M-p" . (lambda ()
(interactive)
(ewm-switch-to-monitor "24B2W1G5")))
("M-f" . (lambda ()
(interactive)
(let ((proc (start-process-shell-command
"wlkbptr-process"
nil
"wl-kbptr-ewm")))
; Absolutely ensures new overlay has focus
(while (not (process-live-p proc))
(accept-process-output proc 0.1))
(start-process-shell-command
"wlkbptr-focus-process"
nil
"ydotool mousemove --absolute 0 0 && ydotool click 0xC0"))))
("M-h" . tab-bar-switch-to-prev-tab)
("M-l" . tab-bar-switch-to-next-tab)
("M-H" . (lambda ()
(interactive)
(dynamic-tab-move-tab-left)))
("M-L" . (lambda ()
(interactive)
(dynamic-tab-move-tab-right)))
("M-k" . windmove-up)
("M-j" . windmove-down))
:config
(add-to-list 'ewm-intercept-prefixes '("<Print>" :fullscreen))
(add-to-list 'ewm-intercept-prefixes ?\M-f)
(add-to-list 'ewm-intercept-prefixes ?\M-d)
(add-to-list 'ewm-intercept-prefixes ?\M-w)
(add-to-list 'ewm-intercept-prefixes ?\M-o)
(add-to-list 'ewm-intercept-prefixes ?\M-p)
(add-to-list 'ewm-intercept-prefixes ?\M-h)
(add-to-list 'ewm-intercept-prefixes ?\M-j)
(add-to-list 'ewm-intercept-prefixes ?\M-H)
(add-to-list 'ewm-intercept-prefixes ?\M-J)
(add-to-list 'ewm-intercept-prefixes ?\M-k)
(add-to-list 'ewm-intercept-prefixes ?\M-l)
(ewm--send-intercept-keys)
:init
;; Dynamic Tabs
(dynamic-tab-init)
(advice-add 'delete-window :before-while #'delete-window-override))
(with-eval-after-load 'ewm
;; Autostart
(ewm-autostart-application "ydotoold")
(ewm-autostart-application "qpwgraph")
(ewm-autostart-application "kdeconnect-indicator")
(ewm-autostart-application "udiskie")
(ewm-autostart-application "nextcloud")
(ewm-autostart-application "wlsunset" "-l 51.5 -L 0.1"))
; Desktop notifications in Emacs
(use-package posframe
:ensure t)
(use-package ednc
:ensure t
:after posframe
:init
(defun simple-notification-popup (old-notification new-notification)
(ignore-errors
(when new-notification
(posframe-show
" *simple-notification-popup-alert*"
:string (concat
(ednc-notification-summary new-notification)
"\n"
(ednc-notification-body new-notification))
:position (point)
:poshandler #'posframe-poshandler-frame-top-right-corner
:background-color "gold"
:foreground-color "black"
:timeout 5))))
(add-hook 'ednc-notification-presentation-functions
#'simple-notification-popup)
(ednc-mode))
;(use-package ednc-popup
; :ensure (:host git :repo "https://codeberg.org/akib/emacs-ednc-popup.git")
; :after ednc
; :config
; (add-hook 'ednc-notification-presentation-functions
; #'ednc-popup-presentation-function))
(use-package kdeconnect
:ensure t
:config
(setq kdeconnect-devices '(("My Phone" . "378230a2e3c24b9a9904e7ffb0dfeb07")))
(setq kdeconnect-active-device '("My Phone" . "378230a2e3c24b9a9904e7ffb0dfeb07"))))
;(use-package elcord
; :ensure t
; :config
; (setq elcord-display-buffer-details nil)
; (elcord-mode)))
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(auth-source-save-behavior nil)
'(elfeed-feeds
'(#("Not found!" 0 10 (face error))
("https://www.reddit.com/r/Clamworks.rss" reddit clamworks)
("https://www.reddit.com/r/Losercity.rss" reddit losercity)
("https://www.reddit.com/r/Shark_Park.rss" reddit sharkpark)
("https://www.reddit.com/r/emacs.rss" reddit emacs)))
'(package-selected-packages
'(bash-completion consult corfu eat evil gcmh magit mu4e orderless
org-journal paredit rainbow-delimiters vertico))
'(smtpmail-smtp-server "smtp.cspark.dev")
'(smtpmail-smtp-service 25))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
)