#+TITLE: Doom Emacs Configuration
#+AUTHOR: Amr Gharbeia

* early-init.el
:PROPERTIES:
:header-args: :tangle ~/.config/emacs/early-init.el
:END:

#+begin_src emacs-lisp
  (setq package-enable-at-startup nil)

  ;;Run Emacs as a server
  (require 'server)
  (unless (server-running-p) (server-start))
  (defvar server-max-buffers 100)
#+end_src

* init.el
:PROPERTIES:
:header-args: :tangle ~/.config/emacs/init.el
:END:

#+begin_src emacs-lisp
  (doom!
  :input
  ;; Disable some input methods as I do not use them
  -chinese
  -cyrillic
  -japanese
  -korean
  -latin
  -multilang
  -persian
  -thai

  :completion
  company
  ;;helm
  ivy

  :ui
  doom
  ;;doom-dashboard
  ;;doom-quit
  ;;(emoji +unicode)
  hl-todo
  hydra
  ;; indent-guides
  ;; ligatures
  menu
  minimap
  modeline
  nav-flash
  neotree
  ophints
  (popup +defaults)
  tabs
  treemacs
  workspaces
  zen

  :editor
  ;; (evil +everywhere)
  file-templates
  fold
  (format +onsave)
  ;;god
  ;;lispy
  multiple-cursors
  ;;objed
  snippets
  ;;word-wrap

  :emacs
  dired
  electric
  ibuffer
  undo
  vc

  :term
  ;;eshell
  shell
  ;;term
  ;;vterm

  :checkers
  syntax
  ;;spell

  :tools
  ;;ansi-colors
  ;;debugger
  ;;direnv
  docker
  editorconfig
  ;;ein
  ;;eval
  lookup
  lsp
  magit
  make
  pass
  rgb
  taskrunner
  ;;terraform
  tmux
  ;;tree-sitter

  :os
  (:if IS-MAC macos)
  ;;tty
  ;;unicode

  :lang
  ;;agda
  ;;assembly
  ;;cc
  ;;clojure
  ;;common-lisp
  ;;coq
  ;;crystal
  ;;csharp
  ;;data
  ;;dart
  ;;elixir
  ;;elm
  emacs-lisp
  ;;erlang
  ;;ess
  ;;fsharp
  ;;fennel
  ;;fontend
  ;;gleam
  ;;go
  ;;graphql
  ;;haskell
  ;;hy
  ;;idris
  ;;java
  ;;javascript
  ;;julia
  ;;kotlin
  ;;latex
  ;;lean
  ;;lua
  ;;markdown
  ;;nim
  ;;nix
  ;;ocaml
  ;;purescript
  ;;plantuml
  ;;powershell
  ;;prolog
  ;;python
  ;;racket
  ;;raku
  ;;ruby
  ;;rust
  ;;scala
  ;;scheme
  ;;sh
  ;;sql
  ;;swift
  ;;terraform
  ;;web

  :email
  ;;(mu4e +org)
  ;;notmuch
  ;;smtp

  :app
  calendar
  ;;chat
  ;;irc
  ;;rss

  :config
  ;;default
  (org +roam2 +capture)
  ;;pdf
  ;;email
  ;;snippets
  ;; +smartparens
  +private/my-config
  secrets
)
#+end_src

* packages.el
:PROPERTIES:
:header-args: :tangle ~/.config/emacs/packages.el
:END:

#+begin_src emacs-lisp
  (package! flycheck)
  (package! org-modern)
  (package! org-super-agenda)
  (package! org-gtd)
  (package! org-roam)
  (package! sqlite3)
  (package! nov)
  (package! pass)
  (package! auth-source)
  (package! beancount
    :recipe (:host github :repo "beancount/beancount-mode"))
  (package! calibredb)
  (package! docker)
  (package! chemtable)
  (package! bash-completion)
  (package! org-noter)
  (package! org-noter-pdftools)
  (package! ellama)
#+end_src

* config.el
:PROPERTIES:
:header-args: :tangle ~/.config/emacs/config.el
:END:

#+begin_src emacs-lisp
;;; Package --- Summary
;;; Commentary:
;;; Code:
;; -*- lexical-binding: t; -*-

;; Front Matter

(defvar custom-file (expand-file-name "custom.el" user-emacs-directory))
(when (file-exists-p custom-file) (load custom-file))

(defvar inhibit-startup-message t)

(savehist-mode)

(subword-mode)

(defvar sentence-end-double-space nil)

(add-hook 'server-done-hook (lambda () (delete-frame)))
(desktop-save-mode t)

(use-package! flycheck
  :init (global-flycheck-mode)
  :diminish (flycheck-mode . "")
  :config
  (add-hook 'after-init-hook #'global-flycheck-mode)
  (setq flycheck-emacs-lisp-load-path 'inherit)
  (setq flycheck-emacs-lisp-load-path (concat user-emacs-directory "straight/build")))

(use-package! org
  :config
  (defvar org-outline-path-complete-in-steps nil)
  (defvar org-pretty-entities t) ; Improve org mode looks
  (defvar org-hide-emphasis-markers t) ; Hide emphasis markup
  (defvar org-num-mode nil)
  (defvar org-startup-folded 'shw2levels)
  (defvar org-pretty-entities t) ; Improve org mode looks
  (defvar org-hide-emphasis-markers t) ; Hide emphasis markup
  (defvar org-num-mode nil)
  (defvar org-list-demote-modify-bullet t)
  (defvar org-src-fontify-natively t)
  (defvar org-src-tab-acts-natively t)
  (defvar org-startup-with-inline-images t)
  (defvar org-image-actual-width '300)
  (defvar org-deadline-warning-days 7)
  (defvar org-agenda-skip-additional-timestamps-same-entry t)
  (defvar org-agenda-span 'fortnight)
  (defvar org-todo-keywords '(
                              (sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!)")
                              (sequence "WAIT(w@/!)" "|" "CNCL(c@)")
                              ))
  (defvar org-todo-keyword-faces '(
                                   ("TODO" :foreground "red" :weight bold)
                                   ("NEXT" :foreground "red" :weight bold)
                                   ("WAIT" :foreground "yellow" :weight bold)
                                   ("DONE" :foreground "green" :weight bold)
                                   ("CNCL" :foreground "blue" :weight bold)
                                   ))
  (defvar org-enforce-todo-dependencies t)
  (defvar org-tags-exclude-from-inheritance '(
                                              "crypt"
                                              "!private"
                                              ))
  (defvar org-log-into-drawer "LOGBOOK")
  (defvar org-clock-into-drawer t)
  (defvar org-habit-graph-column 80)
  (defvar org-habit-show-habits-only-for-today nil)
  (defvar org-refile-targets '((nil :maxlevel . 9)
                               (org-agenda-files :maxlevel . 9)))
  (defvar org-outline-path-complete-in-steps nil)
  (defvar org-refile-allow-creating-parent-nodes 'confirm)
  (defvar org-babel-do-load-languages 'org-babel-load-languages '((shell . t)))

  :bind (("C-c l" . org-store-link)
         ("C-c a" . org-agenda)
         ("C-c c" . org-capture)
         :map org-mode-map))

(defun org-summary-todo (n-done n-not-done)
  "Switch entry to 'DONE' when all subentries are done, to 'TODO' otherwise.
  Uses N-DONE and N-NOT-DONE"
  (let (org-log-done org-log-states)   ; turn off logging
    (org-todo (if (= n-not-done 0) "DONE" "TODO")
              )
    )
  )

(add-hook 'org-after-todo-statistics-hook #'org-summary-todo)

(use-package! org-modern
  :config
  ;; Choose some fonts
  (set-face-attribute 'default nil :family "sans-serif")
  (set-face-attribute 'variable-pitch nil :family "sans-serif")
  (set-face-attribute 'org-modern-symbol nil :family "Iosevka")
  ;; Edit settings
  (defvar org-auto-align-tags nil)
  (defvar org-tags-column 0)
  (defvar org-catch-invisible-edits 'show-and-error)
  (defvar org-special-ctrl-a/e t)
  (defvar org-insert-heading-respect-content t)
  ;; Org styling, hide markup etc.
  (defvar org-hide-emphasis-markers t)
  (defvar org-pretty-entities t)
  ;; Agenda styling
  (defvar org-agenda-tags-column 0)
  (defvar org-agenda-block-separator ?─)
  (defvar org-agenda-time-grid
    '((daily today require-timed)
      (800 1000 1200 1400 1600 1800 2000)
      " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄"))
  (defvar org-agenda-current-time-string
    "◀── now ─────────────────────────────────────────────────")
  ;; Ellipsis styling
  (defvar org-ellipsis "…")
  (set-face-attribute 'org-ellipsis nil :inherit 'default :box nil)
  (global-org-modern-mode))

(use-package! org-super-agenda)

(use-package! org-gtd
  :after org
  :config
  ;; Keeping these two settings on instead of enabling (org-gtd-mode) until this issue is resolved https://github.com/Trevoke/org-gtd.el/issues/198
  (setq org-edna-use-inheritance t)
  (org-edna-mode)
  ;;    (org-gtd-mode)
  :bind (
         ("C-c d c" . org-gtd-capture)
         ("C-c d e" . org-gtd-engage)
         ("C-c d p" . org-gtd-process-inbox)
         :map org-gtd-clarify-map
         ("C-c c" . org-gtd-organize)))

(use-package! org-roam
  :after org
  :config
  (org-roam-db-autosync-enable)
  (require 'org-roam-dailies)
  :bind  (
          ("C-c n f" . org-roam-node-find)
          ("C-c n g" . org-roam-graph)
          ("C-c n r" . org-roam-node-random)
          ("C-c n h" . org-roam-node-convert-headline)
          ("C-c n i" . org-roam-node-insert)
          ("C-c n o" . org-id-get-create)
          ("C-c n t" . org-roam-tag-add)
          ("C-c n a" . org-roam-alias-add)
          ("C-c n l" . org-roam-buffer-display-dedicated)
          ))

(defun my/org-convert-orgzly-to-org-protocol ()
  "Reformat Orgzly bookmark at point to org-protocol bookmark."
  (interactive)
  (when (org-at-heading-p)
    (let ((headline (nth 4 (org-heading-components))))
      ;; Find and store the link. Delete the link line.
      (search-forward-regexp "^https?://\\S-*" nil t)
      (let ((link (match-string 0)))
        (beginning-of-line)
        (kill-line)
        ;; Delete any trailing blank spaces
        (org-back-to-heading)
        (end-of-line)
        (when (not (org-on-heading-p))
          (delete-char 1)
          )
        ;; Set new headline
        (goto-char (org-entry-beginning-position))
        (org-edit-headline (format "[[%s][%s]]" link headline))
        ;; Set new properties
        (org-set-property "TITLE" headline)
        (org-set-property "URI" link)
        (message "Reformatted Orgzly bookmark at point to org-protocol bookmark")
        )
      )
    )
  )


(use-package! calibredb
  :config
  (setq calibredb-format-all-the-icons t)
  (setq calibredb-format-icons-in-terminal t)
  ;; Forcefully reset the variable after loading calibredb
  (defvar calibredb-root-dir (concat (getenv "HOME") "/library/books"))
  (defvar calibredb-db-dir (expand-file-name "metadata.db" calibredb-root-dir))
                                        ; (defvar calibredb-library-alist (concat (getenv "HOME") "/library/books"))
  ;;  (defvar calibredb-search-page-max-rows 1000)
  (defvar calibredb-id-width 6)
  (defvar calibredb-title-width 100)
  (defvar calibredb-format-width 0)
  (defvar calibredb-date-width 0)
  (defvar calibredb-author-width 20)
  (defvar calibredb-comment-width 0)
  (defvar calibredb-tag-width 0)

  (defvar calibredb-show-mode-map
    (let ((map (make-sparse-keymap)))
      (define-key map "?" #'calibredb-entry-dispatch)
      (define-key map "o" #'calibredb-find-file)
      (define-key map "O" #'calibredb-find-file-other-frame)
      (define-key map "V" #'calibredb-open-file-with-default-tool)
      (define-key map "s" #'calibredb-set-metadata-dispatch)
      (define-key map "e" #'calibredb-export-dispatch)
      (define-key map "q" #'calibredb-entry-quit)
      (define-key map "y" #'calibredb-yank-dispatch)
      (define-key map "," #'calibredb-quick-look)
      (define-key map "." #'calibredb-dired-open)
      (define-key map "\M-/" #'calibredb-rga)
      (define-key map "\M-t" #'calibredb-set-metadata--tags)
      (define-key map "\M-a" #'calibredb-set-metadata--author_sort)
      (define-key map "\M-A" #'calibredb-set-metadata--authors)
      (define-key map "\M-T" #'calibredb-set-metadata--title)
      (define-key map "\M-c" #'calibredb-set-metadata--comments)
      map)
    "Keymap for `calibredb-show-mode'.")

  (defvar calibredb-search-mode-map
    (let ((map (make-sparse-keymap)))
      (define-key map [mouse-3] #'calibredb-search-mouse)
      (define-key map (kbd "<RET>") #'calibredb-find-file)
      (define-key map "?" #'calibredb-dispatch)
      (define-key map "a" #'calibredb-add)
      (define-key map "A" #'calibredb-add-dir)
      (define-key map "c" #'calibredb-clone)
      (define-key map "d" #'calibredb-remove)
      (define-key map "D" #'calibredb-remove-marked-items)
      (define-key map "j" #'calibredb-next-entry)
      (define-key map "k" #'calibredb-previous-entry)
      (define-key map "l" #'calibredb-virtual-library-list)
      (define-key map "L" #'calibredb-library-list)
      (define-key map "n" #'calibredb-virtual-library-next)
      (define-key map "N" #'calibredb-library-next)
      (define-key map "p" #'calibredb-virtual-library-previous)
      (define-key map "P" #'calibredb-library-previous)
      (define-key map "s" #'calibredb-set-metadata-dispatch)
      (define-key map "S" #'calibredb-switch-library)
      (define-key map "o" #'calibredb-find-file)
      (define-key map "O" #'calibredb-find-file-other-frame)
      (define-key map "v" #'calibredb-view)
      (define-key map "V" #'calibredb-open-file-with-default-tool)
      (define-key map "," #'calibredb-quick-look)
      (define-key map "." #'calibredb-dired-open)
      (define-key map "y" #'calibredb-yank-dispatch)
      (define-key map "b" #'calibredb-catalog-bib-dispatch)
      (define-key map "e" #'calibredb-export-dispatch)
      (define-key map "r" #'calibredb-search-refresh-and-clear-filter)
      (define-key map "R" #'calibredb-search-clear-filter)
      (define-key map "q" #'calibredb-search-quit)
      (define-key map "m" #'calibredb-mark-and-forward)
      (define-key map "f" #'calibredb-toggle-favorite-at-point)
      (define-key map "x" #'calibredb-toggle-archive-at-point)
      (define-key map "h" #'calibredb-toggle-highlight-at-point)
      (define-key map "u" #'calibredb-unmark-and-forward)
      (define-key map "i" #'calibredb-edit-annotation)
      (define-key map (kbd "<DEL>") #'calibredb-unmark-and-backward)
      (define-key map (kbd "<backtab>") #'calibredb-toggle-view)
      (define-key map (kbd "TAB") #'calibredb-toggle-view-at-point)
      (define-key map "\M-n" #'calibredb-show-next-entry)
      (define-key map "\M-p" #'calibredb-show-previous-entry)
      (define-key map "/" #'calibredb-search-live-filter)
      (define-key map "\M-t" #'calibredb-set-metadata--tags)
      (define-key map "\M-a" #'calibredb-set-metadata--author_sort)
      (define-key map "\M-A" #'calibredb-set-metadata--authors)
      (define-key map "\M-T" #'calibredb-set-metadata--title)
      (define-key map "\M-c" #'calibredb-set-metadata--comments)
      map)
    "Keymap for `calibredb-search-mode'.")
  )

(use-package! org-noter)
(use-package! org-noter-pdftools
  :after org-noter
  :config
  ;; Add a function to ensure precise note is inserted
  (defun org-noter-pdftools-insert-precise-note (&optional toggle-no-questions)
    (interactive "P")
    (org-noter--with-valid-session
     (let ((org-noter-insert-note-no-questions (if toggle-no-questions
                                                   (not org-noter-insert-note-no-questions)
                                                 org-noter-insert-note-no-questions))
           (org-pdftools-use-isearch-link t)
           (org-pdftools-use-freepointer-annot t))
       (org-noter-insert-note (org-noter--get-precise-info)))))
  ;; fix https://github.com/weirdNox/org-noter/pull/93/commits/f8349ae7575e599f375de1be6be2d0d5de4e6cbf
  (defun org-noter-set-start-location (&optional arg)
    "When opening a session with this document, go to the current location.
  With a prefix ARG, remove start location."
    (interactive "P")
    (org-noter--with-valid-session
     (let ((inhibit-read-only t)
           (ast (org-noter--parse-root))
           (location (org-noter--doc-approx-location (when (called-interactively-p 'any) 'interactive))))
       (with-current-buffer (org-noter--session-notes-buffer session)
         (org-with-wide-buffer
          (goto-char (org-element-property :begin ast))
          (if arg
              (org-entry-delete nil org-noter-property-note-location)
            (org-entry-put nil org-noter-property-note-location
                           (org-noter--pretty-print-location location))))))))
  (with-eval-after-load 'pdf-annot
    (add-hook 'pdf-annot-activate-handler-functions #'org-noter-pdftools-jump-to-note)))

(use-package! nov
  :config
  (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode)))

(use-package! pass)

(use-package! auth-source
  :config (auth-source-pass-enable))

(use-package! bash-completion
  :config
  (require 'bash-completion)
  (bash-completion-setup)
  (defvar shell-dynamic-complete-functions t)
  )

(use-package! docker
  :bind ("C-c d" . docker))

(use-package! chemtable)

(use-package! ellama
  :ensure t
  :bind ("C-c e" . ellama)
  ;; send last message in chat buffer with C-c C-c
  :hook (org-ctrl-c-ctrl-c-final . ellama-chat-send-last-message)
  :init (setopt ellama-auto-scroll t)
  :config
  ;; show ellama context in header line in all buffers
  (ellama-context-header-line-global-mode +1)
  ;; show ellama session id in header line in all buffers
  (ellama-session-header-line-global-mode +1))

(use-package! eww
  :config
  (add-hook 'eww-mode-hook 'visual-line-mode))

(use-package! beancount
  :config
  (add-to-list 'auto-mode-alist '("\\.beancount\\'" . beancount-mode))
  (add-hook 'beancount-mode-hook #'outline-minor-mode)
  (define-key beancount-mode-map (kbd "C-c C-n") #'outline-next-visible-heading)
  (define-key beancount-mode-map (kbd "C-c C-p") #'outline-previous-visible-heading)
  (add-hook 'beancount-mode-hook #'flymake-bean-check-enable))

(use-package! flyspell
  :config
  (setq ispell-program-name "hunspell"
        ispell-default-dictionary "en_US"
        )
  :diminish (flyspell-mode . "φ")
  :hook (text-mode . flyspell-mode)
  :bind (
         ("M-<f7>" . flyspell-buffer)
         ("<f7>" . flyspell-word)
         ("C-;" . flyspell-auto-correct-previous-word)
         )
  )

(defun my/fill-or-unfill-paragraph (&optional unfill region)
  "Fill paragraph (or REGION). With the prefix argument UNFILL, fill it instead."
  (interactive (progn
                 (barf-if-buffer-read-only)
                 (list (if current-prefix-arg 'fill) t)))
  (let ((fill-column (if unfill fill-column (point-max))))
    (fill-paragraph nil region)))

(bind-key "M-q" 'my/fill-or-unfill-paragraph)

(defun my/fill-or-unfill-all-paragraphs (&optional unfill)
  "Fill or unfill all paragraphs in the current buffer.
With the prefix argument UNFILL, fill them instead."
  (interactive (list (if current-prefix-arg 'fill)))
  (let ((fill-column (if unfill fill-column (point-max))))
    (save-excursion
      (goto-char (point-min))
      (while (not (eobp))
        (fill-paragraph nil t)
        (forward-paragraph)))))

(bind-key "M-Q" 'my/fill-or-unfill-all-paragraphs)

(remove-hook 'text-mode-hook #'turn-on-auto-fill)
(add-hook 'text-mode-hook 'turn-on-visual-line-mode)
;;; Package --- Summary
;;; Commentary:
;;; Code:
;; -*- lexical-binding: t; -*-

;; Front Matter

(defvar custom-file (expand-file-name "custom.el" user-emacs-directory))
(when (file-exists-p custom-file) (load custom-file))

(defvar inhibit-startup-message t)

(savehist-mode)

(subword-mode)

(defvar sentence-end-double-space nil)

(add-hook 'server-done-hook (lambda () (delete-frame)))
(desktop-save-mode t)

(use-package! flycheck
  :init (global-flycheck-mode)
  :diminish (flycheck-mode . "")
  :config
  (add-hook 'after-init-hook #'global-flycheck-mode)
  (setq flycheck-emacs-lisp-load-path 'inherit)
  (setq flycheck-emacs-lisp-load-path (concat user-emacs-directory "straight/build")))

(use-package! org
  :config
  (defvar org-outline-path-complete-in-steps nil)
  (defvar org-pretty-entities t) ; Improve org mode looks
  (defvar org-hide-emphasis-markers t) ; Hide emphasis markup
  (defvar org-num-mode nil)
  (defvar org-startup-folded 'shw2levels)
  (defvar org-pretty-entities t) ; Improve org mode looks
  (defvar org-hide-emphasis-markers t) ; Hide emphasis markup
  (defvar org-num-mode nil)
  (defvar org-list-demote-modify-bullet t)
  (defvar org-src-fontify-natively t)
  (defvar org-src-tab-acts-natively t)
  (defvar org-startup-with-inline-images t)
  (defvar org-image-actual-width '300)
  (defvar org-deadline-warning-days 7)
  (defvar org-agenda-skip-additional-timestamps-same-entry t)
  (defvar org-agenda-span 'fortnight)
  (defvar org-todo-keywords '(
                              (sequence "TODO(t)" "NEXT(n)" "|" "DONE(d!)")
                              (sequence "WAIT(w@/!)" "|" "CNCL(c@)")
                              ))
  (defvar org-todo-keyword-faces '(
                                   ("TODO" :foreground "red" :weight bold)
                                   ("NEXT" :foreground "red" :weight bold)
                                   ("WAIT" :foreground "yellow" :weight bold)
                                   ("DONE" :foreground "green" :weight bold)
                                   ("CNCL" :foreground "blue" :weight bold)
                                   ))
  (defvar org-enforce-todo-dependencies t)
  (defvar org-tags-exclude-from-inheritance '(
                                              "crypt"
                                              "!private"
                                              ))
  (defvar org-log-into-drawer "LOGBOOK")
  (defvar org-clock-into-drawer t)
  (defvar org-habit-graph-column 80)
  (defvar org-habit-show-habits-only-for-today nil)
  (defvar org-refile-targets '((nil :maxlevel . 9)
                               (org-agenda-files :maxlevel . 9)))
  (defvar org-outline-path-complete-in-steps nil)
  (defvar org-refile-allow-creating-parent-nodes 'confirm)
  (defvar org-babel-do-load-languages 'org-babel-load-languages '((shell . t)))

  :bind (("C-c l" . org-store-link)
         ("C-c a" . org-agenda)
         ("C-c c" . org-capture)
         :map org-mode-map))

(defun org-summary-todo (n-done n-not-done)
  "Switch entry to 'DONE' when all subentries are done, to 'TODO' otherwise.
  Uses N-DONE and N-NOT-DONE"
  (let (org-log-done org-log-states)   ; turn off logging
    (org-todo (if (= n-not-done 0) "DONE" "TODO")
              )
    )
  )

(add-hook 'org-after-todo-statistics-hook #'org-summary-todo)

(use-package! org-modern
  :config
  ;; Choose some fonts
  (set-face-attribute 'default nil :family "sans-serif")
  (set-face-attribute 'variable-pitch nil :family "sans-serif")
  (set-face-attribute 'org-modern-symbol nil :family "Iosevka")
  ;; Edit settings
  (defvar org-auto-align-tags nil)
  (defvar org-tags-column 0)
  (defvar org-catch-invisible-edits 'show-and-error)
  (defvar org-special-ctrl-a/e t)
  (defvar org-insert-heading-respect-content t)
  ;; Org styling, hide markup etc.
  (defvar org-hide-emphasis-markers t)
  (defvar org-pretty-entities t)
  ;; Agenda styling
  (defvar org-agenda-tags-column 0)
  (defvar org-agenda-block-separator ?─)
  (defvar org-agenda-time-grid
    '((daily today require-timed)
      (800 1000 1200 1400 1600 1800 2000)
      " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄"))
  (defvar org-agenda-current-time-string
    "◀── now ─────────────────────────────────────────────────")
  ;; Ellipsis styling
  (defvar org-ellipsis "…")
  (set-face-attribute 'org-ellipsis nil :inherit 'default :box nil)
  (global-org-modern-mode))

(use-package! org-super-agenda)

(use-package! org-gtd
  :after org
  :config
  ;; Keeping these two settings on instead of enabling (org-gtd-mode) until this issue is resolved https://github.com/Trevoke/org-gtd.el/issues/198
  (setq org-edna-use-inheritance t)
  (org-edna-mode)
  ;;    (org-gtd-mode)
  :bind (
         ("C-c d c" . org-gtd-capture)
         ("C-c d e" . org-gtd-engage)
         ("C-c d p" . org-gtd-process-inbox)
         :map org-gtd-clarify-map
         ("C-c c" . org-gtd-organize)))

(use-package! org-roam
  :after org
  :config
  (org-roam-db-autosync-enable)
  (require 'org-roam-dailies)
  :bind  (
          ("C-c n f" . org-roam-node-find)
          ("C-c n g" . org-roam-graph)
          ("C-c n r" . org-roam-node-random)
          ("C-c n h" . org-roam-node-convert-headline)
          ("C-c n i" . org-roam-node-insert)
          ("C-c n o" . org-id-get-create)
          ("C-c n t" . org-roam-tag-add)
          ("C-c n a" . org-roam-alias-add)
          ("C-c n l" . org-roam-buffer-display-dedicated)
          ))

(defun my/org-convert-orgzly-to-org-protocol ()
  "Reformat Orgzly bookmark at point to org-protocol bookmark."
  (interactive)
  (when (org-at-heading-p)
    (let ((headline (nth 4 (org-heading-components))))
      ;; Find and store the link. Delete the link line.
      (search-forward-regexp "^https?://\\S-*" nil t)
      (let ((link (match-string 0)))
        (beginning-of-line)
        (kill-line)
        ;; Delete any trailing blank spaces
        (org-back-to-heading)
        (end-of-line)
        (when (not (org-on-heading-p))
          (delete-char 1)
          )
        ;; Set new headline
        (goto-char (org-entry-beginning-position))
        (org-edit-headline (format "[[%s][%s]]" link headline))
        ;; Set new properties
        (org-set-property "TITLE" headline)
        (org-set-property "URI" link)
        (message "Reformatted Orgzly bookmark at point to org-protocol bookmark")
        )
      )
    )
  )


(use-package! calibredb
  :config
  (setq calibredb-format-all-the-icons t)
  (setq calibredb-format-icons-in-terminal t)
  ;; Forcefully reset the variable after loading calibredb
  (defvar calibredb-root-dir (concat (getenv "HOME") "/library/books"))
  (defvar calibredb-db-dir (expand-file-name "metadata.db" calibredb-root-dir))
                                        ; (defvar calibredb-library-alist (concat (getenv "HOME") "/library/books"))
  ;;  (defvar calibredb-search-page-max-rows 1000)
  (defvar calibredb-id-width 6)
  (defvar calibredb-title-width 100)
  (defvar calibredb-format-width 0)
  (defvar calibredb-date-width 0)
  (defvar calibredb-author-width 20)
  (defvar calibredb-comment-width 0)
  (defvar calibredb-tag-width 0)

  (defvar calibredb-show-mode-map
    (let ((map (make-sparse-keymap)))
      (define-key map "?" #'calibredb-entry-dispatch)
      (define-key map "o" #'calibredb-find-file)
      (define-key map "O" #'calibredb-find-file-other-frame)
      (define-key map "V" #'calibredb-open-file-with-default-tool)
      (define-key map "s" #'calibredb-set-metadata-dispatch)
      (define-key map "e" #'calibredb-export-dispatch)
      (define-key map "q" #'calibredb-entry-quit)
      (define-key map "y" #'calibredb-yank-dispatch)
      (define-key map "," #'calibredb-quick-look)
      (define-key map "." #'calibredb-dired-open)
      (define-key map "\M-/" #'calibredb-rga)
      (define-key map "\M-t" #'calibredb-set-metadata--tags)
      (define-key map "\M-a" #'calibredb-set-metadata--author_sort)
      (define-key map "\M-A" #'calibredb-set-metadata--authors)
      (define-key map "\M-T" #'calibredb-set-metadata--title)
      (define-key map "\M-c" #'calibredb-set-metadata--comments)
      map)
    "Keymap for `calibredb-show-mode'.")

  (defvar calibredb-search-mode-map
    (let ((map (make-sparse-keymap)))
      (define-key map [mouse-3] #'calibredb-search-mouse)
      (define-key map (kbd "<RET>") #'calibredb-find-file)
      (define-key map "?" #'calibredb-dispatch)
      (define-key map "a" #'calibredb-add)
      (define-key map "A" #'calibredb-add-dir)
      (define-key map "c" #'calibredb-clone)
      (define-key map "d" #'calibredb-remove)
      (define-key map "D" #'calibredb-remove-marked-items)
      (define-key map "j" #'calibredb-next-entry)
      (define-key map "k" #'calibredb-previous-entry)
      (define-key map "l" #'calibredb-virtual-library-list)
      (define-key map "L" #'calibredb-library-list)
      (define-key map "n" #'calibredb-virtual-library-next)
      (define-key map "N" #'calibredb-library-next)
      (define-key map "p" #'calibredb-virtual-library-previous)
      (define-key map "P" #'calibredb-library-previous)
      (define-key map "s" #'calibredb-set-metadata-dispatch)
      (define-key map "S" #'calibredb-switch-library)
      (define-key map "o" #'calibredb-find-file)
      (define-key map "O" #'calibredb-find-file-other-frame)
      (define-key map "v" #'calibredb-view)
      (define-key map "V" #'calibredb-open-file-with-default-tool)
      (define-key map "," #'calibredb-quick-look)
      (define-key map "." #'calibredb-dired-open)
      (define-key map "y" #'calibredb-yank-dispatch)
      (define-key map "b" #'calibredb-catalog-bib-dispatch)
      (define-key map "e" #'calibredb-export-dispatch)
      (define-key map "r" #'calibredb-search-refresh-and-clear-filter)
      (define-key map "R" #'calibredb-search-clear-filter)
      (define-key map "q" #'calibredb-search-quit)
      (define-key map "m" #'calibredb-mark-and-forward)
      (define-key map "f" #'calibredb-toggle-favorite-at-point)
      (define-key map "x" #'calibredb-toggle-archive-at-point)
      (define-key map "h" #'calibredb-toggle-highlight-at-point)
      (define-key map "u" #'calibredb-unmark-and-forward)
      (define-key map "i" #'calibredb-edit-annotation)
      (define-key map (kbd "<DEL>") #'calibredb-unmark-and-backward)
      (define-key map (kbd "<backtab>") #'calibredb-toggle-view)
      (define-key map (kbd "TAB") #'calibredb-toggle-view-at-point)
      (define-key map "\M-n" #'calibredb-show-next-entry)
      (define-key map "\M-p" #'calibredb-show-previous-entry)
      (define-key map "/" #'calibredb-search-live-filter)
      (define-key map "\M-t" #'calibredb-set-metadata--tags)
      (define-key map "\M-a" #'calibredb-set-metadata--author_sort)
      (define-key map "\M-A" #'calibredb-set-metadata--authors)
      (define-key map "\M-T" #'calibredb-set-metadata--title)
      (define-key map "\M-c" #'calibredb-set-metadata--comments)
      map)
    "Keymap for `calibredb-search-mode'.")
  )

(use-package! org-noter)
(use-package! org-noter-pdftools
  :after org-noter
  :config
  ;; Add a function to ensure precise note is inserted
  (defun org-noter-pdftools-insert-precise-note (&optional toggle-no-questions)
    (interactive "P")
    (org-noter--with-valid-session
     (let ((org-noter-insert-note-no-questions (if toggle-no-questions
                                                   (not org-noter-insert-note-no-questions)
                                                 org-noter-insert-note-no-questions))
           (org-pdftools-use-isearch-link t)
           (org-pdftools-use-freepointer-annot t))
       (org-noter-insert-note (org-noter--get-precise-info)))))
  ;; fix https://github.com/weirdNox/org-noter/pull/93/commits/f8349ae7575e599f375de1be6be2d0d5de4e6cbf
  (defun org-noter-set-start-location (&optional arg)
    "When opening a session with this document, go to the current location.
  With a prefix ARG, remove start location."
    (interactive "P")
    (org-noter--with-valid-session
     (let ((inhibit-read-only t)
           (ast (org-noter--parse-root))
           (location (org-noter--doc-approx-location (when (called-interactively-p 'any) 'interactive))))
       (with-current-buffer (org-noter--session-notes-buffer session)
         (org-with-wide-buffer
          (goto-char (org-element-property :begin ast))
          (if arg
              (org-entry-delete nil org-noter-property-note-location)
            (org-entry-put nil org-noter-property-note-location
                           (org-noter--pretty-print-location location))))))))
  (with-eval-after-load 'pdf-annot
    (add-hook 'pdf-annot-activate-handler-functions #'org-noter-pdftools-jump-to-note)))

(use-package! nov
  :config
  (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode)))

(use-package! pass)

(use-package! auth-source
  :config (auth-source-pass-enable))

(use-package! bash-completion
  :config
  (require 'bash-completion)
  (bash-completion-setup)
  (defvar shell-dynamic-complete-functions t)
  )

(use-package! docker
  :bind ("C-c d" . docker))

(use-package! chemtable)

(use-package! ellama
  :ensure t
  :bind ("C-c e" . ellama)
  ;; send last message in chat buffer with C-c C-c
  :hook (org-ctrl-c-ctrl-c-final . ellama-chat-send-last-message)
  :init (setopt ellama-auto-scroll t)
  :config
  ;; show ellama context in header line in all buffers
  (ellama-context-header-line-global-mode +1)
  ;; show ellama session id in header line in all buffers
  (ellama-session-header-line-global-mode +1))

(use-package! eww
  :config
  (add-hook 'eww-mode-hook 'visual-line-mode))

(use-package! beancount
  :config
  (add-to-list 'auto-mode-alist '("\\.beancount\\'" . beancount-mode))
  (add-hook 'beancount-mode-hook #'outline-minor-mode)
  (define-key beancount-mode-map (kbd "C-c C-n") #'outline-next-visible-heading)
  (define-key beancount-mode-map (kbd "C-c C-p") #'outline-previous-visible-heading)
  (add-hook 'beancount-mode-hook #'flymake-bean-check-enable))

(use-package! flyspell
  :config
  (setq ispell-program-name "hunspell"
        ispell-default-dictionary "en_US"
        )
  :diminish (flyspell-mode . "φ")
  :hook (text-mode . flyspell-mode)
  :bind (
         ("M-<f7>" . flyspell-buffer)
         ("<f7>" . flyspell-word)
         ("C-;" . flyspell-auto-correct-previous-word)
         )
  )

(defun my/fill-or-unfill-paragraph (&optional unfill region)
  "Fill paragraph (or REGION). With the prefix argument UNFILL, fill it instead."
  (interactive (progn
                 (barf-if-buffer-read-only)
                 (list (if current-prefix-arg 'fill) t)))
  (let ((fill-column (if unfill fill-column (point-max))))
    (fill-paragraph nil region)))

(bind-key "M-q" 'my/fill-or-unfill-paragraph)

(defun my/fill-or-unfill-all-paragraphs (&optional unfill)
  "Fill or unfill all paragraphs in the current buffer.
With the prefix argument UNFILL, fill them instead."
  (interactive (list (if current-prefix-arg 'fill)))
  (let ((fill-column (if unfill fill-column (point-max))))
    (save-excursion
      (goto-char (point-min))
      (while (not (eobp))
        (fill-paragraph nil t)
        (forward-paragraph)))))

(bind-key "M-Q" 'my/fill-or-unfill-all-paragraphs)

(remove-hook 'text-mode-hook #'turn-on-auto-fill)
(add-hook 'text-mode-hook 'turn-on-visual-line-mode)
#+end_src

* custom.el
:PROPERTIES:
:header-args: :tangle ~/.config/emacs/custom.el
:END:

#+begin_src emacs-lisp
;; To enable an installed theme, just copy the relevant lines below into your
;; ~/.doom.d/config.el file.

(custom-set-variables
 '(user-full-name "Amr Gharbeia")
 '(email-address "amr@gharbeia.net")
 '(calendar-location-name "Washington, DC")
 '(calendar-latitude 39.0)
 '(calendar-time-zone -300)
 '(calendar-longitude -77.1)
 '(calendar-standard-time-zone-name "EST")
 '(calendar-daylight-time-zone-name "EDT")

 '(my-laptop-p (equal (system-name) "lilitop"))
 '(my-server-p (and (equal (system-name) "localhost") (equal user-login-name "root")))
 '(my-phone-p (not (null (getenv "ANDROID_ROOT"))) "If non-nil, GNU Emacs is running on Termux.")
 '(when my-phone-p (defvar gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3"))
 '(global-auto-revert-mode)  ; simplifies syncing

 '(org-directory (concat (getenv "HOME") "/org/"))
 '(org-default-notes-file (concat org-directory "/inbox.org"))
 '(org-attach-id-dir (concat org-directory "/library"))
 '(org-noter-notes-search-path (list (concat org-directory "/library/books")))
 '(org-noter-default-notes-file-names '("books.org"))
 '(org-agenda-files (list
                     (concat org-directory "/inbox.org")
                     (concat org-directory "/org-gtd-tasks.org")
                     ))
 '(org-gtd-directory org-directory)
 '(org-gtd-organize-hooks '(org-gtd-set-area-of-focus
                            org-set-tags-command))
 '(org-gtd-areas-of-focus '("Atoms"
                            "Bits"
                            "Cells"
                            "Flags"
                            "Business"
                            "Wealth"
                            "Learning"
                            "Skills"
                            "Privacy"
                            "Archive"
                            "Library"
                            "Writing"
                            "Health"
                            "Home"
                            "Family"
                            "Social"
                            "Egypt"
                            ))
 '(org-gtd-clarify-show-horizons 'right)
 )

'(org-capture-templates
  '(("p" "Protocol" entry (file "inbox.org") "* %^{Title}\nSource: %u, %c\n #+BEGIN_QUOTE\n%i\n#+END_QUOTE\n\n\n%?")
    ("L" "Protocol Link" entry (file "inbox.org") "* %? [[%:link][%:description]]\n:PROPERTIES:\n:TITLE: %:description\n:URI: %:link\n:CREATED: %U\n:END:" :prepend nil :empty-lines 1 :created t :kill-buffer t)))

'(org-protocol-default-template-key "L")

'(org-roam-capture-templates
  '(("L" "link" plain (function org-roam--capture-get-point) "%?" :file-name "web/%<%Y-%m-%dT%H%M%S>.org" :head "#+TITLE: ${title}\n#+CREATED: %<%Y-%m-%dT%H%M%S>" :immediate-finish t :unnarrowed t)
    ("h" "hugo post" plain "%?" :target (file+head "posts/${slug}.org" "#+TITLE: ${title}\n#+DATE: %U\n#+HUGO_BASE_DIR: ~/gharbeia.net\n#+HUGO_SECTION: ./posts\n#+HUGO_AUTO_SET_LASTMOD: t\n#+HUGO_TAGS: article\n#+HUGO_DRAFT: true\n") :immediate-finish t :unnarrowed t)
    ("p" "person" plain "%?" :if-new (file+head "people/${slug}.org" "#+TITLE: ${title}") :immediate-finish t :unnarrowed t)))

'(org-roam-dailies-capture-templates
  '(("d" "daily" plain "" :target ("file+heaed %<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n\n") :immediate-finish t)))


(custom-set-faces
 '(default ((t (:inherit default :height 120))))
 '(org-level-1 ((t (:height 1.5))))
 '(org-level-2 ((t (:height 1.3))))
 '(org-level-3 ((t (:height 1.2))))
 '(org-level-4 ((t (:height 1.1))))
 '(org-level-5 ((t (:height 1.05))))
 '(org-level-6 ((t (:height 1.0))))
 '(org-link ((t (:underline t))))
 '(org-todo ((t (:foreground "red" :weight bold)))))


'(ellama-providers
  '((("ollama" . (make-llm-ollama :chat-model "gemma3:latest" :embedding-model "gemma3:latest" :default-chat-non-standard-params '(("num_ctx" . 8192))))) . nil)
  (nil . nil)
  (nil . nil)
  (("openai" . (make-llm-openai :key (auth-source-pass-get "api-key" "www/openai.com/amr@gharbeia.net") :chat-model "gpt-4o" :embedding-model "text-embedding-3-large")) . nil)
  (nil . nil)
  (("google" . (make-llm-google :key (auth-source-pass-get "gemini-api-key" "www/google.com/amr.gharbeia") :chat-model "latest" :embedding-model "text-embedding-004")) . nil)
  (("groq" . (make-llm-openai-compatible :url "https://api.groq.com/openai/v1" :key (auth-source-pass-get "api-key" "www/console.groq.com/groq@amr.gharbeia.net") :chat-model "llama3-70b-8192" :embedding-model "llama3-70b-8192")) . nil))

'(ellama-provider "ollama")
#+end_src

* private/config.el
:PROPERTIES:
:header-args: :tangle ~/.config/emacs/private/config.el
:END:
