;; -*- 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 (("" . 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 "") '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-" . eshell) ("" . (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 '("" :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. )