fix: restore lost work (bookmark mu4e agenda context-tag)
This commit is contained in:
@@ -92,6 +92,19 @@ available. You can either set ~doom-theme~ or manually load a theme with the
|
|||||||
(setq doom-theme 'doom-gruvbox)
|
(setq doom-theme 'doom-gruvbox)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
|
** Projectile (ExoKortex)
|
||||||
|
|
||||||
|
Only directories with a =.projectile= file are treated as project roots.
|
||||||
|
No more auto-discovery of random =.git= repos.
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(after! projectile
|
||||||
|
;; Only .projectile files define a project root
|
||||||
|
(setq projectile-project-root-files '(".projectile"))
|
||||||
|
;; Stop auto-scanning everything — only explicit .projectile markers count
|
||||||
|
(setq projectile-auto-discover nil))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
** Bookmark
|
** Bookmark
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
@@ -107,7 +120,7 @@ To prevent saving a old buffer content after a git branch switch!
|
|||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Minimize window
|
** Minimize window
|
||||||
My current workflow consist in having the 3-5 files I work on open in vertical split and enlarging one at the time. but sometime I want to see 2 side by side. So I first open all windows with equal size, then collapse all but the 2 I want with this keymap. It's not perfect and I'm thinking about of a buffer based workflow that would give me more screen space.
|
My current workflow consist in having the 3-5 files I work on open in vertical split and enlarging one at the time. but sometime I want to see 2 side by side. So I first open all windows with equal size, then collapse all but the 2 I want with this keymap. It's not perfect and I'm thinking about a buffer based workflow that would give me more screen space.
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(map! :leader
|
(map! :leader
|
||||||
@@ -117,6 +130,14 @@ My current workflow consist in having the 3-5 files I work on open in vertical s
|
|||||||
:desc "Hermes Agent" "h" #'gortium/hermes-vterm))
|
:desc "Hermes Agent" "h" #'gortium/hermes-vterm))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
|
** Hermes remote shortcut
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(map! :leader
|
||||||
|
(:prefix ("z" . "Personal")
|
||||||
|
:desc "Hermes Agent" "h" #'gortium/hermes-vterm))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
** Ultra-scroll
|
** Ultra-scroll
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(use-package! ultra-scroll
|
(use-package! ultra-scroll
|
||||||
@@ -164,7 +185,7 @@ My current workflow consist in having the 3-5 files I work on open in vertical s
|
|||||||
** Fix
|
** Fix
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
;; Fixes..
|
;; Fixes.. idk why or what.. dont ask me
|
||||||
(require 'cl-lib)
|
(require 'cl-lib)
|
||||||
(unless (fboundp 'find-if)
|
(unless (fboundp 'find-if)
|
||||||
(defalias 'find-if #'cl-find-if))
|
(defalias 'find-if #'cl-find-if))
|
||||||
@@ -187,8 +208,18 @@ change ~org-directory~. It must be set before org loads!
|
|||||||
+org-capture-emails-file "~/ExoKortex/inbox.org"
|
+org-capture-emails-file "~/ExoKortex/inbox.org"
|
||||||
;; +org-capture-central-project-todo-file '"refile.org"
|
;; +org-capture-central-project-todo-file '"refile.org"
|
||||||
;; +org-capture-project-todo-file '"refile.org"
|
;; +org-capture-project-todo-file '"refile.org"
|
||||||
org-agenda-files (directory-files-recursively "~/ExoKortex/" "\\.org$")
|
org-agenda-files
|
||||||
)
|
(let ((all (directory-files-recursively "~/ExoKortex/" "\\.org$")))
|
||||||
|
(append '("~/ExoKortex/inbox.org"
|
||||||
|
"~/ExoKortex/projects.org"
|
||||||
|
"~/ExoKortex/areas.org"
|
||||||
|
"~/ExoKortex/journal.org"
|
||||||
|
"~/ExoKortex/telos_thierry_pouplier.org")
|
||||||
|
(seq-filter
|
||||||
|
(lambda (f) (string-match-p "/1-Plan/" f))
|
||||||
|
all)))
|
||||||
|
org-agenda-files (seq-uniq org-agenda-files)
|
||||||
|
)
|
||||||
|
|
||||||
(after! org
|
(after! org
|
||||||
(setq org-cycle-show-planning-line t)
|
(setq org-cycle-show-planning-line t)
|
||||||
@@ -220,10 +251,19 @@ change ~org-directory~. It must be set before org loads!
|
|||||||
'(org-level-6 :foreground "#fe8019" :weight normal :height 1.05) ; orange
|
'(org-level-6 :foreground "#fe8019" :weight normal :height 1.05) ; orange
|
||||||
'(org-level-7 :foreground "#cc241d" :weight normal :height 1.02) ; red
|
'(org-level-7 :foreground "#cc241d" :weight normal :height 1.02) ; red
|
||||||
'(org-level-8 :foreground "#a89984" :weight normal :height 1.00)) ; grey-brown
|
'(org-level-8 :foreground "#a89984" :weight normal :height 1.00)) ; grey-brown
|
||||||
|
;; '(org-level-1 :weight semi-bold :height 1.45)
|
||||||
|
;; '(org-level-2 :weight semi-bold :height 1.35)
|
||||||
|
;; '(org-level-3 :weight semi-bold :height 1.25)
|
||||||
|
;; '(org-level-4 :weight semi-bold :height 1.15)
|
||||||
|
;; '(org-level-5 :weight normal :height 1.10)
|
||||||
|
;; '(org-level-6 :weight normal :height 1.05)
|
||||||
|
;; '(org-level-7 :weight normal :height 1.02)
|
||||||
|
;; '(org-level-8 :weight normal :height 1.00))
|
||||||
|
|
||||||
;; Clamp all levels beyond 8 to use org-level-8 face
|
;; Clamp all levels beyond 8 to use org-level-8 face
|
||||||
(defvar gortium/org-level-8-face
|
(defvar gortium/org-level-8-face
|
||||||
'((t :foreground "#a89984" :weight normal :height 1.00)))
|
'((t :foreground "#a89984" :weight normal :height 1.00)))
|
||||||
|
;; '((t :weight normal :height 1.00)))
|
||||||
|
|
||||||
;; Définir org-level-9 à org-level-20 avec la même face que org-level-8
|
;; Définir org-level-9 à org-level-20 avec la même face que org-level-8
|
||||||
(cl-loop for lvl from 9 to 20
|
(cl-loop for lvl from 9 to 20
|
||||||
@@ -310,6 +350,7 @@ change ~org-directory~. It must be set before org loads!
|
|||||||
org-agenda-skip-deadline-if-done nil
|
org-agenda-skip-deadline-if-done nil
|
||||||
org-agenda-skip-scheduled-if-done nil
|
org-agenda-skip-scheduled-if-done nil
|
||||||
org-agenda-skip-timestamp-if-done nil
|
org-agenda-skip-timestamp-if-done nil
|
||||||
|
org-agenda-sticky t
|
||||||
org-journal-enable-agenda-integration t
|
org-journal-enable-agenda-integration t
|
||||||
org-log-done 'time
|
org-log-done 'time
|
||||||
org-agenda-span 'day
|
org-agenda-span 'day
|
||||||
@@ -487,7 +528,7 @@ change ~org-directory~. It must be set before org loads!
|
|||||||
)
|
)
|
||||||
((org-agenda-tag-filter-preset '("+work")))
|
((org-agenda-tag-filter-preset '("+work")))
|
||||||
)
|
)
|
||||||
("pp" "Planning"
|
("pp" "Planning 1 month"
|
||||||
((agenda ""
|
((agenda ""
|
||||||
((org-agenda-span 'month)
|
((org-agenda-span 'month)
|
||||||
(org-agenda-start-day nil)
|
(org-agenda-start-day nil)
|
||||||
@@ -511,6 +552,54 @@ change ~org-directory~. It must be set before org loads!
|
|||||||
)
|
)
|
||||||
((org-agenda-tag-filter-preset '("+perso")))
|
((org-agenda-tag-filter-preset '("+perso")))
|
||||||
)
|
)
|
||||||
|
("pP" "Planning 2 months"
|
||||||
|
((agenda ""
|
||||||
|
((org-agenda-span 60)
|
||||||
|
(org-agenda-start-day nil)
|
||||||
|
(org-agenda-overriding-header "📅 Sheduled Tasks")
|
||||||
|
(org-agenda-time-grid nil)
|
||||||
|
(org-agenda-prefix-format " %12t") ;; reserve time space
|
||||||
|
(org-agenda-todo-keyword-format " ") ;; fixed-width TODO
|
||||||
|
(org-agenda-tags-column -100) ;; right-align tags
|
||||||
|
(org-agenda-remove-tags nil))
|
||||||
|
)
|
||||||
|
|
||||||
|
(todo "TODO|NEXT|DELG|WAIT|HOLD|STRT|IDEA"
|
||||||
|
((org-agenda-overriding-header "📋 Unscheduled TODOs")
|
||||||
|
(org-agenda-skip-function '(org-agenda-skip-entry-if 'scheduled))
|
||||||
|
(org-agenda-prefix-format " %12t") ;; reserve time space
|
||||||
|
(org-agenda-todo-keyword-format " ") ;; fixed-width TODO
|
||||||
|
(org-agenda-tags-column -100) ;; right-align tags
|
||||||
|
(org-agenda-remove-tags t)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
((org-agenda-tag-filter-preset '("+perso")))
|
||||||
|
)
|
||||||
|
("pI" "ImmoFacile"
|
||||||
|
((agenda ""
|
||||||
|
((org-agenda-span 'month)
|
||||||
|
(org-agenda-start-day nil)
|
||||||
|
(org-agenda-overriding-header "📅 Sheduled Tasks")
|
||||||
|
(org-agenda-time-grid nil)
|
||||||
|
(org-agenda-prefix-format " %12t") ;; reserve time space
|
||||||
|
(org-agenda-todo-keyword-format " ") ;; fixed-width TODO
|
||||||
|
(org-agenda-tags-column -100) ;; right-align tags
|
||||||
|
(org-agenda-remove-tags t))
|
||||||
|
)
|
||||||
|
|
||||||
|
(todo "TODO|NEXT|DELG|WAIT|HOLD|STRT|IDEA"
|
||||||
|
((org-agenda-overriding-header "📋 Unscheduled TODOs")
|
||||||
|
(org-agenda-skip-function '(org-agenda-skip-entry-if 'scheduled))
|
||||||
|
(org-agenda-prefix-format " %12t") ;; reserve time space
|
||||||
|
(org-agenda-todo-keyword-format " ") ;; fixed-width TODO
|
||||||
|
(org-agenda-tags-column -100) ;; right-align tags
|
||||||
|
(org-agenda-remove-tags t)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
((org-agenda-tag-filter-preset '("+ImmoFacile")))
|
||||||
|
)
|
||||||
("wP" "Installation Bombardier"
|
("wP" "Installation Bombardier"
|
||||||
((agenda ""
|
((agenda ""
|
||||||
((org-agenda-span 90)
|
((org-agenda-span 90)
|
||||||
@@ -1266,6 +1355,14 @@ Deactivated for ultra-scroll
|
|||||||
(after! text-mode
|
(after! text-mode
|
||||||
(add-hook 'text-mode-hook #'visual-line-mode))
|
(add-hook 'text-mode-hook #'visual-line-mode))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
** Rainbow
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package! rainbow-delimiters
|
||||||
|
:hook ((emacs-lisp-mode lisp-mode scheme-mode clojure-mode) . rainbow-delimiters-mode))
|
||||||
|
|
||||||
|
(after! org-src
|
||||||
|
(add-hook 'org-src-mode-hook #'rainbow-delimiters-mode))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
** Org-phscroll
|
** Org-phscroll
|
||||||
Load org-phscroll after org. This allow horizontal scroll in tables. (No line wrap in table)
|
Load org-phscroll after org. This allow horizontal scroll in tables. (No line wrap in table)
|
||||||
@@ -1344,7 +1441,7 @@ numbers are disabled. For relative line numbers, set this to ~relative~.
|
|||||||
(setq display-line-numbers-type 'relative)
|
(setq display-line-numbers-type 'relative)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Evile-Escape (JK)
|
** Evil-Escape (JK)
|
||||||
JK to escape was not working. Added it back.
|
JK to escape was not working. Added it back.
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
@@ -1453,7 +1550,7 @@ I'm a formatting nazi now xD
|
|||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Age
|
** Age
|
||||||
Allow me to edit encryted age file directly in emacs buffer.
|
Allow me to edit encrypted age file directly in emacs buffer.
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(use-package! age
|
(use-package! age
|
||||||
@@ -2086,7 +2183,8 @@ Useful for the user, but also when you have someone over your shoulder trying to
|
|||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(after! mu4e
|
(after! mu4e
|
||||||
(setq mu4e-maildir-shortcuts mu4e-maildir-list mu4e-maildir-initial-input mu4e-maildir-info-delimiter)
|
(setq mu4e-maildir-shortcuts mu4e-maildir-list mu4e-maildir-initial-input mu4e-maildir-info-delimiter)
|
||||||
|
(setq mu4e-update-interval 300)
|
||||||
|
(setq mu4e-headers-auto-update t)
|
||||||
(setq mu4e-contexts
|
(setq mu4e-contexts
|
||||||
(list
|
(list
|
||||||
;; --- CONTEXT: TDNDE ---
|
;; --- CONTEXT: TDNDE ---
|
||||||
@@ -2196,6 +2294,195 @@ Cordialement,
|
|||||||
(evil-define-key 'emacs mu4e-view-mode-map "l" #'mu4e-view-mark-for-label)))
|
(evil-define-key 'emacs mu4e-view-mode-map "l" #'mu4e-view-mark-for-label)))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
|
** Project mu4e maildir shortcuts
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(defconst gortium/project-mu4e-keys '(?I ?S ?D ?T ?A)
|
||||||
|
"Character keys used for project mu4e maildir shortcuts (uppercase avoids conflicts).")
|
||||||
|
|
||||||
|
(defvar gortium/project-mu4e-bookmarks nil
|
||||||
|
"Cached project mu4e maildir shortcuts for re-injection when mu4e loads.")
|
||||||
|
|
||||||
|
(defun gortium/inject-project-mu4e-bookmarks ()
|
||||||
|
"Inject project-specific mu4e bookmark shortcuts [PI]/[PS]/[PD]/[PT]/[PA].
|
||||||
|
Caches them for re-injection when mu4e loads."
|
||||||
|
(let* ((proj-root (and (projectile-project-p) (projectile-project-root)))
|
||||||
|
(proj-tag (when proj-root
|
||||||
|
(let ((default-directory proj-root))
|
||||||
|
(gortium/read-dir-local 'gortium/project-tag))))
|
||||||
|
(project-bm '()))
|
||||||
|
(when proj-tag
|
||||||
|
(dolist (folder '(("Inbox" . ?I)
|
||||||
|
("Sent" . ?S)
|
||||||
|
("Drafts" . ?D)
|
||||||
|
("Trash" . ?T)
|
||||||
|
("Archive" . ?A)))
|
||||||
|
(push (list :name (format "[%s] %s" proj-tag (car folder))
|
||||||
|
:query (format "maildir:/%s.%s" (car folder) proj-tag)
|
||||||
|
:key (cdr folder))
|
||||||
|
project-bm)))
|
||||||
|
;; Cache for later re-injection
|
||||||
|
(setq gortium/project-mu4e-bookmarks project-bm)
|
||||||
|
;; Inject now if mu4e is already loaded
|
||||||
|
(when (boundp 'mu4e-bookmarks)
|
||||||
|
(setq mu4e-bookmarks
|
||||||
|
(append
|
||||||
|
(seq-filter (lambda (bm)
|
||||||
|
(if (and (listp bm) (keywordp (car-safe bm)))
|
||||||
|
(let ((key (plist-get bm :key)))
|
||||||
|
(not (member key gortium/project-mu4e-keys)))
|
||||||
|
t))
|
||||||
|
mu4e-bookmarks)
|
||||||
|
gortium/project-mu4e-bookmarks))
|
||||||
|
;; Clear caches so new bookmarks appear
|
||||||
|
(setq mu4e--bookmark-items-cached nil)
|
||||||
|
;; Re-render main view if visible
|
||||||
|
(when (get-buffer "*mu4e-main*")
|
||||||
|
(with-current-buffer "*mu4e-main*"
|
||||||
|
(mu4e--main-redraw))))))
|
||||||
|
|
||||||
|
(add-hook 'projectile-after-switch-project-hook
|
||||||
|
#'gortium/inject-project-mu4e-bookmarks)
|
||||||
|
(add-hook 'find-file-hook #'gortium/inject-project-mu4e-bookmarks)
|
||||||
|
(add-hook 'mu4e-main-mode-hook #'gortium/inject-project-mu4e-bookmarks)
|
||||||
|
(after! mu4e
|
||||||
|
(gortium/inject-project-mu4e-bookmarks))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
* Elfeed (RSS)
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(after! elfeed
|
||||||
|
;; Define where your feed sources are kept (Org-mode file integration)
|
||||||
|
(setq rmh-elfeed-org-files '("~/ExoKortex/2-Areas/Home-Infra/2-Assets/rss-feed.org"))
|
||||||
|
|
||||||
|
;; Set the default search filter (only show unread entries from the last x weeks)
|
||||||
|
(setq-default elfeed-search-filter "@4-weeks-ago +unread")
|
||||||
|
|
||||||
|
;; Automatically fetch feed updates over the network when you first open Elfeed
|
||||||
|
(add-hook! 'elfeed-search-mode-hook #'elfeed-update)
|
||||||
|
|
||||||
|
;; Optimize connection parameters to prevent lagging and queue timeouts
|
||||||
|
(setq elfeed-connection-timeout 10) ; Timeout limit for fetching a single feed (seconds)
|
||||||
|
(setq url-queue-timeout 15) ; Maximum wait time for the network queue
|
||||||
|
|
||||||
|
;; Set the function used to display entries (pop-to-buffer prevents taking over the whole screen)
|
||||||
|
(setq elfeed-show-entry-switch #'pop-to-buffer)
|
||||||
|
|
||||||
|
;; A pure MPV launcher (Bypasses buggy EMMS metadata scanners)
|
||||||
|
(defun gortium/launch-mpv-overlay (url)
|
||||||
|
"Launch MPV as a floating overlay directly via system process."
|
||||||
|
(start-process "elfeed-mpv" nil "mpv"
|
||||||
|
"--quiet" "--really-quiet"
|
||||||
|
"--geometry=30%x30%-30-50" "--ontop" "--no-border"
|
||||||
|
"--title=Elfeed MPV"
|
||||||
|
url))
|
||||||
|
|
||||||
|
;; Smart Elfeed Dispatcher
|
||||||
|
(defun gortium/elfeed-open-dispatcher ()
|
||||||
|
"Play YouTube and Podcasts directly in MPV; open web links in EWW."
|
||||||
|
(interactive)
|
||||||
|
(let* ((entry (if (eq major-mode 'elfeed-show-mode)
|
||||||
|
elfeed-show-entry
|
||||||
|
(elfeed-search-selected :single)))
|
||||||
|
(link (elfeed-entry-link entry))
|
||||||
|
;; Extract enclosures (where podcasts store their mp3s)
|
||||||
|
(enclosures (elfeed-entry-enclosures entry))
|
||||||
|
(enclosure-url (when enclosures (car (car enclosures)))))
|
||||||
|
(cond
|
||||||
|
;; Case 1: YouTube -> Direct MPV Overlay
|
||||||
|
((and link (string-match-p "youtu\\(?:\\.be\\|be\\.com\\)" link))
|
||||||
|
(message "Streaming YouTube video in MPV: %s" link)
|
||||||
|
(gortium/launch-mpv-overlay link))
|
||||||
|
|
||||||
|
;; Case 2: Podcast/Media Enclosure found -> Direct MPV Overlay
|
||||||
|
(enclosure-url
|
||||||
|
(message "Streaming podcast enclosure in MPV: %s" enclosure-url)
|
||||||
|
(gortium/launch-mpv-overlay enclosure-url))
|
||||||
|
|
||||||
|
;; Case 3: Standard Web Article (Must start with http) -> EWW
|
||||||
|
((and link (string-match-p "^http" link))
|
||||||
|
(message "Opening article in EWW (readable): %s" link)
|
||||||
|
(cond
|
||||||
|
((eq major-mode 'elfeed-show-mode)
|
||||||
|
(eww link))
|
||||||
|
(t
|
||||||
|
(let ((target-window (if (one-window-p)
|
||||||
|
(split-window-right)
|
||||||
|
(next-window))))
|
||||||
|
(select-window target-window)
|
||||||
|
(eww link))))
|
||||||
|
(add-hook 'eww-after-render-hook #'eww-readable nil t))
|
||||||
|
|
||||||
|
;; Case 4: Safety Fallback
|
||||||
|
(t
|
||||||
|
(message "No playable media or valid web link found for this entry.")))))
|
||||||
|
|
||||||
|
;; Bind the dispatcher
|
||||||
|
(map! :map elfeed-search-mode-map
|
||||||
|
"M-RET" #'gortium/elfeed-open-dispatcher)
|
||||||
|
|
||||||
|
(map! :map elfeed-show-mode-map
|
||||||
|
"M-RET" #'gortium/elfeed-open-dispatcher))
|
||||||
|
|
||||||
|
;; Initialize and configure elfeed-goodies for an enhanced UI layout
|
||||||
|
(after! elfeed-goodies
|
||||||
|
;; Enable the standard elfeed-goodies setup (adds search headers and status lines)
|
||||||
|
(elfeed-goodies/setup)
|
||||||
|
|
||||||
|
;; Choose where to split the window when viewing an entry ('bottom, 'left, 'right, 'top)
|
||||||
|
(setq elfeed-goodies/entry-pane-position 'right)
|
||||||
|
|
||||||
|
;; Define how much screen space the entry display pane takes (0.5 = 50% of the frame)
|
||||||
|
(setq elfeed-goodies/entry-pane-size 0.5)
|
||||||
|
|
||||||
|
;; Display feed tags in their own clean, separate column in the search buffer
|
||||||
|
(setq elfeed-goodies/show-tags-as-column t)
|
||||||
|
|
||||||
|
;; Prevent feed source URLs from cluttering up the title line in search results
|
||||||
|
(setq elfeed-goodies/feed-source-column-width 16))
|
||||||
|
|
||||||
|
;; Configure elfeed-tube (active when +youtube flag is enabled)
|
||||||
|
(after! elfeed-tube
|
||||||
|
;; Initialize elfeed-tube to fetch YouTube metadata and transcripts
|
||||||
|
(elfeed-tube-setup)
|
||||||
|
|
||||||
|
;; Define a quick binding to manually fetch/refresh a transcript if auto-fetch is idle
|
||||||
|
(map! :map elfeed-show-mode-map
|
||||||
|
:ne "F" #'elfeed-tube-fetch))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
* EMMS
|
||||||
|
|
||||||
|
#+begin_Src emacs-lisp
|
||||||
|
;; Configure EMMS to use MPV for streaming
|
||||||
|
(after! emms
|
||||||
|
(require 'emms-setup)
|
||||||
|
(emms-all)
|
||||||
|
(require 'emms-source-file)
|
||||||
|
|
||||||
|
;; Swap VLC out for MPV (which seamlessly leverages yt-dlp)
|
||||||
|
(setq emms-player-list '(emms-player-mpv))
|
||||||
|
|
||||||
|
;; Force EMMS to route HTTP/HTTPS URLs matching YouTube patterns to MPV
|
||||||
|
(emms-player-set emms-player-mpv 'regex
|
||||||
|
(rx (or (: "http" (? "s") "://" (* nonl) "youtube.com" (* nonl))
|
||||||
|
(: "http" (? "s") "://" (* nonl) "youtu.be" (* nonl))))))
|
||||||
|
#+end_src
|
||||||
|
* EAF
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package! eaf
|
||||||
|
:load-path "~/.config/emacs/.local/straight/repos/emacs-application-framework"
|
||||||
|
:config
|
||||||
|
(require 'eaf)
|
||||||
|
|
||||||
|
;; Load the Browser module (for YouTube web streams)
|
||||||
|
(add-to-list 'load-path "~/.config/emacs/.local/straight/repos/emacs-application-framework/app/browser")
|
||||||
|
(require 'eaf-browser)
|
||||||
|
|
||||||
|
;; Load the Video Player module (for local video files)
|
||||||
|
(add-to-list 'load-path "~/.config/emacs/.local/straight/repos/emacs-application-framework/app/video-player")
|
||||||
|
(require 'eaf-video-player))
|
||||||
|
#+end_src
|
||||||
* IRC
|
* IRC
|
||||||
** Circe
|
** Circe
|
||||||
|
|
||||||
@@ -2260,7 +2547,7 @@ Really useful to be able to have a buffer with notes, and another with the offic
|
|||||||
|
|
||||||
* Custom functions
|
* Custom functions
|
||||||
|
|
||||||
** Custom function refile to datetree
|
** Refile to datetree
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
;;Refile in datetree function
|
;;Refile in datetree function
|
||||||
@@ -2292,7 +2579,35 @@ If FILE is nil, refile in the current file."
|
|||||||
(widen)))))
|
(widen)))))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Custom function ediff org fold
|
** Create a datetree
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(require 'calendar)
|
||||||
|
|
||||||
|
(defun gortium/org-create-datetree-range ()
|
||||||
|
"Génère une structure datetree Org entre deux dates choisies via le calendrier interactif.
|
||||||
|
Si la date de début est validée vide, utilise la date d'aujourd'hui."
|
||||||
|
(interactive)
|
||||||
|
(let* ((start-time (org-read-date nil t nil "Date de début (Entrée pour aujourd'hui) : "))
|
||||||
|
(end-time (org-read-date nil t nil "Date de fin : "))
|
||||||
|
(start-decoded (decode-time start-time))
|
||||||
|
(end-decoded (decode-time end-time))
|
||||||
|
;; Format attendu par le calendrier Emacs : (mois jour année)
|
||||||
|
(start-date (list (decoded-time-month start-decoded)
|
||||||
|
(decoded-time-day start-decoded)
|
||||||
|
(decoded-time-year start-decoded)))
|
||||||
|
(end-date (list (decoded-time-month end-decoded)
|
||||||
|
(decoded-time-day end-decoded)
|
||||||
|
(decoded-time-year end-decoded)))
|
||||||
|
(start-abs (calendar-absolute-from-gregorian start-date))
|
||||||
|
(end-abs (calendar-absolute-from-gregorian end-date)))
|
||||||
|
(if (< end-abs start-abs)
|
||||||
|
(user-error "Erreur : La date de fin doit être après la date de début ! ")
|
||||||
|
(save-excursion
|
||||||
|
(cl-loop for abs from start-abs to end-abs
|
||||||
|
do (org-datetree-find-date-create (calendar-gregorian-from-absolute abs))))
|
||||||
|
(message "Datetree généré avec succès !"))))
|
||||||
|
#+end_src
|
||||||
|
** Ediff org fold
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
;; Open all org fold in ediff
|
;; Open all org fold in ediff
|
||||||
@@ -2303,7 +2618,7 @@ If FILE is nil, refile in the current file."
|
|||||||
(add-hook 'ediff-prepare-buffer-hook 'org-ediff-prepare-buffer)
|
(add-hook 'ediff-prepare-buffer-hook 'org-ediff-prepare-buffer)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Custom function for scheduling
|
** Scheduling Engine
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
;;; ============================================================
|
;;; ============================================================
|
||||||
@@ -2752,7 +3067,7 @@ and ensure the standard property drawer exists without overwriting existing data
|
|||||||
|
|
||||||
#+RESULTS:
|
#+RESULTS:
|
||||||
: gortium/add-ids-to-subtree
|
: gortium/add-ids-to-subtree
|
||||||
** Custom function for maintenance
|
** Maintenance Engine
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(after! org
|
(after! org
|
||||||
;; ---------------------------------------------------------
|
;; ---------------------------------------------------------
|
||||||
@@ -2851,7 +3166,7 @@ and ensure the standard property drawer exists without overwriting existing data
|
|||||||
(add-hook 'org-after-todo-state-change-hook #'my/org-todo-state-handler)
|
(add-hook 'org-after-todo-state-change-hook #'my/org-todo-state-handler)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Custom function for rescheduling task later
|
** Rescheduling task later
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(defun my/org-smart-advance-date ()
|
(defun my/org-smart-advance-date ()
|
||||||
"Jump to next repeater occurrence or add 1 day.
|
"Jump to next repeater occurrence or add 1 day.
|
||||||
@@ -2890,7 +3205,7 @@ Suppresses all logging, notes, and custom odometer hooks."
|
|||||||
:localleader "L" #'my/org-smart-advance-date))
|
:localleader "L" #'my/org-smart-advance-date))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Custom function Open link in other frame
|
** Open link in other frame
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
;; Open link in another frame
|
;; Open link in another frame
|
||||||
@@ -2906,7 +3221,7 @@ Suppresses all logging, notes, and custom odometer hooks."
|
|||||||
:n "gF" #'gortium/org-open-link-in-other-frame)
|
:n "gF" #'gortium/org-open-link-in-other-frame)
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Custom function to open current buffer to new frame
|
** Open current buffer to new frame
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(defvar gortium/main-frame nil
|
(defvar gortium/main-frame nil
|
||||||
@@ -2977,7 +3292,7 @@ Suppresses all logging, notes, and custom odometer hooks."
|
|||||||
(delete-window old-win)))))))
|
(delete-window old-win)))))))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Custom function for ExoKortex
|
** ExoKortex System
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
;;; ====================================================================
|
;;; ====================================================================
|
||||||
@@ -2987,10 +3302,11 @@ Suppresses all logging, notes, and custom odometer hooks."
|
|||||||
(defvar gortium/exokortex-root "~/ExoKortex"
|
(defvar gortium/exokortex-root "~/ExoKortex"
|
||||||
"The absolute root directory of your ExoKortex.")
|
"The absolute root directory of your ExoKortex.")
|
||||||
|
|
||||||
(defun gortium/ensure-node-structure (node-dir &optional default-todo)
|
(defun gortium/ensure-node-structure (node-dir &optional default-todo project-tag context-tag)
|
||||||
"Ensure NODE-DIR conforms to the ExoKortex node blueprint.
|
"Ensure NODE-DIR conforms to the ExoKortex node blueprint.
|
||||||
Creates folders, writes text-layer filters, and handles Git injection.
|
Creates folders, writes text-layer filters, and handles Git injection.
|
||||||
Returns the absolute path to the core Org file."
|
When PROJECT-TAG is provided, also creates .projectile and .dir-locals.el
|
||||||
|
with a project-tagged custom agenda entry and optional CONTEXT-TAG (mu4e/agenda)."
|
||||||
(let* ((node-dir (expand-file-name node-dir))
|
(let* ((node-dir (expand-file-name node-dir))
|
||||||
(node-name (file-name-nondirectory (directory-file-name node-dir)))
|
(node-name (file-name-nondirectory (directory-file-name node-dir)))
|
||||||
;; Blueprint Paths
|
;; Blueprint Paths
|
||||||
@@ -3014,39 +3330,84 @@ Returns the absolute path to the core Org file."
|
|||||||
;; 3. Build index Org block if absent
|
;; 3. Build index Org block if absent
|
||||||
(unless (file-exists-p org-file)
|
(unless (file-exists-p org-file)
|
||||||
(with-temp-file org-file
|
(with-temp-file org-file
|
||||||
(insert (format "#+TITLE: %s\n#+DATE: %s\n#+CATEGORY: %s\n\n* Setup\n- [ ] %s\n"
|
(insert (format "#+TITLE: %s\n#+DATE: %s\n#+CATEGORY: %s\n#+FILETAGS: :%s:\n\n* Setup\nTODO %s\n\n* Bookmarks\n** Example link\n[[file:./%s.org][Example link for %s]]\n\n* Contacts\n** Contact Name\n:PROPERTIES:\n:EMAIL: contact@example.com\n:ROLE: Client / Partner\n:END:\n\n[[mu4e:from:contact@example.com][Correspondence with Contact Name]]\n\n* Tasks\n** TODO First task for %s\n"
|
||||||
node-name
|
node-name
|
||||||
(format-time-string "[%Y-%m-%d %a]")
|
(format-time-string "[%Y-%m-%d %a]")
|
||||||
node-name
|
node-name
|
||||||
(or default-todo "Initialize project context")))))
|
(or project-tag "project")
|
||||||
|
(or default-todo "Initialize project context")
|
||||||
|
node-name
|
||||||
|
node-name
|
||||||
|
node-name))))
|
||||||
|
|
||||||
;; 4. Unified Git Integration Loop
|
;; 4. ExoKortex project markers (if project-tag provided)
|
||||||
|
(when project-tag
|
||||||
|
(let ((projectile-file (expand-file-name ".projectile" node-dir)))
|
||||||
|
(unless (file-exists-p projectile-file)
|
||||||
|
(with-temp-file projectile-file
|
||||||
|
(insert ""))))
|
||||||
|
|
||||||
|
(let ((dir-locals-file (expand-file-name ".dir-locals.el" node-dir)))
|
||||||
|
(unless (file-exists-p dir-locals-file)
|
||||||
|
(with-temp-file dir-locals-file
|
||||||
|
(prin1
|
||||||
|
`((nil . ((gortium/project-tag . ,project-tag)
|
||||||
|
(gortium/project-agenda-entry .
|
||||||
|
(("P" ,node-name
|
||||||
|
((agenda "" ((org-agenda-span 14)
|
||||||
|
(org-agenda-overriding-header
|
||||||
|
,(format "📅 %s" node-name))))
|
||||||
|
(todo "TODO|NEXT|DELG|WAIT|HOLD|STRT|IDEA"
|
||||||
|
((org-agenda-overriding-header "📋 Unscheduled")
|
||||||
|
(org-agenda-skip-function
|
||||||
|
'(org-agenda-skip-entry-if 'scheduled)))))
|
||||||
|
|
||||||
|
((org-agenda-tag-filter-preset (list ,(concat "+" project-tag))))))))
|
||||||
|
,@(if context-tag
|
||||||
|
`((gortium/context-tag . ,context-tag))
|
||||||
|
nil))))
|
||||||
|
(current-buffer))))))
|
||||||
|
|
||||||
|
;; 5. Unified Git Integration Loop
|
||||||
(let ((default-directory node-dir))
|
(let ((default-directory node-dir))
|
||||||
(if (not git-existed-p)
|
(if (not git-existed-p)
|
||||||
(progn
|
(progn
|
||||||
(shell-command "git init")
|
(shell-command "git init")
|
||||||
(shell-command "git add .gitignore 1-Plan/ 3-Notes/")
|
(let ((add-files ".gitignore 1-Plan/ 3-Notes/"))
|
||||||
|
(when project-tag
|
||||||
|
(setq add-files (concat add-files " .projectile .dir-locals.el")))
|
||||||
|
(shell-command (concat "git add -f " add-files)))
|
||||||
(shell-command "git commit -m 'System: Initialize node repository'"))
|
(shell-command "git commit -m 'System: Initialize node repository'"))
|
||||||
(shell-command "git add .gitignore 1-Plan/ 3-Notes/")))
|
|
||||||
|
;; existing repo: add new marker files if they exist
|
||||||
|
(let ((add-files ".gitignore 1-Plan/ 3-Notes/"))
|
||||||
|
(when project-tag
|
||||||
|
(setq add-files (concat add-files " .projectile .dir-locals.el")))
|
||||||
|
(shell-command (concat "git add -f " add-files)))))
|
||||||
|
|
||||||
;; Pass the target file location back up to the caller
|
;; Pass the target file location back up to the caller
|
||||||
org-file))
|
org-file)
|
||||||
|
|
||||||
|
|
||||||
;;; ====================================================================
|
;;; ====================================================================
|
||||||
;;; Interactive Entry Points
|
;;; Interactive Entry Points
|
||||||
;;; ====================================================================
|
;;; ====================================================================
|
||||||
|
|
||||||
(defun gortium/create-node (category node-name)
|
(defun gortium/create-node (category node-name tag context-tag)
|
||||||
"Create a brand new standardized PARA Node inside your ExoKortex vault."
|
"Create a brand new standardized PARA Node inside your ExoKortex vault.
|
||||||
|
TAG is a short slug for agenda/email filtering (e.g. "airbus_vision").
|
||||||
|
CONTEXT-TAG is the mu4e/agenda context (e.g. "TDNDE", "Perso")."
|
||||||
(interactive
|
(interactive
|
||||||
(list (completing-read "Category: " '("1-Projects" "2-Areas" "3-Resources"))
|
(list (completing-read "Category: " '("1-Projects" "2-Areas" "3-Resources"))
|
||||||
(read-string "Node Name: ")))
|
(read-string "Node Name: ")
|
||||||
|
(read-string "Project tag (slug for agenda/email filtering): ")
|
||||||
|
(completing-read "Context tag (TDNDE / Perso): " '("TDNDE" "Perso") nil t)))
|
||||||
(let* ((category-dir (expand-file-name category gortium/exokortex-root))
|
(let* ((category-dir (expand-file-name category gortium/exokortex-root))
|
||||||
(node-dir (expand-file-name node-name category-dir))
|
(node-dir (expand-file-name node-name category-dir))
|
||||||
(org-file (gortium/ensure-node-structure node-dir "Initialize project context")))
|
(org-file (gortium/ensure-node-structure node-dir
|
||||||
|
"Initialize project context" tag context-tag)))
|
||||||
(find-file org-file)
|
(find-file org-file)
|
||||||
(message "ExoKortex Node '%s' created in %s." node-name category)))
|
(message "ExoKortex Node '%s' created in %s with tag '%s' (context: %s)."
|
||||||
|
node-name category tag context-tag)))
|
||||||
|
|
||||||
(defun gortium/convert-to-node (node-dirs)
|
(defun gortium/convert-to-node (node-dirs)
|
||||||
"Retrofit existing workspace directories into tracked ExoKortex nodes.
|
"Retrofit existing workspace directories into tracked ExoKortex nodes.
|
||||||
@@ -3095,7 +3456,7 @@ Otherwise, prompts for a single directory, defaulting to the item under the curs
|
|||||||
(message "Successfully batch-converted %d directories to ExoKortex nodes!" count))))
|
(message "Successfully batch-converted %d directories to ExoKortex nodes!" count))))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Custom function to refile task to dailies
|
** Refile task to Roam dailies
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(defun gortium/refile-to-today-daily ()
|
(defun gortium/refile-to-today-daily ()
|
||||||
@@ -3159,35 +3520,52 @@ fallback to today's daily note. Ensures the daily has an Org-roam ID."
|
|||||||
headline
|
headline
|
||||||
(file-name-nondirectory daily-file)))))
|
(file-name-nondirectory daily-file)))))
|
||||||
#+end_src
|
#+end_src
|
||||||
** Custom function to create a datetree
|
** Project-Aware Agenda
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(require 'calendar)
|
|
||||||
|
|
||||||
(defun gortium/org-create-datetree-range ()
|
Reads =.dir-locals.el= from the current Projectile root and injects the
|
||||||
"Génère une structure datetree Org entre deux dates choisies via le calendrier interactif.
|
project-specific custom agenda command. Use ~SPC o P~ instead of ~SPC o a~ to
|
||||||
Si la date de début est validée vide, utilise la date d'aujourd'hui."
|
get a context-scoped agenda.
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(defun gortium/read-dir-local (var)
|
||||||
|
"Read variable VAR from .dir-locals.el in `default-directory'.
|
||||||
|
Returns nil if file doesn't exist or VAR isn't set."
|
||||||
|
(let ((dir-locals (expand-file-name ".dir-locals.el" default-directory)))
|
||||||
|
(when (file-exists-p dir-locals)
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert-file-contents dir-locals)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(let* ((sexp (ignore-errors (read (current-buffer))))
|
||||||
|
(nil-entry (assq nil sexp)))
|
||||||
|
(when nil-entry
|
||||||
|
(cdr (assq var (cdr nil-entry)))))))))
|
||||||
|
|
||||||
|
(defun gortium/project-aware-agenda ()
|
||||||
|
"Launch org-agenda scoped to the current Projectile project.
|
||||||
|
Reads `gortium/project-agenda-entry' from .dir-locals.el and merges
|
||||||
|
it into `org-agenda-custom-commands'. Restricts `org-agenda-files'
|
||||||
|
to .org files under the project root."
|
||||||
(interactive)
|
(interactive)
|
||||||
(let* ((start-time (org-read-date nil t nil "Date de début (Entrée pour aujourd'hui) : "))
|
(if (projectile-project-p)
|
||||||
(end-time (org-read-date nil t nil "Date de fin : "))
|
(let* ((proj-root (projectile-project-root))
|
||||||
(start-decoded (decode-time start-time))
|
(default-directory proj-root)
|
||||||
(end-decoded (decode-time end-time))
|
(agenda-entry (gortium/read-dir-local 'gortium/project-agenda-entry))
|
||||||
;; Format attendu par le calendrier Emacs : (mois jour année)
|
(org-agenda-files (directory-files-recursively proj-root "\\.org$"))
|
||||||
(start-date (list (decoded-time-month start-decoded)
|
(org-agenda-custom-commands
|
||||||
(decoded-time-day start-decoded)
|
(append org-agenda-custom-commands
|
||||||
(decoded-time-year start-decoded)))
|
(when agenda-entry (list agenda-entry)))))
|
||||||
(end-date (list (decoded-time-month end-decoded)
|
(org-agenda))
|
||||||
(decoded-time-day end-decoded)
|
(org-agenda)))
|
||||||
(decoded-time-year end-decoded)))
|
|
||||||
(start-abs (calendar-absolute-from-gregorian start-date))
|
(map! :leader
|
||||||
(end-abs (calendar-absolute-from-gregorian end-date)))
|
:desc "Project-aware agenda"
|
||||||
(if (< end-abs start-abs)
|
"o P" #'gortium/project-aware-agenda)
|
||||||
(user-error "Erreur : La date de fin doit être après la date de début ! ")
|
|
||||||
(save-excursion
|
|
||||||
(cl-loop for abs from start-abs to end-abs
|
|
||||||
do (org-datetree-find-date-create (calendar-gregorian-from-absolute abs))))
|
|
||||||
(message "Datetree généré avec succès !"))))
|
|
||||||
#+end_src
|
#+end_src
|
||||||
** Custom Emacs-Everywhere for Hyprland
|
|
||||||
|
#+RESULTS:
|
||||||
|
: gortium/project-aware-agenda
|
||||||
|
|
||||||
|
** Emacs-Everywhere for Hyprland
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
;; Like Emacs everywhere, but work in hyprland
|
;; Like Emacs everywhere, but work in hyprland
|
||||||
@@ -3364,3 +3742,219 @@ Need chrome... :(
|
|||||||
:localleader
|
:localleader
|
||||||
:desc "Edit Edna rules" "E" #'org-edna-edit))
|
:desc "Edit Edna rules" "E" #'org-edna-edit))
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
** Project-Aware Agenda
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
;; Remove stale project entries and inject current project's agenda
|
||||||
|
(defvar gortium/project-agenda-commands nil
|
||||||
|
"Cached project agenda commands.")
|
||||||
|
|
||||||
|
(defun gortium/inject-project-agenda ()
|
||||||
|
"Inject the current project's custom agenda commands into
|
||||||
|
`org-agenda-custom-commands', or remove them if no project."
|
||||||
|
(let* ((proj-root (and (projectile-project-p) (projectile-project-root)))
|
||||||
|
(project-commands nil))
|
||||||
|
(when proj-root
|
||||||
|
(let ((default-directory proj-root))
|
||||||
|
(setq project-commands
|
||||||
|
(gortium/read-dir-local 'gortium/project-agenda-entry))))
|
||||||
|
;; Remove stale project entries (key "P")
|
||||||
|
(setq org-agenda-custom-commands
|
||||||
|
(seq-filter (lambda (entry)
|
||||||
|
(not (and (stringp (car entry))
|
||||||
|
(string= "P" (car entry)))))
|
||||||
|
org-agenda-custom-commands))
|
||||||
|
;; Add current project commands
|
||||||
|
(when project-commands
|
||||||
|
(setq org-agenda-custom-commands
|
||||||
|
(append org-agenda-custom-commands project-commands)))))
|
||||||
|
|
||||||
|
;; Hook into project switching
|
||||||
|
(add-hook 'projectile-after-switch-project-hook
|
||||||
|
#'gortium/inject-project-agenda)
|
||||||
|
;; Also inject at startup and on file open in a project
|
||||||
|
(add-hook 'find-file-hook #'gortium/inject-project-agenda)
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
** Project Bookmarks
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(defvar gortium/project-bookmark-names nil
|
||||||
|
"Bookmark names currently in the Plan file for the active project.")
|
||||||
|
|
||||||
|
(defun gortium//project-plan-file ()
|
||||||
|
"Return the first .org file under 1-Plan/ at the project root, or nil."
|
||||||
|
(when-let* ((root (projectile-project-root)))
|
||||||
|
(car (directory-files (expand-file-name "1-Plan" root) t "\.org\'" t))))
|
||||||
|
|
||||||
|
(defun gortium//project-tag ()
|
||||||
|
"Read the project tag from .dir-locals.el in the current project."
|
||||||
|
(when (projectile-project-p)
|
||||||
|
(let ((default-directory (projectile-project-root)))
|
||||||
|
(gortium/read-dir-local 'gortium/project-tag))))
|
||||||
|
|
||||||
|
(defun gortium//read-plan-bookmarks ()
|
||||||
|
"Parse * Bookmarks from the Plan file.
|
||||||
|
Returns alist ((NAME . URL) ...) where URL is the link target."
|
||||||
|
(when-let* ((plan-file (gortium//project-plan-file))
|
||||||
|
(_ (file-exists-p plan-file)))
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert-file-contents plan-file)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(when (re-search-forward "^\* Bookmarks" nil t)
|
||||||
|
(forward-line 1)
|
||||||
|
(let ((bookmarks '()))
|
||||||
|
(while (not (or (eobp) (looking-at "^\* ")))
|
||||||
|
(cond
|
||||||
|
;; ** Name\n[[file:path][Name]]
|
||||||
|
((looking-at "^\*\* \(.+\)
|
||||||
|
\[\[file:\(.+\)\]\[\(.+\)\]\]")
|
||||||
|
(push (cons (match-string 1) (match-string 2)) bookmarks)
|
||||||
|
(forward-line 2))
|
||||||
|
;; standalone [[url][name]]
|
||||||
|
((looking-at "\[\[\(.+\)\]\[\(.+\)\]\]")
|
||||||
|
(unless (string-prefix-p "mu4e:" (match-string 1))
|
||||||
|
(push (cons (match-string 2) (match-string 1)) bookmarks))
|
||||||
|
(forward-line 1))
|
||||||
|
(t (forward-line 1))))
|
||||||
|
(setq gortium/project-bookmark-names (mapcar #'car bookmarks))
|
||||||
|
(nreverse bookmarks))))))
|
||||||
|
|
||||||
|
(defun gortium//append-bookmark-to-plan (name url)
|
||||||
|
"Append a bookmark as [[url][name]] under * Bookmarks in the Plan file.
|
||||||
|
NAME is the display name, URL is the link target (file path or web URL)."
|
||||||
|
(when-let* ((plan-file (gortium//project-plan-file)))
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert-file-contents plan-file)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(if (re-search-forward "^\* Bookmarks" nil t)
|
||||||
|
(forward-line 1)
|
||||||
|
(goto-char (point-max))
|
||||||
|
(insert "
|
||||||
|
* Bookmarks
|
||||||
|
"))
|
||||||
|
(insert (format "[[%s][%s]]
|
||||||
|
" url name))
|
||||||
|
(write-region (point-min) (point-max) plan-file nil 'silent)
|
||||||
|
(message "Bookmark added to %s" (file-name-nondirectory plan-file))
|
||||||
|
(push name gortium/project-bookmark-names))))
|
||||||
|
|
||||||
|
(defun gortium//remove-bookmark-from-plan (name)
|
||||||
|
"Remove a bookmark entry from * Bookmarks in the Plan file."
|
||||||
|
(when-let* ((plan-file (gortium//project-plan-file)))
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert-file-contents plan-file)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(when (re-search-forward (format "^\*\* %s
|
||||||
|
" (regexp-quote name)) nil t)
|
||||||
|
(forward-line -1)
|
||||||
|
(let ((start (point)))
|
||||||
|
(forward-line 2)
|
||||||
|
(delete-region start (point)))
|
||||||
|
(write-region (point-min) (point-max) plan-file nil 'silent)
|
||||||
|
(message "Bookmark '%s' removed from Plan file" name)
|
||||||
|
(setq gortium/project-bookmark-names
|
||||||
|
(delete name gortium/project-bookmark-names))))))
|
||||||
|
|
||||||
|
(defun gortium/bookmark-set ()
|
||||||
|
"Save a bookmark. Prompts: (g)lobal or (p)roject.
|
||||||
|
Project bookmarks go to * Bookmarks in the Plan file.
|
||||||
|
The prompt shows the project tag so you can verify the target."
|
||||||
|
(interactive)
|
||||||
|
(let ((choice (read-char-choice "Save: (g)lobal / (p)roject? " '(?g ?p))))
|
||||||
|
(pcase choice
|
||||||
|
(?g (call-interactively 'bookmark-set))
|
||||||
|
(?p
|
||||||
|
(if (projectile-project-p)
|
||||||
|
(let* ((tag (or (gortium//project-tag) "project"))
|
||||||
|
(name (read-string (format "[%s] Bookmark name: " tag)))
|
||||||
|
(file (buffer-file-name)))
|
||||||
|
(gortium//append-bookmark-to-plan name file)
|
||||||
|
(message "Project bookmark saved to Plan file."))
|
||||||
|
(user-error "Not in a Projectile project"))))))
|
||||||
|
|
||||||
|
(defun gortium/list-project-bookmarks ()
|
||||||
|
"Show project bookmarks in the bookmark menu (with categories).
|
||||||
|
Temporarily replaces bookmark-alist so the menu shows project bookmarks
|
||||||
|
in their native categories. On quit restores global bookmarks."
|
||||||
|
(interactive)
|
||||||
|
(let* ((bookmarks (gortium//read-plan-bookmarks))
|
||||||
|
(names (mapcar #'car bookmarks)))
|
||||||
|
(if (not names)
|
||||||
|
(message "No bookmarks found in this project's Plan file.")
|
||||||
|
(let ((saved-alist (copy-tree bookmark-alist)))
|
||||||
|
(setq bookmark-alist nil)
|
||||||
|
(dolist (bm (nreverse (copy-tree bookmarks)))
|
||||||
|
(bookmark-store (car bm) `((filename . ,(cdr bm))) nil))
|
||||||
|
(bookmark-bmenu-list)
|
||||||
|
(with-current-buffer "*Bookmark List*"
|
||||||
|
(add-hook 'kill-buffer-hook
|
||||||
|
(lambda ()
|
||||||
|
(setq bookmark-alist saved-alist)
|
||||||
|
;; Write changes back to Plan file
|
||||||
|
(when-let* ((plan-file (gortium//project-plan-file))
|
||||||
|
(entries (bookmark-maybe-sort-alist bookmark-alist)))
|
||||||
|
(gortium//sync-plan-to-bookmark-file
|
||||||
|
(mapcar (lambda (e)
|
||||||
|
(cons (car e)
|
||||||
|
(alist-get 'filename (cdr e))))
|
||||||
|
entries))))
|
||||||
|
nil t))))))
|
||||||
|
|
||||||
|
(defun gortium//sync-plan-to-bookmark-file (bookmarks)
|
||||||
|
"Write BOOKMARKS alist back to the Plan file under * Bookmarks."
|
||||||
|
(when-let* ((plan-file (gortium//project-plan-file)))
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert-file-contents plan-file)
|
||||||
|
(when (re-search-forward "^\* Bookmarks" nil t)
|
||||||
|
(forward-line 1)
|
||||||
|
(let ((start (point)))
|
||||||
|
(if (re-search-forward "^\* " nil t)
|
||||||
|
(forward-line -1)
|
||||||
|
(goto-char (point-max)))
|
||||||
|
(delete-region start (point)))
|
||||||
|
(dolist (bm (nreverse (copy-tree bookmarks)))
|
||||||
|
(insert (format "[[%s][%s]]
|
||||||
|
" (cdr bm) (car bm))))
|
||||||
|
(write-region (point-min) (point-max) plan-file nil 'silent)))))
|
||||||
|
|
||||||
|
(defun gortium/transfer-to-project ()
|
||||||
|
"Transfer a global bookmark to the current project's Plan file."
|
||||||
|
(interactive)
|
||||||
|
(if (projectile-project-p)
|
||||||
|
(let* ((name (completing-read "Transfer to project: " (bookmark-all-names) nil t))
|
||||||
|
(file (bookmark-location name)))
|
||||||
|
(when (and name file (y-or-n-p (format "Transfer '%s' to project? " name)))
|
||||||
|
(gortium//append-bookmark-to-plan name file)
|
||||||
|
(bookmark-delete name)
|
||||||
|
(bookmark-save)
|
||||||
|
(message "Transferred '%s' to project %s" name
|
||||||
|
(or (gortium//project-tag) ""))))
|
||||||
|
(user-error "Not in a Projectile project")))
|
||||||
|
|
||||||
|
(defun gortium/transfer-to-global ()
|
||||||
|
"Transfer a project bookmark to the global bookmarks list."
|
||||||
|
(interactive)
|
||||||
|
(let* ((bookmarks (gortium//read-plan-bookmarks))
|
||||||
|
(names (mapcar #'car bookmarks)))
|
||||||
|
(if names
|
||||||
|
(let* ((name (completing-read "Transfer to global: " names nil t))
|
||||||
|
(url (cdr (assoc name bookmarks))))
|
||||||
|
(when (and name url (y-or-n-p (format "Transfer '%s' to global? " name)))
|
||||||
|
(bookmark-store name `((filename . ,url)) t)
|
||||||
|
(bookmark-save)
|
||||||
|
(gortium//remove-bookmark-from-plan name)
|
||||||
|
(message "Transferred '%s' to global" name)))
|
||||||
|
(message "No project bookmarks to transfer."))))
|
||||||
|
|
||||||
|
;; Bindings
|
||||||
|
(map! :leader
|
||||||
|
:desc "Set bookmark (global/project)"
|
||||||
|
"b m" #'gortium/bookmark-set
|
||||||
|
:desc "Jump to project bookmark"
|
||||||
|
"p RET" #'gortium/list-project-bookmarks)
|
||||||
|
#+end_src
|
||||||
@@ -12,6 +12,12 @@
|
|||||||
|
|
||||||
(setq doom-theme 'doom-gruvbox)
|
(setq doom-theme 'doom-gruvbox)
|
||||||
|
|
||||||
|
(after! projectile
|
||||||
|
;; Only .projectile files define a project root
|
||||||
|
(setq projectile-project-root-files '(".projectile"))
|
||||||
|
;; Stop auto-scanning everything — only explicit .projectile markers count
|
||||||
|
(setq projectile-auto-discover nil))
|
||||||
|
|
||||||
(setq bookmark-save-flag 1)
|
(setq bookmark-save-flag 1)
|
||||||
|
|
||||||
(setq global-auto-revert-mode 1)
|
(setq global-auto-revert-mode 1)
|
||||||
@@ -22,6 +28,10 @@
|
|||||||
(:prefix ("z" . "Personal")
|
(:prefix ("z" . "Personal")
|
||||||
:desc "Hermes Agent" "h" #'gortium/hermes-vterm))
|
:desc "Hermes Agent" "h" #'gortium/hermes-vterm))
|
||||||
|
|
||||||
|
(map! :leader
|
||||||
|
(:prefix ("z" . "Personal")
|
||||||
|
:desc "Hermes Agent" "h" #'gortium/hermes-vterm))
|
||||||
|
|
||||||
(use-package! ultra-scroll
|
(use-package! ultra-scroll
|
||||||
:init
|
:init
|
||||||
(setq scroll-margin 0 ; Required for ultra-scroll
|
(setq scroll-margin 0 ; Required for ultra-scroll
|
||||||
@@ -56,7 +66,7 @@
|
|||||||
(defvar journal-base-path
|
(defvar journal-base-path
|
||||||
(file-name-as-directory "~/ExoKortex/2-Areas/Journal"))
|
(file-name-as-directory "~/ExoKortex/2-Areas/Journal"))
|
||||||
|
|
||||||
;; Fixes..
|
;; Fixes.. idk why or what.. dont ask me
|
||||||
(require 'cl-lib)
|
(require 'cl-lib)
|
||||||
(unless (fboundp 'find-if)
|
(unless (fboundp 'find-if)
|
||||||
(defalias 'find-if #'cl-find-if))
|
(defalias 'find-if #'cl-find-if))
|
||||||
@@ -71,8 +81,18 @@
|
|||||||
+org-capture-emails-file "~/ExoKortex/inbox.org"
|
+org-capture-emails-file "~/ExoKortex/inbox.org"
|
||||||
;; +org-capture-central-project-todo-file '"refile.org"
|
;; +org-capture-central-project-todo-file '"refile.org"
|
||||||
;; +org-capture-project-todo-file '"refile.org"
|
;; +org-capture-project-todo-file '"refile.org"
|
||||||
org-agenda-files (directory-files-recursively "~/ExoKortex/" "\\.org$")
|
org-agenda-files
|
||||||
)
|
(let ((all (directory-files-recursively "~/ExoKortex/" "\\.org$")))
|
||||||
|
(append '("~/ExoKortex/inbox.org"
|
||||||
|
"~/ExoKortex/projects.org"
|
||||||
|
"~/ExoKortex/areas.org"
|
||||||
|
"~/ExoKortex/journal.org"
|
||||||
|
"~/ExoKortex/telos_thierry_pouplier.org")
|
||||||
|
(seq-filter
|
||||||
|
(lambda (f) (string-match-p "/1-Plan/" f))
|
||||||
|
all)))
|
||||||
|
org-agenda-files (seq-uniq org-agenda-files)
|
||||||
|
)
|
||||||
|
|
||||||
(after! org
|
(after! org
|
||||||
(setq org-cycle-show-planning-line t)
|
(setq org-cycle-show-planning-line t)
|
||||||
@@ -96,10 +116,19 @@
|
|||||||
'(org-level-6 :foreground "#fe8019" :weight normal :height 1.05) ; orange
|
'(org-level-6 :foreground "#fe8019" :weight normal :height 1.05) ; orange
|
||||||
'(org-level-7 :foreground "#cc241d" :weight normal :height 1.02) ; red
|
'(org-level-7 :foreground "#cc241d" :weight normal :height 1.02) ; red
|
||||||
'(org-level-8 :foreground "#a89984" :weight normal :height 1.00)) ; grey-brown
|
'(org-level-8 :foreground "#a89984" :weight normal :height 1.00)) ; grey-brown
|
||||||
|
;; '(org-level-1 :weight semi-bold :height 1.45)
|
||||||
|
;; '(org-level-2 :weight semi-bold :height 1.35)
|
||||||
|
;; '(org-level-3 :weight semi-bold :height 1.25)
|
||||||
|
;; '(org-level-4 :weight semi-bold :height 1.15)
|
||||||
|
;; '(org-level-5 :weight normal :height 1.10)
|
||||||
|
;; '(org-level-6 :weight normal :height 1.05)
|
||||||
|
;; '(org-level-7 :weight normal :height 1.02)
|
||||||
|
;; '(org-level-8 :weight normal :height 1.00))
|
||||||
|
|
||||||
;; Clamp all levels beyond 8 to use org-level-8 face
|
;; Clamp all levels beyond 8 to use org-level-8 face
|
||||||
(defvar gortium/org-level-8-face
|
(defvar gortium/org-level-8-face
|
||||||
'((t :foreground "#a89984" :weight normal :height 1.00)))
|
'((t :foreground "#a89984" :weight normal :height 1.00)))
|
||||||
|
;; '((t :weight normal :height 1.00)))
|
||||||
|
|
||||||
;; Définir org-level-9 à org-level-20 avec la même face que org-level-8
|
;; Définir org-level-9 à org-level-20 avec la même face que org-level-8
|
||||||
(cl-loop for lvl from 9 to 20
|
(cl-loop for lvl from 9 to 20
|
||||||
@@ -178,6 +207,7 @@
|
|||||||
org-agenda-skip-deadline-if-done nil
|
org-agenda-skip-deadline-if-done nil
|
||||||
org-agenda-skip-scheduled-if-done nil
|
org-agenda-skip-scheduled-if-done nil
|
||||||
org-agenda-skip-timestamp-if-done nil
|
org-agenda-skip-timestamp-if-done nil
|
||||||
|
org-agenda-sticky t
|
||||||
org-journal-enable-agenda-integration t
|
org-journal-enable-agenda-integration t
|
||||||
org-log-done 'time
|
org-log-done 'time
|
||||||
org-agenda-span 'day
|
org-agenda-span 'day
|
||||||
@@ -355,7 +385,7 @@
|
|||||||
)
|
)
|
||||||
((org-agenda-tag-filter-preset '("+work")))
|
((org-agenda-tag-filter-preset '("+work")))
|
||||||
)
|
)
|
||||||
("pp" "Planning"
|
("pp" "Planning 1 month"
|
||||||
((agenda ""
|
((agenda ""
|
||||||
((org-agenda-span 'month)
|
((org-agenda-span 'month)
|
||||||
(org-agenda-start-day nil)
|
(org-agenda-start-day nil)
|
||||||
@@ -379,6 +409,54 @@
|
|||||||
)
|
)
|
||||||
((org-agenda-tag-filter-preset '("+perso")))
|
((org-agenda-tag-filter-preset '("+perso")))
|
||||||
)
|
)
|
||||||
|
("pP" "Planning 2 months"
|
||||||
|
((agenda ""
|
||||||
|
((org-agenda-span 60)
|
||||||
|
(org-agenda-start-day nil)
|
||||||
|
(org-agenda-overriding-header "📅 Sheduled Tasks")
|
||||||
|
(org-agenda-time-grid nil)
|
||||||
|
(org-agenda-prefix-format " %12t") ;; reserve time space
|
||||||
|
(org-agenda-todo-keyword-format " ") ;; fixed-width TODO
|
||||||
|
(org-agenda-tags-column -100) ;; right-align tags
|
||||||
|
(org-agenda-remove-tags nil))
|
||||||
|
)
|
||||||
|
|
||||||
|
(todo "TODO|NEXT|DELG|WAIT|HOLD|STRT|IDEA"
|
||||||
|
((org-agenda-overriding-header "📋 Unscheduled TODOs")
|
||||||
|
(org-agenda-skip-function '(org-agenda-skip-entry-if 'scheduled))
|
||||||
|
(org-agenda-prefix-format " %12t") ;; reserve time space
|
||||||
|
(org-agenda-todo-keyword-format " ") ;; fixed-width TODO
|
||||||
|
(org-agenda-tags-column -100) ;; right-align tags
|
||||||
|
(org-agenda-remove-tags t)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
((org-agenda-tag-filter-preset '("+perso")))
|
||||||
|
)
|
||||||
|
("pI" "ImmoFacile"
|
||||||
|
((agenda ""
|
||||||
|
((org-agenda-span 'month)
|
||||||
|
(org-agenda-start-day nil)
|
||||||
|
(org-agenda-overriding-header "📅 Sheduled Tasks")
|
||||||
|
(org-agenda-time-grid nil)
|
||||||
|
(org-agenda-prefix-format " %12t") ;; reserve time space
|
||||||
|
(org-agenda-todo-keyword-format " ") ;; fixed-width TODO
|
||||||
|
(org-agenda-tags-column -100) ;; right-align tags
|
||||||
|
(org-agenda-remove-tags t))
|
||||||
|
)
|
||||||
|
|
||||||
|
(todo "TODO|NEXT|DELG|WAIT|HOLD|STRT|IDEA"
|
||||||
|
((org-agenda-overriding-header "📋 Unscheduled TODOs")
|
||||||
|
(org-agenda-skip-function '(org-agenda-skip-entry-if 'scheduled))
|
||||||
|
(org-agenda-prefix-format " %12t") ;; reserve time space
|
||||||
|
(org-agenda-todo-keyword-format " ") ;; fixed-width TODO
|
||||||
|
(org-agenda-tags-column -100) ;; right-align tags
|
||||||
|
(org-agenda-remove-tags t)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
((org-agenda-tag-filter-preset '("+ImmoFacile")))
|
||||||
|
)
|
||||||
("wP" "Installation Bombardier"
|
("wP" "Installation Bombardier"
|
||||||
((agenda ""
|
((agenda ""
|
||||||
((org-agenda-span 90)
|
((org-agenda-span 90)
|
||||||
@@ -769,6 +847,12 @@ Handles org-clock and context link capture for tasks."
|
|||||||
(after! text-mode
|
(after! text-mode
|
||||||
(add-hook 'text-mode-hook #'visual-line-mode))
|
(add-hook 'text-mode-hook #'visual-line-mode))
|
||||||
|
|
||||||
|
(use-package! rainbow-delimiters
|
||||||
|
:hook ((emacs-lisp-mode lisp-mode scheme-mode clojure-mode) . rainbow-delimiters-mode))
|
||||||
|
|
||||||
|
(after! org-src
|
||||||
|
(add-hook 'org-src-mode-hook #'rainbow-delimiters-mode))
|
||||||
|
|
||||||
(use-package! org-phscroll
|
(use-package! org-phscroll
|
||||||
:after org)
|
:after org)
|
||||||
|
|
||||||
@@ -1257,7 +1341,8 @@ DIFF:
|
|||||||
|
|
||||||
(after! mu4e
|
(after! mu4e
|
||||||
(setq mu4e-maildir-shortcuts mu4e-maildir-list mu4e-maildir-initial-input mu4e-maildir-info-delimiter)
|
(setq mu4e-maildir-shortcuts mu4e-maildir-list mu4e-maildir-initial-input mu4e-maildir-info-delimiter)
|
||||||
|
(setq mu4e-update-interval 300)
|
||||||
|
(setq mu4e-headers-auto-update t)
|
||||||
(setq mu4e-contexts
|
(setq mu4e-contexts
|
||||||
(list
|
(list
|
||||||
;; --- CONTEXT: TDNDE ---
|
;; --- CONTEXT: TDNDE ---
|
||||||
@@ -1366,6 +1451,183 @@ Cordialement,
|
|||||||
(evil-define-key 'visual mu4e-view-mode-map "l" #'mu4e-view-mark-for-label)
|
(evil-define-key 'visual mu4e-view-mode-map "l" #'mu4e-view-mark-for-label)
|
||||||
(evil-define-key 'emacs mu4e-view-mode-map "l" #'mu4e-view-mark-for-label)))
|
(evil-define-key 'emacs mu4e-view-mode-map "l" #'mu4e-view-mark-for-label)))
|
||||||
|
|
||||||
|
(defconst gortium/project-mu4e-keys '(?I ?S ?D ?T ?A)
|
||||||
|
"Character keys used for project mu4e maildir shortcuts (uppercase avoids conflicts).")
|
||||||
|
|
||||||
|
(defvar gortium/project-mu4e-bookmarks nil
|
||||||
|
"Cached project mu4e maildir shortcuts for re-injection when mu4e loads.")
|
||||||
|
|
||||||
|
(defun gortium/inject-project-mu4e-bookmarks ()
|
||||||
|
"Inject project-specific mu4e bookmark shortcuts [PI]/[PS]/[PD]/[PT]/[PA].
|
||||||
|
Caches them for re-injection when mu4e loads."
|
||||||
|
(let* ((proj-root (and (projectile-project-p) (projectile-project-root)))
|
||||||
|
(proj-tag (when proj-root
|
||||||
|
(let ((default-directory proj-root))
|
||||||
|
(gortium/read-dir-local 'gortium/project-tag))))
|
||||||
|
(project-bm '()))
|
||||||
|
(when proj-tag
|
||||||
|
(dolist (folder '(("Inbox" . ?I)
|
||||||
|
("Sent" . ?S)
|
||||||
|
("Drafts" . ?D)
|
||||||
|
("Trash" . ?T)
|
||||||
|
("Archive" . ?A)))
|
||||||
|
(push (list :name (format "[%s] %s" proj-tag (car folder))
|
||||||
|
:query (format "maildir:/%s.%s" (car folder) proj-tag)
|
||||||
|
:key (cdr folder))
|
||||||
|
project-bm)))
|
||||||
|
;; Cache for later re-injection
|
||||||
|
(setq gortium/project-mu4e-bookmarks project-bm)
|
||||||
|
;; Inject now if mu4e is already loaded
|
||||||
|
(when (boundp 'mu4e-bookmarks)
|
||||||
|
(setq mu4e-bookmarks
|
||||||
|
(append
|
||||||
|
(seq-filter (lambda (bm)
|
||||||
|
(if (and (listp bm) (keywordp (car-safe bm)))
|
||||||
|
(let ((key (plist-get bm :key)))
|
||||||
|
(not (member key gortium/project-mu4e-keys)))
|
||||||
|
t))
|
||||||
|
mu4e-bookmarks)
|
||||||
|
gortium/project-mu4e-bookmarks))
|
||||||
|
;; Clear caches so new bookmarks appear
|
||||||
|
(setq mu4e--bookmark-items-cached nil)
|
||||||
|
;; Re-render main view if visible
|
||||||
|
(when (get-buffer "*mu4e-main*")
|
||||||
|
(with-current-buffer "*mu4e-main*"
|
||||||
|
(mu4e--main-redraw))))))
|
||||||
|
|
||||||
|
(add-hook 'projectile-after-switch-project-hook
|
||||||
|
#'gortium/inject-project-mu4e-bookmarks)
|
||||||
|
(add-hook 'find-file-hook #'gortium/inject-project-mu4e-bookmarks)
|
||||||
|
(add-hook 'mu4e-main-mode-hook #'gortium/inject-project-mu4e-bookmarks)
|
||||||
|
(after! mu4e
|
||||||
|
(gortium/inject-project-mu4e-bookmarks))
|
||||||
|
|
||||||
|
(after! elfeed
|
||||||
|
;; Define where your feed sources are kept (Org-mode file integration)
|
||||||
|
(setq rmh-elfeed-org-files '("~/ExoKortex/2-Areas/Home-Infra/2-Assets/rss-feed.org"))
|
||||||
|
|
||||||
|
;; Set the default search filter (only show unread entries from the last x weeks)
|
||||||
|
(setq-default elfeed-search-filter "@4-weeks-ago +unread")
|
||||||
|
|
||||||
|
;; Automatically fetch feed updates over the network when you first open Elfeed
|
||||||
|
(add-hook! 'elfeed-search-mode-hook #'elfeed-update)
|
||||||
|
|
||||||
|
;; Optimize connection parameters to prevent lagging and queue timeouts
|
||||||
|
(setq elfeed-connection-timeout 10) ; Timeout limit for fetching a single feed (seconds)
|
||||||
|
(setq url-queue-timeout 15) ; Maximum wait time for the network queue
|
||||||
|
|
||||||
|
;; Set the function used to display entries (pop-to-buffer prevents taking over the whole screen)
|
||||||
|
(setq elfeed-show-entry-switch #'pop-to-buffer)
|
||||||
|
|
||||||
|
;; A pure MPV launcher (Bypasses buggy EMMS metadata scanners)
|
||||||
|
(defun gortium/launch-mpv-overlay (url)
|
||||||
|
"Launch MPV as a floating overlay directly via system process."
|
||||||
|
(start-process "elfeed-mpv" nil "mpv"
|
||||||
|
"--quiet" "--really-quiet"
|
||||||
|
"--geometry=30%x30%-30-50" "--ontop" "--no-border"
|
||||||
|
"--title=Elfeed MPV"
|
||||||
|
url))
|
||||||
|
|
||||||
|
;; Smart Elfeed Dispatcher
|
||||||
|
(defun gortium/elfeed-open-dispatcher ()
|
||||||
|
"Play YouTube and Podcasts directly in MPV; open web links in EWW."
|
||||||
|
(interactive)
|
||||||
|
(let* ((entry (if (eq major-mode 'elfeed-show-mode)
|
||||||
|
elfeed-show-entry
|
||||||
|
(elfeed-search-selected :single)))
|
||||||
|
(link (elfeed-entry-link entry))
|
||||||
|
;; Extract enclosures (where podcasts store their mp3s)
|
||||||
|
(enclosures (elfeed-entry-enclosures entry))
|
||||||
|
(enclosure-url (when enclosures (car (car enclosures)))))
|
||||||
|
(cond
|
||||||
|
;; Case 1: YouTube -> Direct MPV Overlay
|
||||||
|
((and link (string-match-p "youtu\\(?:\\.be\\|be\\.com\\)" link))
|
||||||
|
(message "Streaming YouTube video in MPV: %s" link)
|
||||||
|
(gortium/launch-mpv-overlay link))
|
||||||
|
|
||||||
|
;; Case 2: Podcast/Media Enclosure found -> Direct MPV Overlay
|
||||||
|
(enclosure-url
|
||||||
|
(message "Streaming podcast enclosure in MPV: %s" enclosure-url)
|
||||||
|
(gortium/launch-mpv-overlay enclosure-url))
|
||||||
|
|
||||||
|
;; Case 3: Standard Web Article (Must start with http) -> EWW
|
||||||
|
((and link (string-match-p "^http" link))
|
||||||
|
(message "Opening article in EWW (readable): %s" link)
|
||||||
|
(cond
|
||||||
|
((eq major-mode 'elfeed-show-mode)
|
||||||
|
(eww link))
|
||||||
|
(t
|
||||||
|
(let ((target-window (if (one-window-p)
|
||||||
|
(split-window-right)
|
||||||
|
(next-window))))
|
||||||
|
(select-window target-window)
|
||||||
|
(eww link))))
|
||||||
|
(add-hook 'eww-after-render-hook #'eww-readable nil t))
|
||||||
|
|
||||||
|
;; Case 4: Safety Fallback
|
||||||
|
(t
|
||||||
|
(message "No playable media or valid web link found for this entry.")))))
|
||||||
|
|
||||||
|
;; Bind the dispatcher
|
||||||
|
(map! :map elfeed-search-mode-map
|
||||||
|
"M-RET" #'gortium/elfeed-open-dispatcher)
|
||||||
|
|
||||||
|
(map! :map elfeed-show-mode-map
|
||||||
|
"M-RET" #'gortium/elfeed-open-dispatcher))
|
||||||
|
|
||||||
|
;; Initialize and configure elfeed-goodies for an enhanced UI layout
|
||||||
|
(after! elfeed-goodies
|
||||||
|
;; Enable the standard elfeed-goodies setup (adds search headers and status lines)
|
||||||
|
(elfeed-goodies/setup)
|
||||||
|
|
||||||
|
;; Choose where to split the window when viewing an entry ('bottom, 'left, 'right, 'top)
|
||||||
|
(setq elfeed-goodies/entry-pane-position 'right)
|
||||||
|
|
||||||
|
;; Define how much screen space the entry display pane takes (0.5 = 50% of the frame)
|
||||||
|
(setq elfeed-goodies/entry-pane-size 0.5)
|
||||||
|
|
||||||
|
;; Display feed tags in their own clean, separate column in the search buffer
|
||||||
|
(setq elfeed-goodies/show-tags-as-column t)
|
||||||
|
|
||||||
|
;; Prevent feed source URLs from cluttering up the title line in search results
|
||||||
|
(setq elfeed-goodies/feed-source-column-width 16))
|
||||||
|
|
||||||
|
;; Configure elfeed-tube (active when +youtube flag is enabled)
|
||||||
|
(after! elfeed-tube
|
||||||
|
;; Initialize elfeed-tube to fetch YouTube metadata and transcripts
|
||||||
|
(elfeed-tube-setup)
|
||||||
|
|
||||||
|
;; Define a quick binding to manually fetch/refresh a transcript if auto-fetch is idle
|
||||||
|
(map! :map elfeed-show-mode-map
|
||||||
|
:ne "F" #'elfeed-tube-fetch))
|
||||||
|
|
||||||
|
;; Configure EMMS to use MPV for streaming
|
||||||
|
(after! emms
|
||||||
|
(require 'emms-setup)
|
||||||
|
(emms-all)
|
||||||
|
(require 'emms-source-file)
|
||||||
|
|
||||||
|
;; Swap VLC out for MPV (which seamlessly leverages yt-dlp)
|
||||||
|
(setq emms-player-list '(emms-player-mpv))
|
||||||
|
|
||||||
|
;; Force EMMS to route HTTP/HTTPS URLs matching YouTube patterns to MPV
|
||||||
|
(emms-player-set emms-player-mpv 'regex
|
||||||
|
(rx (or (: "http" (? "s") "://" (* nonl) "youtube.com" (* nonl))
|
||||||
|
(: "http" (? "s") "://" (* nonl) "youtu.be" (* nonl))))))
|
||||||
|
|
||||||
|
(use-package! eaf
|
||||||
|
:load-path "~/.config/emacs/.local/straight/repos/emacs-application-framework"
|
||||||
|
:config
|
||||||
|
(require 'eaf)
|
||||||
|
|
||||||
|
;; Load the Browser module (for YouTube web streams)
|
||||||
|
(add-to-list 'load-path "~/.config/emacs/.local/straight/repos/emacs-application-framework/app/browser")
|
||||||
|
(require 'eaf-browser)
|
||||||
|
|
||||||
|
;; Load the Video Player module (for local video files)
|
||||||
|
(add-to-list 'load-path "~/.config/emacs/.local/straight/repos/emacs-application-framework/app/video-player")
|
||||||
|
(require 'eaf-video-player))
|
||||||
|
|
||||||
(defun gortium-circe-nickserv-password (server)
|
(defun gortium-circe-nickserv-password (server)
|
||||||
(auth-source-passage-get 'secret "irc"))
|
(auth-source-passage-get 'secret "irc"))
|
||||||
|
|
||||||
@@ -1424,6 +1686,32 @@ If FILE is nil, refile in the current file."
|
|||||||
(org-paste-subtree 4)
|
(org-paste-subtree 4)
|
||||||
(widen)))))
|
(widen)))))
|
||||||
|
|
||||||
|
(require 'calendar)
|
||||||
|
|
||||||
|
(defun gortium/org-create-datetree-range ()
|
||||||
|
"Génère une structure datetree Org entre deux dates choisies via le calendrier interactif.
|
||||||
|
Si la date de début est validée vide, utilise la date d'aujourd'hui."
|
||||||
|
(interactive)
|
||||||
|
(let* ((start-time (org-read-date nil t nil "Date de début (Entrée pour aujourd'hui) : "))
|
||||||
|
(end-time (org-read-date nil t nil "Date de fin : "))
|
||||||
|
(start-decoded (decode-time start-time))
|
||||||
|
(end-decoded (decode-time end-time))
|
||||||
|
;; Format attendu par le calendrier Emacs : (mois jour année)
|
||||||
|
(start-date (list (decoded-time-month start-decoded)
|
||||||
|
(decoded-time-day start-decoded)
|
||||||
|
(decoded-time-year start-decoded)))
|
||||||
|
(end-date (list (decoded-time-month end-decoded)
|
||||||
|
(decoded-time-day end-decoded)
|
||||||
|
(decoded-time-year end-decoded)))
|
||||||
|
(start-abs (calendar-absolute-from-gregorian start-date))
|
||||||
|
(end-abs (calendar-absolute-from-gregorian end-date)))
|
||||||
|
(if (< end-abs start-abs)
|
||||||
|
(user-error "Erreur : La date de fin doit être après la date de début ! ")
|
||||||
|
(save-excursion
|
||||||
|
(cl-loop for abs from start-abs to end-abs
|
||||||
|
do (org-datetree-find-date-create (calendar-gregorian-from-absolute abs))))
|
||||||
|
(message "Datetree généré avec succès !"))))
|
||||||
|
|
||||||
;; Open all org fold in ediff
|
;; Open all org fold in ediff
|
||||||
(defun org-ediff-prepare-buffer ()
|
(defun org-ediff-prepare-buffer ()
|
||||||
(when (memq major-mode '(org-mode emacs-lisp-mode))
|
(when (memq major-mode '(org-mode emacs-lisp-mode))
|
||||||
@@ -2092,10 +2380,11 @@ Suppresses all logging, notes, and custom odometer hooks."
|
|||||||
(defvar gortium/exokortex-root "~/ExoKortex"
|
(defvar gortium/exokortex-root "~/ExoKortex"
|
||||||
"The absolute root directory of your ExoKortex.")
|
"The absolute root directory of your ExoKortex.")
|
||||||
|
|
||||||
(defun gortium/ensure-node-structure (node-dir &optional default-todo)
|
(defun gortium/ensure-node-structure (node-dir &optional default-todo project-tag context-tag)
|
||||||
"Ensure NODE-DIR conforms to the ExoKortex node blueprint.
|
"Ensure NODE-DIR conforms to the ExoKortex node blueprint.
|
||||||
Creates folders, writes text-layer filters, and handles Git injection.
|
Creates folders, writes text-layer filters, and handles Git injection.
|
||||||
Returns the absolute path to the core Org file."
|
When PROJECT-TAG is provided, also creates .projectile and .dir-locals.el
|
||||||
|
with a project-tagged custom agenda entry and optional CONTEXT-TAG (mu4e/agenda)."
|
||||||
(let* ((node-dir (expand-file-name node-dir))
|
(let* ((node-dir (expand-file-name node-dir))
|
||||||
(node-name (file-name-nondirectory (directory-file-name node-dir)))
|
(node-name (file-name-nondirectory (directory-file-name node-dir)))
|
||||||
;; Blueprint Paths
|
;; Blueprint Paths
|
||||||
@@ -2119,39 +2408,84 @@ Returns the absolute path to the core Org file."
|
|||||||
;; 3. Build index Org block if absent
|
;; 3. Build index Org block if absent
|
||||||
(unless (file-exists-p org-file)
|
(unless (file-exists-p org-file)
|
||||||
(with-temp-file org-file
|
(with-temp-file org-file
|
||||||
(insert (format "#+TITLE: %s\n#+DATE: %s\n#+CATEGORY: %s\n\n* Setup\n- [ ] %s\n"
|
(insert (format "#+TITLE: %s\n#+DATE: %s\n#+CATEGORY: %s\n#+FILETAGS: :%s:\n\n* Setup\nTODO %s\n\n* Bookmarks\n** Example link\n[[file:./%s.org][Example link for %s]]\n\n* Contacts\n** Contact Name\n:PROPERTIES:\n:EMAIL: contact@example.com\n:ROLE: Client / Partner\n:END:\n\n[[mu4e:from:contact@example.com][Correspondence with Contact Name]]\n\n* Tasks\n** TODO First task for %s\n"
|
||||||
node-name
|
node-name
|
||||||
(format-time-string "[%Y-%m-%d %a]")
|
(format-time-string "[%Y-%m-%d %a]")
|
||||||
node-name
|
node-name
|
||||||
(or default-todo "Initialize project context")))))
|
(or project-tag "project")
|
||||||
|
(or default-todo "Initialize project context")
|
||||||
|
node-name
|
||||||
|
node-name
|
||||||
|
node-name))))
|
||||||
|
|
||||||
;; 4. Unified Git Integration Loop
|
;; 4. ExoKortex project markers (if project-tag provided)
|
||||||
|
(when project-tag
|
||||||
|
(let ((projectile-file (expand-file-name ".projectile" node-dir)))
|
||||||
|
(unless (file-exists-p projectile-file)
|
||||||
|
(with-temp-file projectile-file
|
||||||
|
(insert ""))))
|
||||||
|
|
||||||
|
(let ((dir-locals-file (expand-file-name ".dir-locals.el" node-dir)))
|
||||||
|
(unless (file-exists-p dir-locals-file)
|
||||||
|
(with-temp-file dir-locals-file
|
||||||
|
(prin1
|
||||||
|
`((nil . ((gortium/project-tag . ,project-tag)
|
||||||
|
(gortium/project-agenda-entry .
|
||||||
|
(("P" ,node-name
|
||||||
|
((agenda "" ((org-agenda-span 14)
|
||||||
|
(org-agenda-overriding-header
|
||||||
|
,(format "📅 %s" node-name))))
|
||||||
|
(todo "TODO|NEXT|DELG|WAIT|HOLD|STRT|IDEA"
|
||||||
|
((org-agenda-overriding-header "📋 Unscheduled")
|
||||||
|
(org-agenda-skip-function
|
||||||
|
'(org-agenda-skip-entry-if 'scheduled)))))
|
||||||
|
|
||||||
|
((org-agenda-tag-filter-preset (list ,(concat "+" project-tag))))))))
|
||||||
|
,@(if context-tag
|
||||||
|
`((gortium/context-tag . ,context-tag))
|
||||||
|
nil))))
|
||||||
|
(current-buffer))))))
|
||||||
|
|
||||||
|
;; 5. Unified Git Integration Loop
|
||||||
(let ((default-directory node-dir))
|
(let ((default-directory node-dir))
|
||||||
(if (not git-existed-p)
|
(if (not git-existed-p)
|
||||||
(progn
|
(progn
|
||||||
(shell-command "git init")
|
(shell-command "git init")
|
||||||
(shell-command "git add .gitignore 1-Plan/ 3-Notes/")
|
(let ((add-files ".gitignore 1-Plan/ 3-Notes/"))
|
||||||
|
(when project-tag
|
||||||
|
(setq add-files (concat add-files " .projectile .dir-locals.el")))
|
||||||
|
(shell-command (concat "git add -f " add-files)))
|
||||||
(shell-command "git commit -m 'System: Initialize node repository'"))
|
(shell-command "git commit -m 'System: Initialize node repository'"))
|
||||||
(shell-command "git add .gitignore 1-Plan/ 3-Notes/")))
|
|
||||||
|
;; existing repo: add new marker files if they exist
|
||||||
|
(let ((add-files ".gitignore 1-Plan/ 3-Notes/"))
|
||||||
|
(when project-tag
|
||||||
|
(setq add-files (concat add-files " .projectile .dir-locals.el")))
|
||||||
|
(shell-command (concat "git add -f " add-files)))))
|
||||||
|
|
||||||
;; Pass the target file location back up to the caller
|
;; Pass the target file location back up to the caller
|
||||||
org-file))
|
org-file)
|
||||||
|
|
||||||
|
|
||||||
;;; ====================================================================
|
;;; ====================================================================
|
||||||
;;; Interactive Entry Points
|
;;; Interactive Entry Points
|
||||||
;;; ====================================================================
|
;;; ====================================================================
|
||||||
|
|
||||||
(defun gortium/create-node (category node-name)
|
(defun gortium/create-node (category node-name tag context-tag)
|
||||||
"Create a brand new standardized PARA Node inside your ExoKortex vault."
|
"Create a brand new standardized PARA Node inside your ExoKortex vault.
|
||||||
|
TAG is a short slug for agenda/email filtering (e.g. "airbus_vision").
|
||||||
|
CONTEXT-TAG is the mu4e/agenda context (e.g. "TDNDE", "Perso")."
|
||||||
(interactive
|
(interactive
|
||||||
(list (completing-read "Category: " '("1-Projects" "2-Areas" "3-Resources"))
|
(list (completing-read "Category: " '("1-Projects" "2-Areas" "3-Resources"))
|
||||||
(read-string "Node Name: ")))
|
(read-string "Node Name: ")
|
||||||
|
(read-string "Project tag (slug for agenda/email filtering): ")
|
||||||
|
(completing-read "Context tag (TDNDE / Perso): " '("TDNDE" "Perso") nil t)))
|
||||||
(let* ((category-dir (expand-file-name category gortium/exokortex-root))
|
(let* ((category-dir (expand-file-name category gortium/exokortex-root))
|
||||||
(node-dir (expand-file-name node-name category-dir))
|
(node-dir (expand-file-name node-name category-dir))
|
||||||
(org-file (gortium/ensure-node-structure node-dir "Initialize project context")))
|
(org-file (gortium/ensure-node-structure node-dir
|
||||||
|
"Initialize project context" tag context-tag)))
|
||||||
(find-file org-file)
|
(find-file org-file)
|
||||||
(message "ExoKortex Node '%s' created in %s." node-name category)))
|
(message "ExoKortex Node '%s' created in %s with tag '%s' (context: %s)."
|
||||||
|
node-name category tag context-tag)))
|
||||||
|
|
||||||
(defun gortium/convert-to-node (node-dirs)
|
(defun gortium/convert-to-node (node-dirs)
|
||||||
"Retrofit existing workspace directories into tracked ExoKortex nodes.
|
"Retrofit existing workspace directories into tracked ExoKortex nodes.
|
||||||
@@ -2258,31 +2592,39 @@ fallback to today's daily note. Ensures the daily has an Org-roam ID."
|
|||||||
headline
|
headline
|
||||||
(file-name-nondirectory daily-file)))))
|
(file-name-nondirectory daily-file)))))
|
||||||
|
|
||||||
(require 'calendar)
|
(defun gortium/read-dir-local (var)
|
||||||
|
"Read variable VAR from .dir-locals.el in `default-directory'.
|
||||||
|
Returns nil if file doesn't exist or VAR isn't set."
|
||||||
|
(let ((dir-locals (expand-file-name ".dir-locals.el" default-directory)))
|
||||||
|
(when (file-exists-p dir-locals)
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert-file-contents dir-locals)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(let* ((sexp (ignore-errors (read (current-buffer))))
|
||||||
|
(nil-entry (assq nil sexp)))
|
||||||
|
(when nil-entry
|
||||||
|
(cdr (assq var (cdr nil-entry)))))))))
|
||||||
|
|
||||||
(defun gortium/org-create-datetree-range ()
|
(defun gortium/project-aware-agenda ()
|
||||||
"Génère une structure datetree Org entre deux dates choisies via le calendrier interactif.
|
"Launch org-agenda scoped to the current Projectile project.
|
||||||
Si la date de début est validée vide, utilise la date d'aujourd'hui."
|
Reads `gortium/project-agenda-entry' from .dir-locals.el and merges
|
||||||
|
it into `org-agenda-custom-commands'. Restricts `org-agenda-files'
|
||||||
|
to .org files under the project root."
|
||||||
(interactive)
|
(interactive)
|
||||||
(let* ((start-time (org-read-date nil t nil "Date de début (Entrée pour aujourd'hui) : "))
|
(if (projectile-project-p)
|
||||||
(end-time (org-read-date nil t nil "Date de fin : "))
|
(let* ((proj-root (projectile-project-root))
|
||||||
(start-decoded (decode-time start-time))
|
(default-directory proj-root)
|
||||||
(end-decoded (decode-time end-time))
|
(agenda-entry (gortium/read-dir-local 'gortium/project-agenda-entry))
|
||||||
;; Format attendu par le calendrier Emacs : (mois jour année)
|
(org-agenda-files (directory-files-recursively proj-root "\\.org$"))
|
||||||
(start-date (list (decoded-time-month start-decoded)
|
(org-agenda-custom-commands
|
||||||
(decoded-time-day start-decoded)
|
(append org-agenda-custom-commands
|
||||||
(decoded-time-year start-decoded)))
|
(when agenda-entry (list agenda-entry)))))
|
||||||
(end-date (list (decoded-time-month end-decoded)
|
(org-agenda))
|
||||||
(decoded-time-day end-decoded)
|
(org-agenda)))
|
||||||
(decoded-time-year end-decoded)))
|
|
||||||
(start-abs (calendar-absolute-from-gregorian start-date))
|
(map! :leader
|
||||||
(end-abs (calendar-absolute-from-gregorian end-date)))
|
:desc "Project-aware agenda"
|
||||||
(if (< end-abs start-abs)
|
"o P" #'gortium/project-aware-agenda)
|
||||||
(user-error "Erreur : La date de fin doit être après la date de début ! ")
|
|
||||||
(save-excursion
|
|
||||||
(cl-loop for abs from start-abs to end-abs
|
|
||||||
do (org-datetree-find-date-create (calendar-gregorian-from-absolute abs))))
|
|
||||||
(message "Datetree généré avec succès !"))))
|
|
||||||
|
|
||||||
;; Like Emacs everywhere, but work in hyprland
|
;; Like Emacs everywhere, but work in hyprland
|
||||||
(defun thanos/wtype-text (text)
|
(defun thanos/wtype-text (text)
|
||||||
@@ -2337,3 +2679,33 @@ Si la date de début est validée vide, utilise la date d'aujourd'hui."
|
|||||||
;; Make the frame more temporary-like
|
;; Make the frame more temporary-like
|
||||||
(set-frame-parameter frame 'delete-before-kill-buffer t)
|
(set-frame-parameter frame 'delete-before-kill-buffer t)
|
||||||
(set-window-dedicated-p (selected-window) t))))
|
(set-window-dedicated-p (selected-window) t))))
|
||||||
|
|
||||||
|
;; Remove stale project entries and inject current project's agenda
|
||||||
|
(defvar gortium/project-agenda-commands nil
|
||||||
|
"Cached project agenda commands.")
|
||||||
|
|
||||||
|
(defun gortium/inject-project-agenda ()
|
||||||
|
"Inject the current project's custom agenda commands into
|
||||||
|
`org-agenda-custom-commands', or remove them if no project."
|
||||||
|
(let* ((proj-root (and (projectile-project-p) (projectile-project-root)))
|
||||||
|
(project-commands nil))
|
||||||
|
(when proj-root
|
||||||
|
(let ((default-directory proj-root))
|
||||||
|
(setq project-commands
|
||||||
|
(gortium/read-dir-local 'gortium/project-agenda-entry))))
|
||||||
|
;; Remove stale project entries (key "P")
|
||||||
|
(setq org-agenda-custom-commands
|
||||||
|
(seq-filter (lambda (entry)
|
||||||
|
(not (and (stringp (car entry))
|
||||||
|
(string= "P" (car entry)))))
|
||||||
|
org-agenda-custom-commands))
|
||||||
|
;; Add current project commands
|
||||||
|
(when project-commands
|
||||||
|
(setq org-agenda-custom-commands
|
||||||
|
(append org-agenda-custom-commands project-commands)))))
|
||||||
|
|
||||||
|
;; Hook into project switching
|
||||||
|
(add-hook 'projectile-after-switch-project-hook
|
||||||
|
#'gortium/inject-project-agenda)
|
||||||
|
;; Also inject at startup and on file open in a project
|
||||||
|
(add-hook 'find-file-hook #'gortium/inject-project-agenda)
|
||||||
|
|||||||
Reference in New Issue
Block a user