mirror of
https://github.com/SqrtMinusOne/dotfiles.git
synced 2025-12-10 19:23:03 +03:00
emacs: experiments with review workflow; update some packages
This commit is contained in:
parent
327340a95c
commit
3e4efbae78
3 changed files with 643 additions and 453 deletions
|
|
@ -8,7 +8,7 @@
|
|||
"imagemagick"
|
||||
"font-gnu-freefont"
|
||||
"font-gnu-unifont"
|
||||
"tdlib-1.8.16"
|
||||
"emacs-telega-sever"
|
||||
"yt-dlp"
|
||||
"mpv"
|
||||
"python-youtube-transcript-api"
|
||||
|
|
|
|||
492
.emacs.d/init.el
492
.emacs.d/init.el
|
|
@ -1723,6 +1723,7 @@ targets."
|
|||
(setq lsp-headerline-breadcrumb-enable nil)
|
||||
(setq lsp-modeline-code-actions-enable nil)
|
||||
(setq lsp-modeline-diagnostics-enable nil)
|
||||
(setq lsp-volar-take-over-mode nil)
|
||||
(add-to-list 'lsp-language-id-configuration '(svelte-mode . "svelte")))
|
||||
|
||||
(use-package lsp-ui
|
||||
|
|
@ -2166,7 +2167,7 @@ Returns (<buffer> . <workspace-index>) or nil."
|
|||
(use-package copilot
|
||||
:straight (:host github :repo "copilot-emacs/copilot.el")
|
||||
:commands (copilot-mode)
|
||||
:if (not (or my/remote-server my/is-termux))
|
||||
:disabled t
|
||||
:init
|
||||
(add-hook 'emacs-startup-hook
|
||||
(lambda ()
|
||||
|
|
@ -2310,11 +2311,13 @@ Returns (<buffer> . <workspace-index>) or nil."
|
|||
(add-hook 'web-mode-hook #'my/web-mode-lsp)
|
||||
|
||||
(defun my/web-mode-vue-setup (&rest _)
|
||||
(when (string-match-p (rx ".vue" eos) (buffer-file-name))
|
||||
(setq-local web-mode-script-padding 0)
|
||||
(setq-local web-mode-style-padding 0)
|
||||
(setq-local create-lockfiles nil)
|
||||
(setq-local web-mode-enable-auto-pairing nil)))
|
||||
(let ((filename (buffer-file-name)))
|
||||
(when (and (stringp filename)
|
||||
(string-match-p (rx ".vue" eos) filename))
|
||||
(setq-local web-mode-script-padding 0)
|
||||
(setq-local web-mode-style-padding 0)
|
||||
(setq-local create-lockfiles nil)
|
||||
(setq-local web-mode-enable-auto-pairing nil))))
|
||||
|
||||
(add-hook 'web-mode-hook 'my/web-mode-vue-setup)
|
||||
(add-hook 'editorconfig-after-apply-functions 'my/web-mode-vue-setup)
|
||||
|
|
@ -2783,7 +2786,7 @@ Returns (<buffer> . <workspace-index>) or nil."
|
|||
:config
|
||||
(setq langtool-language-tool-server-jar "/home/pavel/bin/LanguageTool-6.4/languagetool-server.jar")
|
||||
(setq langtool-mother-tongue "ru")
|
||||
(setq langtool-default-language "en-US"))
|
||||
(setq langtool-default-language "ru-RU"))
|
||||
|
||||
(my-leader-def
|
||||
:infix "L"
|
||||
|
|
@ -3557,6 +3560,7 @@ With ARG, repeats or can move backward if negative."
|
|||
`((emacs-lisp . t)
|
||||
(python . t)
|
||||
(sql . t)
|
||||
(sqlite . t)
|
||||
;; (typescript .t)
|
||||
(hy . t)
|
||||
(shell . t)
|
||||
|
|
@ -3946,6 +3950,9 @@ With ARG, repeats or can move backward if negative."
|
|||
(with-eval-after-load 'org
|
||||
(my-leader-def "ol" #'org-clock-agg))
|
||||
:config
|
||||
(setq org-clock-agg-node-format
|
||||
"%-%(+ title-width)t %20c %8z %s/%S")
|
||||
(setq org-clock-agg-node-title-width-delta 47)
|
||||
(push
|
||||
(cons "Agenda+Archive"
|
||||
(append
|
||||
|
|
@ -4063,6 +4070,21 @@ With ARG, repeats or can move backward if negative."
|
|||
:infix "SPC"
|
||||
"C" #'my/org-clock-recent))
|
||||
|
||||
(defun my/org-fix-task-kind ()
|
||||
(interactive)
|
||||
(let ((entries (org-ql-query
|
||||
:select #'element-with-markers
|
||||
:from (current-buffer)
|
||||
:where '(and (olp "Tasks")
|
||||
(not (property "TASK_KIND"))
|
||||
(clocked)))))
|
||||
(org-fold-show-all)
|
||||
(dolist (entry entries)
|
||||
(let ((marker (org-element-property :org-marker entry)))
|
||||
(org-with-point-at marker
|
||||
(let ((value (org-read-property-value "TASK_KIND")))
|
||||
(org-set-property "TASK_KIND" value)))))))
|
||||
|
||||
(use-package org-super-agenda
|
||||
:straight t
|
||||
:after (org)
|
||||
|
|
@ -4150,6 +4172,30 @@ TYPE may be `ts', `ts-active', `ts-inactive', `clocked', or
|
|||
:sort '(priority todo deadline)
|
||||
:super-groups '((:auto-outline-path-file t)))))
|
||||
|
||||
(defun my/org-ql-clocked-today ()
|
||||
(interactive)
|
||||
(let ((today (format-time-string
|
||||
"%Y-%m-%d"
|
||||
(days-to-time
|
||||
(- (org-today) (time-to-days 0))))))
|
||||
(org-ql-search (org-agenda-files) `(clocked :from ,today)
|
||||
:title "Clocked today"
|
||||
:sort '(todo priority date)
|
||||
:super-groups '((:auto-outline-path-file t)
|
||||
(:auto-todo t)))))
|
||||
|
||||
(defun my/org-ql-closed-today ()
|
||||
(interactive)
|
||||
(let ((today (format-time-string
|
||||
"%Y-%m-%d"
|
||||
(days-to-time
|
||||
(- (org-today) (time-to-days 0))))))
|
||||
(org-ql-search (org-agenda-files) `(closed :from ,today)
|
||||
:title "Closed today"
|
||||
:sort '(todo priority date)
|
||||
:super-groups '((:auto-outline-path-file t)
|
||||
(:auto-todo t)))))
|
||||
|
||||
(setq org-ql-views
|
||||
(list
|
||||
(cons "Overview: All TODO" #'my/org-ql-all-todo)
|
||||
|
|
@ -4171,13 +4217,8 @@ TYPE may be `ts', `ts-active', `ts-inactive', `clocked', or
|
|||
:sort '(todo priority date)
|
||||
:super-groups '((:auto-outline-path-file t))))
|
||||
(cons "Review: Recently timestamped" #'my/org-ql-view-recent-items)
|
||||
(cons "Review: Unlinked to meetings"
|
||||
(list :buffers-files #'org-agenda-files
|
||||
:query '(and (todo "DONE" "NO")
|
||||
(not (property "MEETING"))
|
||||
(ts :from -7))
|
||||
:super-groups '((:auto-outline-path-file t))))
|
||||
(cons "Review: Meeting" #'my/org-ql-meeting-tasks)
|
||||
(cons "Review: Clocked today" #'my/org-ql-clocked-today)
|
||||
(cons "Review: Closed today" #'my/org-ql-closed-today)
|
||||
(cons "Fix: tasks without TASK_KIND"
|
||||
(lambda ()
|
||||
(interactive)
|
||||
|
|
@ -4522,7 +4563,7 @@ KEYS is a list of cons cells like (<label> . <time>)."
|
|||
(thread-last
|
||||
heading
|
||||
(substring-no-properties)
|
||||
(replace-regexp-in-string (rx (| "(" "[") (+ alnum) (| "]" ")")) "")
|
||||
(replace-regexp-in-string (rx (| "(" "[") (+ nonl) (| "]" ")")) "")
|
||||
(replace-regexp-in-string (rx " " (+ (or digit "."))) " ")
|
||||
(replace-regexp-in-string (rx (+ " ")) " ")
|
||||
(string-trim)))
|
||||
|
|
@ -4774,6 +4815,11 @@ KEYS is a list of cons cells like (<label> . <time>)."
|
|||
(add-hook 'org-journal-after-entry-create-hook
|
||||
#'my/set-journal-header)
|
||||
|
||||
(defun my/org-journal-decrypt ()
|
||||
"Decrypt the current org journal file."
|
||||
(interactive)
|
||||
(org-journal-tags--ensure-decrypted))
|
||||
|
||||
(use-package citar
|
||||
:straight t
|
||||
:init
|
||||
|
|
@ -4841,21 +4887,61 @@ KEYS is a list of cons cells like (<label> . <time>)."
|
|||
|
||||
(setq org-roam-capture-templates
|
||||
`(("d" "default" plain "%?"
|
||||
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
|
||||
:target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
|
||||
:unnarrowed t)
|
||||
("f" "fleeting" plain "%?"
|
||||
:target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+filetags: :fleeting:\n")
|
||||
:unnarrowed t)
|
||||
("e" "encrypted" plain "%?"
|
||||
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org.gpg" "#+title: ${title}\n")
|
||||
:target (file+head "%<%Y%m%d%H%M%S>-${slug}.org.gpg" "#+title: ${title}\n")
|
||||
:unnarrowed t)))
|
||||
|
||||
(use-package org-roam-ql
|
||||
:straight t
|
||||
:after (org-roam)
|
||||
:config
|
||||
(general-define-key
|
||||
:states '(normal visual)
|
||||
:keymaps '(org-roam-ql-mode-map)
|
||||
"s" #'org-roam-ql-buffer-dispatch))
|
||||
|
||||
(defun my/org-roam-node-find-permanent (&optional other-window)
|
||||
(interactive current-prefix-arg)
|
||||
(org-roam-node-find
|
||||
other-window
|
||||
nil
|
||||
(lambda (node)
|
||||
(not
|
||||
(seq-contains-p
|
||||
"fleeting"
|
||||
(org-roam-node-tags node))))))
|
||||
|
||||
(defun my/org-roam-node-insert-permanent ()
|
||||
(interactive)
|
||||
(org-roam-node-insert
|
||||
(lambda (node)
|
||||
(not
|
||||
(seq-contains-p
|
||||
(org-roam-node-tags node)
|
||||
"fleeting")))))
|
||||
|
||||
(defun my/org-roam-ql-fleeting ()
|
||||
(interactive)
|
||||
(org-roam-ql-search
|
||||
'(tags "fleeting")
|
||||
"Fleeting notes"))
|
||||
|
||||
(with-eval-after-load 'org-roam
|
||||
(my-leader-def
|
||||
:infix "or"
|
||||
"" '(:which-key "org-roam")
|
||||
"i" #'org-roam-node-insert
|
||||
"r" #'org-roam-node-find
|
||||
"i" #'my/org-roam-node-insert-permanent
|
||||
"r" #'my/org-roam-node-find-permanent
|
||||
"g" #'org-roam-graph
|
||||
"c" #'org-roam-capture
|
||||
"b" #'org-roam-buffer-toggle)
|
||||
"b" #'org-roam-buffer-toggle
|
||||
"q" #'org-roam-ql-search
|
||||
"f" #'my/org-roam-ql-fleeting)
|
||||
(general-define-key
|
||||
:keymaps 'org-roam-mode-map
|
||||
:states '(normal)
|
||||
|
|
@ -4876,7 +4962,8 @@ KEYS is a list of cons cells like (<label> . <time>)."
|
|||
"a" #'org-roam-alias-add)
|
||||
(general-define-key
|
||||
:keymap 'org-mode-map
|
||||
"C-c i" 'org-roam-node-insert))
|
||||
"C-c i" #'my/org-roam-node-insert-permanent
|
||||
"C-c I" #'org-roam-node-insert))
|
||||
|
||||
(defface my/org-roam-count-overlay-face
|
||||
'((t :inherit tooltip))
|
||||
|
|
@ -5122,6 +5209,8 @@ Review checklist:
|
|||
- [ ] Reconcile ledger
|
||||
- [ ] Clear [[file:~/Downloads][downloads]] and [[file:~/00-Scratch][scratch]] folders
|
||||
- [ ] Process [[file:~/30-39 Life/35 Photos/35.00 Inbox/][photo inbox]]
|
||||
- [ ] Process new [[elisp:(my/org-roam-ql-fleeting)][fleeting notes]] (skip if tired)
|
||||
- [ ] Process new [[https://wallabag.sqrtminusone.xyz/tag/list/t:zk-inbox][zk-inbox]] (skip if tired)
|
||||
- [ ] Process [[file:../inbox.org][inbox]]
|
||||
- [ ] Create [[file:../recurring.org][recurring tasks]] for next week
|
||||
- [ ] Check agenda (-1 / +2 weeks): priorities, deadlines
|
||||
|
|
@ -5129,6 +5218,7 @@ Review checklist:
|
|||
- [[org-ql-search:todo%3A?buffers-files=%22org-agenda-files%22&super-groups=%28%28%3Aauto-outline-path-file%20t%29%29&sort=%28priority%20todo%20deadline%29][org-ql-search: All TODOs]]
|
||||
- [[org-ql-search:(and (todo) (not (tags \"nots\")) (not (ts :from -14)))?buffers-files=%22org-agenda-files%22&super-groups=%28%28%3Aauto-outline-path-file%20t%29%29&sort=%28priority%20todo%20deadline%29][org-ql-search: Stale tasks]]
|
||||
- [[org-ql-search:todo%3AWAIT?buffers-files=%22org-agenda-files%22&super-groups=%28%28%3Aauto-outline-path-file%20t%29%29&sort=%28priority%20todo%20deadline%29][org-ql-search: WAIT]]
|
||||
- [[org-ql-search:todo%3AMAYBE?buffers-files=%22org-agenda-files%22&super-groups=%28%28%3Aauto-outline-path-file%20t%29%29&sort=%28priority%20todo%20deadline%29][org-ql-search: MAYBE]]
|
||||
- [ ] Run auto-archiving
|
||||
- [ ] Review journal records
|
||||
")
|
||||
|
|
@ -5150,6 +5240,58 @@ TODO Write something, maybe? "))))
|
|||
(with-eval-after-load 'org-journal
|
||||
(my-leader-def "ojw" #'my/org-review-weekly))
|
||||
|
||||
(defun my/kill-messengers ()
|
||||
(interactive)
|
||||
(when (get-buffer telega-root-buffer-name)
|
||||
(telega-kill t))
|
||||
(call-process-shell-command "pkill -f rocketchat-desktop")
|
||||
(call-process-shell-command "pkill -f 'bwrap --args 36 element'")
|
||||
(call-process-shell-command "pkill -f element-desktop"))
|
||||
|
||||
(defun my/org-review-set-daily-record ()
|
||||
(save-excursion
|
||||
(org-journal-tags-prop-apply-delta :add '("review.daily"))
|
||||
(insert "Daily Review")
|
||||
(goto-char (point-max))
|
||||
|
||||
(insert "
|
||||
Maintenance checklist (/delete this/):
|
||||
- [ ] [[elisp:(my/kill-messengers)][Close all messengers]]
|
||||
- [ ] Process [[file:../inbox.org][inbox]]
|
||||
- [ ] Check if clocked tasks are properly annotated
|
||||
- [[elisp:(my/org-ql-clocked-today)][Tasks clocked today]]
|
||||
- [[elisp:(my/org-ql-closed-today)][Tasks closed today]]
|
||||
- [ ] Check agenda for the current week
|
||||
|
||||
/Remember, all of the following headers are optional./
|
||||
|
||||
*** Happened today
|
||||
Happened to me:
|
||||
- /Anything interesting?/
|
||||
Happened to the world:
|
||||
- /Anything important?/
|
||||
|
||||
*** New ideas
|
||||
/Write them down in org-roam with the \"fleeting\" tag; leave links here. Perhaps note what sparked that idea?/
|
||||
|
||||
*** Interactions today
|
||||
/Any meaninginful interactions, conflicts or tensions?/
|
||||
|
||||
*** Emotions today
|
||||
/How did I feel?/
|
||||
")))
|
||||
|
||||
(defun my/org-review-daily ()
|
||||
(interactive)
|
||||
(let ((org-journal-after-entry-create-hook
|
||||
`(,@org-journal-after-entry-create-hook
|
||||
my/org-review-set-daily-record)))
|
||||
(org-journal-new-entry nil)
|
||||
(org-fold-show-subtree)))
|
||||
|
||||
(with-eval-after-load 'org-journal
|
||||
(my-leader-def "ojd" #'my/org-review-daily))
|
||||
|
||||
(use-package org-contacts
|
||||
:straight (:type git :repo "https://repo.or.cz/org-contacts.git")
|
||||
:if (not my/remote-server)
|
||||
|
|
@ -5400,7 +5542,7 @@ TODO Write something, maybe? "))))
|
|||
"M-r" #'wdired-change-to-wdired-mode
|
||||
"<left>" #'dired-up-directory
|
||||
"<right>" #'dired-find-file
|
||||
"M-<return>" #'dired-open-xdg))
|
||||
"M-<return>" #'my/dired-open-xdg))
|
||||
|
||||
(defun my/dired-home ()
|
||||
"Open dired at $HOME"
|
||||
|
|
@ -5486,7 +5628,9 @@ TODO Write something, maybe? "))))
|
|||
:hook (dired-mode . (lambda ()
|
||||
(unless (or (file-remote-p default-directory)
|
||||
(string-match-p "/gnu/store" default-directory))
|
||||
(nerd-icons-dired-mode)))))
|
||||
(nerd-icons-dired-mode))))
|
||||
:config
|
||||
(advice-add #'dired-create-empty-file :around #'nerd-icons-dired--refresh-advice))
|
||||
|
||||
(use-package dired-open
|
||||
:straight t
|
||||
|
|
@ -5522,11 +5666,18 @@ TODO Write something, maybe? "))))
|
|||
:init
|
||||
(my-leader-def "aa" #'avy-dired-goto-line))
|
||||
|
||||
(defun my/dired-rsync--refresh ()
|
||||
(cl-loop for window being the windows
|
||||
do (with-current-buffer (window-buffer window)
|
||||
(when (derived-mode-p 'dired-mode)
|
||||
(revert-buffer)))))
|
||||
|
||||
(use-package dired-rsync
|
||||
:straight t
|
||||
:after (dired)
|
||||
:config
|
||||
(add-to-list 'global-mode-string '(:eval dired-rsync-modeline-status))
|
||||
(add-hook 'dired-rsync-success-hook #'my/dired-rsync--refresh)
|
||||
(general-define-key
|
||||
:states '(normal)
|
||||
:keymaps '(dired-mode-map)
|
||||
|
|
@ -5572,6 +5723,14 @@ TODO Write something, maybe? "))))
|
|||
:keymaps 'dired-mode-map
|
||||
"H" #'my/dired-goto-project-root))
|
||||
|
||||
(defun my/dired-open-xdg ()
|
||||
"Try to run `xdg-open' to open the file under point."
|
||||
(interactive)
|
||||
(when (executable-find "xdg-open")
|
||||
(let ((file (ignore-errors (dired-get-file-for-visit))))
|
||||
(start-process "dired-open" nil
|
||||
"xdg-open" (file-truename file)))))
|
||||
|
||||
(defun my/dired-bookmark-open ()
|
||||
(interactive)
|
||||
(let ((bookmarks
|
||||
|
|
@ -6994,109 +7153,6 @@ by the `my/elfeed-youtube-subtitles' function."
|
|||
(setq emms-volume-change-function #'my/set-volume)
|
||||
(setq emms-volume-change-amount 5)
|
||||
|
||||
(use-package ytel
|
||||
:straight t
|
||||
:commands (ytel)
|
||||
:config
|
||||
(setq ytel-invidious-api-url "https://invidio.xamh.de/")
|
||||
(general-define-key
|
||||
:states '(normal)
|
||||
:keymaps 'ytel-mode-map
|
||||
"q" #'ytel-quit
|
||||
"s" #'ytel-search
|
||||
"L" #'ytel-search-next-page
|
||||
"H" #'ytel-search-previous-page
|
||||
"RET" #'my/ytel-add-emms))
|
||||
|
||||
(with-eval-after-load 'emms
|
||||
(define-emms-source ytel (video)
|
||||
(let ((track (emms-track
|
||||
'url (concat "https://www.youtube.com/watch?v="
|
||||
(ytel-video-id video)))))
|
||||
(emms-track-set track 'info-title (ytel-video-title video))
|
||||
(emms-track-set track 'info-artist (ytel-video-author video))
|
||||
(emms-playlist-insert-track track))))
|
||||
|
||||
(defun my/ytel-add-emms ()
|
||||
(interactive)
|
||||
(emms-add-ytel (ytel-get-current-video)))
|
||||
|
||||
(setq my/invidious-instances-url
|
||||
"https://api.invidious.io/instances.json?pretty=1&sort_by=health")
|
||||
|
||||
(defun my/ytel-instances-fetch-json ()
|
||||
"Fetch list of invidious instances as json, sorted by health."
|
||||
(let
|
||||
((url-request-method "GET")
|
||||
(url-request-extra-headers
|
||||
'(("Accept" . "application/json"))))
|
||||
(with-current-buffer
|
||||
(url-retrieve-synchronously my/invidious-instances-url)
|
||||
(goto-char (point-min))
|
||||
(re-search-forward "^$")
|
||||
(let* ((json-object-type 'alist)
|
||||
(json-array-type 'list)
|
||||
(json-key-type 'string))
|
||||
(json-read)))))
|
||||
|
||||
(defun my/ytel-instances-alist-from-json ()
|
||||
"Make the json of invidious instances into an alist."
|
||||
(let ((jsonlist (my/ytel-instances-fetch-json))
|
||||
(inst ()))
|
||||
(while jsonlist
|
||||
(push (concat "https://" (caar jsonlist)) inst)
|
||||
(setq jsonlist (cdr jsonlist)))
|
||||
(nreverse inst)))
|
||||
|
||||
(defun my/ytel-choose-instance ()
|
||||
"Prompt user to choose an invidious instance to use."
|
||||
(interactive)
|
||||
(setq ytel-invidious-api-url
|
||||
(or (condition-case nil
|
||||
(completing-read "Using instance: "
|
||||
(cl-subseq (my/ytel-instances-alist-from-json) 0 11) nil "confirm" "https://")
|
||||
(error nil))
|
||||
"https://invidious.synopyta.org")))
|
||||
|
||||
(defun my/ytel-draw--buffer-nil-videos-fix ()
|
||||
(let ((inhibit-read-only t)
|
||||
(current-line (line-number-at-pos)))
|
||||
(erase-buffer)
|
||||
(setf header-line-format
|
||||
(concat "Search results for "
|
||||
(propertize ytel-search-term 'face 'ytel-video-published-face)
|
||||
", page "
|
||||
(number-to-string ytel-current-page)))
|
||||
(seq-do
|
||||
(lambda (v)
|
||||
(ytel--insert-video v)
|
||||
(insert "\n"))
|
||||
(seq-filter
|
||||
(lambda (v)
|
||||
(ytel-video-title v))
|
||||
ytel-videos))
|
||||
(goto-char (point-min))))
|
||||
|
||||
(with-eval-after-load 'ytel
|
||||
(advice-add #'ytel--draw-buffer :override #'my/ytel-draw--buffer-nil-videos-fix))
|
||||
|
||||
(defun my/ytel--format-unknown-fix (fun &rest args)
|
||||
(if (car args)
|
||||
(apply fun args)
|
||||
"unknown "))
|
||||
|
||||
(with-eval-after-load 'ytel
|
||||
(advice-add #'ytel--format-video-length :around #'my/ytel--format-unknown-fix)
|
||||
(advice-add #'ytel--format-video-published :around #'my/ytel--format-unknown-fix)
|
||||
(advice-add #'ytel--format-video-views :around #'my/ytel--format-unknown-fix))
|
||||
|
||||
(defun my/ytel-kill-url ()
|
||||
(interactive)
|
||||
(kill-new
|
||||
(concat
|
||||
"https://www.youtube.com/watch?v="
|
||||
(ytel-video-id (ytel-get-current-video)))))
|
||||
|
||||
(defun my/toggle-shr-use-fonts ()
|
||||
"Toggle the shr-use-fonts variable in buffer"
|
||||
(interactive)
|
||||
|
|
@ -7206,25 +7262,31 @@ by the `my/elfeed-youtube-subtitles' function."
|
|||
(setq mastodon-active-user "sqrtminusone")
|
||||
(my/persp-add-rule mastodon-mode 0 "mastodon")
|
||||
;; Hide spoilers by default
|
||||
(setq-default mastodon-toot--content-warning t)
|
||||
;; (setq-default mastodon-toot--content-warning nil)
|
||||
(setq mastodon-media--avatar-height 40)
|
||||
(setq mastodon-tl--timeline-posts-count "40")
|
||||
(setq mastodon-tl--show-avatars t)
|
||||
(setq mastodon-tl--horiz-bar
|
||||
(make-string shr-max-width
|
||||
(if (char-displayable-p ?―) ?― ?-)))
|
||||
;; The default emojis take two characters for me
|
||||
(setq mastodon-tl--symbols
|
||||
'((reply "" . "R")
|
||||
(boost "" . "B")
|
||||
(favourite "" . "F")
|
||||
(bookmark "" . "K")
|
||||
(media "" . "[media]")
|
||||
(verified "" . "V")
|
||||
(locked "" . "[locked]")
|
||||
(private "" . "[followers]")
|
||||
(direct "" . "[direct]")
|
||||
(edited "" . "[edited]"))))
|
||||
(mapcar (lambda (item)
|
||||
(setf (alist-get (car item) mastodon-tl--symbols)
|
||||
(cdr item)))
|
||||
'((reply "" . "R")
|
||||
(boost "" . "B")
|
||||
(favourite "" . "F")
|
||||
(bookmark "" . "K")
|
||||
(media "" . "[media]")
|
||||
(verified "" . "V")
|
||||
(locked "" . "[locked]")
|
||||
(private "" . "[followers]")
|
||||
(direct "" . "[direct]")
|
||||
(edited "" . "[edited]"))))
|
||||
|
||||
(use-package mastodon-alt
|
||||
:straight (:host github :repo "rougier/mastodon-alt")
|
||||
:disabled t
|
||||
:after (mastodon)
|
||||
:config
|
||||
(mastodon-alt-tl-activate))
|
||||
|
|
@ -7362,12 +7424,15 @@ by the `my/elfeed-youtube-subtitles' function."
|
|||
(lambda (toot)
|
||||
(and
|
||||
(or (not hide-replies)
|
||||
;; Why is the original function inverted??
|
||||
(mastodon-tl--is-reply toot))
|
||||
(not (mastodon-tl--is-reply toot)))
|
||||
(or (not hide-boosts)
|
||||
(not (alist-get 'reblog toot)))))
|
||||
toots)))
|
||||
(mapc #'mastodon-tl--toot toots))))
|
||||
toots))
|
||||
(start-pos (point)))
|
||||
(mapc #'mastodon-tl--toot toots)
|
||||
(when mastodon-tl--display-media-p
|
||||
(save-excursion
|
||||
(mastodon-media--inline-images start-pos (point)))))))
|
||||
|
||||
(defun my/mastodon-tl--get-home (hide-replies hide-boosts)
|
||||
(mastodon-tl--init
|
||||
|
|
@ -7474,8 +7539,8 @@ base toot."
|
|||
("o" "Thread" mastodon-tl--thread)
|
||||
("w" "Browser" my/mastodon-toot--browse)
|
||||
("le" "List edits" mastodon-toot--view-toot-edits)
|
||||
("lf" "List favouriters" mastodon-toot--list-toot-favouriters)
|
||||
("lb" "List boosters" mastodon-toot--list-toot-boosters)]
|
||||
("lf" "List favouriters" mastodon-toot--list-favouriters)
|
||||
("lb" "List boosters" mastodon-toot--list-boosters)]
|
||||
["Toot Actions"
|
||||
:class transient-row
|
||||
("r" "Reply" mastodon-toot--reply)
|
||||
|
|
@ -7677,6 +7742,8 @@ base toot."
|
|||
(message "Scrolled %s" scrolled)))))
|
||||
|
||||
(use-package telega
|
||||
;; :straight (:type built-in)
|
||||
;; For now emacs-telega-server is compatible with the latest telega.el
|
||||
:straight t
|
||||
:if (not (or my/remote-server))
|
||||
:commands (telega)
|
||||
|
|
@ -7688,6 +7755,10 @@ base toot."
|
|||
(telega-webpage-chat-link :foreground (my/color-value 'base0)
|
||||
:background (my/color-value 'fg)))
|
||||
:config
|
||||
(when (file-directory-p "~/.guix-extra-profiles/emacs/")
|
||||
(setq telega-server-command
|
||||
(expand-file-name
|
||||
"~/.guix-extra-profiles/emacs/emacs/bin/telega-server")))
|
||||
(setq telega-emoji-use-images nil)
|
||||
(setq telega-chat-fill-column 80)
|
||||
(setq telega-completing-read-function #'completing-read)
|
||||
|
|
@ -7744,7 +7815,7 @@ base toot."
|
|||
(company-mode 1)
|
||||
(setopt visual-fill-column-width
|
||||
(+ telega-chat-fill-column
|
||||
(if (display-graphic-p) 4 5)))
|
||||
(if (display-graphic-p) 5 6)))
|
||||
(setq-local split-width-threshold 1))
|
||||
(add-hook 'telega-chat-mode-hook #'my/telega-chat-setup)
|
||||
|
||||
|
|
@ -7839,7 +7910,7 @@ base toot."
|
|||
(setq biome-api-try-parse-error-as-response t))
|
||||
:config
|
||||
(add-to-list 'biome-query-coords
|
||||
'("Saint-Petersburg, Russia" 59.93863 30.31413))
|
||||
'("Saint-Petersburg, Russia" 59.942651 30.229930))
|
||||
(add-to-list 'biome-query-coords
|
||||
'("Tyumen, Russia" 57.15222 65.52722)))
|
||||
|
||||
|
|
@ -7971,20 +8042,31 @@ base toot."
|
|||
(setq gptel-backend (gptel-make-ollama "Ollama"
|
||||
:host "localhost:11434"
|
||||
:stream t
|
||||
:models '("llama3.1:latest" "llama3.1:instruct")))
|
||||
:models '("llama3.1:8b" "deepseek-r1:32b"
|
||||
"qwen2.5:32b" "qwen2.5-coder:32b"
|
||||
"eva-qwen2.5-q4_k_l-32b:latest"
|
||||
"t-pro-1.0-q4_k_m:latest")))
|
||||
(gptel-make-openai "OpenRouter"
|
||||
:host "openrouter.ai/api"
|
||||
:key (lambda () (my/password-store-get-field
|
||||
"My_Online/Accounts/openrouter" "api-key"))
|
||||
:stream t
|
||||
:models '("anthropic/claude-3.5-haiku"))
|
||||
(setq gptel--known-backends
|
||||
(seq-filter
|
||||
(lambda (cell)
|
||||
(not (equal (car cell) "ChatGPT")))
|
||||
gptel--known-backends))
|
||||
(setq gptel-response-prefix-alist
|
||||
'((markdown-mode . "[Response] ")
|
||||
(org-mode . "*** Response: ")
|
||||
(text-mode . "[Response]")))
|
||||
|
||||
;; (my/gptel-switch-backend "llama3.1:latest")
|
||||
(general-define-key
|
||||
:keymaps '(gptel-mode-map)
|
||||
:states '(insert normal)
|
||||
"C-<return>" 'gptel-send)
|
||||
(general-define-key
|
||||
:keymaps '(gptel-mode-map)
|
||||
:states '(normal)
|
||||
"?" #'gptel-menu)
|
||||
(gptel-make-gemini "Gemini"
|
||||
:key (my/password-store-get-field "My_Online/Accounts/google-gemini" "api")
|
||||
:stream t))
|
||||
"C-<return>" 'gptel-send
|
||||
"M-o" #'gptel-menu))
|
||||
|
||||
(use-package ellama
|
||||
:straight t
|
||||
|
|
@ -7995,19 +8077,26 @@ base toot."
|
|||
(require 'llm-ollama)
|
||||
;; I've looked for this option for 1.5 hours
|
||||
(setq ellama-long-lines-length 100000)
|
||||
(my-leader-def
|
||||
"aie" '(:wk "ellama" :keymap ellama-command-map))
|
||||
|
||||
(setq ellama-provider (make-llm-ollama
|
||||
:chat-model "llama3.1:instruct"
|
||||
:embedding-model "llama3.1:instruct"))
|
||||
:chat-model "qwen2.5:32b"
|
||||
:embedding-model "qwen2.5:32b"))
|
||||
(setq ellama-coding-provider (make-llm-ollama
|
||||
:chat-model "qwen2.5-coder:32b"
|
||||
:embedding-model "qwen2.5-coder:32b"))
|
||||
(setq ellama-providers
|
||||
`(("llama3.1:8b" . ,(make-llm-ollama
|
||||
:chat-model "llama3.1:latest"
|
||||
:embedding-model "llama3.1:latest"))
|
||||
("llama3.1:instruct" . ,(make-llm-ollama
|
||||
:chat-model "llama3.1:instruct"
|
||||
:embedding-model "llama3.1:instruct")))))
|
||||
:chat-model "llama3.1:latest"
|
||||
:embedding-model "llama3.1:latest"))
|
||||
("phi4:latest" . ,(make-llm-ollama
|
||||
:chat-model "phi4:latest"
|
||||
:embedding-model "phi4:latest"))
|
||||
("qwen2.5:32b" . ,(make-llm-ollama
|
||||
:chat-model "qwen2.5:32b"
|
||||
:embedding-model "qwen2.5:32b"))
|
||||
("qwen2.5-coder:32b" . ,(make-llm-ollama
|
||||
:chat-model "qwen2.5-coder:32b"
|
||||
:embedding-model "qwen2.5-coder:32b")))))
|
||||
|
||||
(with-eval-after-load 'ellama
|
||||
(transient-define-prefix my/ellama-transient ()
|
||||
|
|
@ -8024,9 +8113,7 @@ base toot."
|
|||
("ci" "Improve" ellama-code-improve)]
|
||||
["Natural Language"
|
||||
:class transient-row
|
||||
("np" "Proof-read" my/ellama-proof-read)
|
||||
("nw" "Improve wording" my/ellama-improve-wording)
|
||||
("nc" "Improve conciseness" my/ellama-improve-concise)]
|
||||
("np" "Proof-read" my/ellama-proof-read)]
|
||||
["Formatting"
|
||||
:class transient-row
|
||||
("ff" "Format" ellama-make-format)
|
||||
|
|
@ -8051,7 +8138,6 @@ base toot."
|
|||
("sr" "Rename ression" ellama-session-rename)
|
||||
("sd" "Delete session" ellama-session-remove)]))
|
||||
|
||||
|
||||
(defun my/ellama ()
|
||||
(interactive)
|
||||
(require 'ellama)
|
||||
|
|
@ -8076,27 +8162,46 @@ base toot."
|
|||
(delete-file file1)
|
||||
(delete-file file2))))
|
||||
|
||||
(defun my/ellama-text-with-diff (text is-org-mode prompt)
|
||||
(require 'ellama)
|
||||
(defun my/ellama-proof-read--display (text is-org-mode prompt)
|
||||
(llm-chat-async
|
||||
ellama-provider
|
||||
(llm-make-chat-prompt
|
||||
(format prompt text))
|
||||
(lambda (changed-text)
|
||||
(when is-org-mode
|
||||
(setq changed-text (ellama--translate-markdown-to-org-filter changed-text)))
|
||||
(let ((buffer (generate-new-buffer "*ellama-diff*")))
|
||||
(lambda (response)
|
||||
(let* ((parts (split-string response "-FIXED TEXT ENDS-"))
|
||||
(changed-text (nth 0 parts))
|
||||
(comments (nth 1 parts))
|
||||
(buffer (generate-new-buffer "*ellama-diff*")))
|
||||
(when is-org-mode
|
||||
(setq changed-text (ellama--translate-markdown-to-org-filter changed-text)))
|
||||
(with-current-buffer buffer
|
||||
(text-mode)
|
||||
(insert changed-text)
|
||||
(insert "\n\n")
|
||||
(insert (my/diff-strings text changed-text)))
|
||||
(insert
|
||||
(propertize "Changed text:\n" 'face 'transient-heading)
|
||||
(string-trim changed-text)
|
||||
"\n\n"
|
||||
(propertize "Comments:\n" 'face 'transient-heading)
|
||||
(string-trim comments)
|
||||
"\n\n"
|
||||
(propertize "Diff:\n" 'face 'transient-heading)
|
||||
(my/diff-strings text changed-text)))
|
||||
(display-buffer buffer)))
|
||||
(lambda (&rest err)
|
||||
(message "Error: %s" err))))
|
||||
|
||||
(setq my/ellama-proof-read-prompt
|
||||
"Proof-read the following text. Fix any errors but keep the original style and punctuation, including linebreaks. Print the changed text and nothing else, not even \"Here's the proof-read text\".\n\n %s")
|
||||
"Proof-read the following text. Follow these rules:
|
||||
- Fix all grammar errors
|
||||
- Keep the original style and punctuation, including linebreaks.
|
||||
- Use British spelling
|
||||
- Do not replace ' with ’, and do not touch other such symbols
|
||||
|
||||
Output the following and nothing else:
|
||||
- The fixed text
|
||||
- The string -FIXED TEXT ENDS-
|
||||
- List of found errors
|
||||
- List of style suggestions
|
||||
%s")
|
||||
|
||||
(defun my/ellama--text ()
|
||||
(if (region-active-p)
|
||||
|
|
@ -8105,28 +8210,17 @@ base toot."
|
|||
|
||||
(defun my/ellama-proof-read (text is-org-mode)
|
||||
(interactive (list (my/ellama--text) (derived-mode-p 'org-mode)))
|
||||
(my/ellama-text-with-diff text is-org-mode my/ellama-proof-read-prompt))
|
||||
|
||||
(setq my/ellama-improve-wording-prompt
|
||||
"Proof-read the following text. Fix any errors and improve wording. Print the changed text and nothing else, not even \"Here's the improved text\".\n\n %s")
|
||||
|
||||
(defun my/ellama-improve-wording (text is-org-mode)
|
||||
(interactive (list (my/ellama--text) (derived-mode-p 'org-mode)))
|
||||
(my/ellama-text-with-diff text is-org-mode my/ellama-improve-wording-prompt))
|
||||
|
||||
(setq my/ellama-improve-concise-prompt
|
||||
"Make the following text more concise. Print the changed text and nothing else, not even \"Here's the improved text\".\n\n %s")
|
||||
|
||||
(defun my/ellama-improve-concise (text is-org-mode)
|
||||
(interactive (list (my/ellama--text) (derived-mode-p 'org-mode)))
|
||||
(my/ellama-text-with-diff text is-org-mode my/ellama-improve-concise-prompt))
|
||||
(require 'ellama)
|
||||
(my/ellama-proof-read--display text is-org-mode my/ellama-proof-read-prompt))
|
||||
|
||||
(defun my/whisper--format-vtt-seconds (seconds)
|
||||
(let* ((hours (/ (floor seconds) (* 60 60)))
|
||||
(minutes (/ (- (floor seconds) (* hours 60 60)) 60))
|
||||
(sec (% (floor seconds) 60))
|
||||
(ms (floor (* 1000 (- seconds (floor seconds))))))
|
||||
(format "%.2d:%.2d:%.2d.%.3d" hours minutes sec ms)))
|
||||
(if (numberp seconds)
|
||||
(let* ((hours (/ (floor seconds) (* 60 60)))
|
||||
(minutes (/ (- (floor seconds) (* hours 60 60)) 60))
|
||||
(sec (% (floor seconds) 60))
|
||||
(ms (floor (* 1000 (- seconds (floor seconds))))))
|
||||
(format "%.2d:%.2d:%.2d.%.3d" hours minutes sec ms))
|
||||
""))
|
||||
|
||||
(defun my/whisper--save-chucks-vtt (path data)
|
||||
(with-temp-file path
|
||||
|
|
|
|||
602
Emacs.org
602
Emacs.org
|
|
@ -44,6 +44,7 @@ I decided not to keep configs for features that I do not use anymore because thi
|
|||
|
||||
| Feature | Last commit |
|
||||
|--------------------------+------------------------------------------|
|
||||
| ytel | 327340a95c4ff9cffd171f6bd937c6041f63add7 |
|
||||
| org-roam dailies | d2648918fcc338bd5c1cd6d5c0aa60a65077ccf7 |
|
||||
| org-roam projects | 025278a1e180e86f3aade20242e4ac1cdc1a2f13 |
|
||||
| treemacs | 3d87852745caacc0863c747f1fa9871d367240d2 |
|
||||
|
|
@ -2639,6 +2640,7 @@ References:
|
|||
(setq lsp-headerline-breadcrumb-enable nil)
|
||||
(setq lsp-modeline-code-actions-enable nil)
|
||||
(setq lsp-modeline-diagnostics-enable nil)
|
||||
(setq lsp-volar-take-over-mode nil)
|
||||
(add-to-list 'lsp-language-id-configuration '(svelte-mode . "svelte")))
|
||||
|
||||
(use-package lsp-ui
|
||||
|
|
@ -3210,7 +3212,7 @@ A general-purpose package to run formatters on files. While the most popular for
|
|||
(use-package copilot
|
||||
:straight (:host github :repo "copilot-emacs/copilot.el")
|
||||
:commands (copilot-mode)
|
||||
:if (not (or my/remote-server my/is-termux))
|
||||
:disabled t
|
||||
:init
|
||||
(add-hook 'emacs-startup-hook
|
||||
(lambda ()
|
||||
|
|
@ -3392,11 +3394,13 @@ Hooking this up with lsp.
|
|||
Vue settings
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/web-mode-vue-setup (&rest _)
|
||||
(when (string-match-p (rx ".vue" eos) (buffer-file-name))
|
||||
(setq-local web-mode-script-padding 0)
|
||||
(setq-local web-mode-style-padding 0)
|
||||
(setq-local create-lockfiles nil)
|
||||
(setq-local web-mode-enable-auto-pairing nil)))
|
||||
(let ((filename (buffer-file-name)))
|
||||
(when (and (stringp filename)
|
||||
(string-match-p (rx ".vue" eos) filename))
|
||||
(setq-local web-mode-script-padding 0)
|
||||
(setq-local web-mode-style-padding 0)
|
||||
(setq-local create-lockfiles nil)
|
||||
(setq-local web-mode-enable-auto-pairing nil))))
|
||||
|
||||
(add-hook 'web-mode-hook 'my/web-mode-vue-setup)
|
||||
(add-hook 'editorconfig-after-apply-functions 'my/web-mode-vue-setup)
|
||||
|
|
@ -3813,7 +3817,7 @@ References:
|
|||
:config
|
||||
(setq langtool-language-tool-server-jar "/home/pavel/bin/LanguageTool-6.4/languagetool-server.jar")
|
||||
(setq langtool-mother-tongue "ru")
|
||||
(setq langtool-default-language "en-US"))
|
||||
(setq langtool-default-language "ru-RU"))
|
||||
|
||||
(my-leader-def
|
||||
:infix "L"
|
||||
|
|
@ -4950,6 +4954,7 @@ Enable languages
|
|||
`((emacs-lisp . t)
|
||||
(python . t)
|
||||
(sql . t)
|
||||
(sqlite . t)
|
||||
;; (typescript .t)
|
||||
(hy . t)
|
||||
(shell . t)
|
||||
|
|
@ -5510,6 +5515,9 @@ It's been somewhat complicated to integrate into my workflow, but I think it's b
|
|||
(with-eval-after-load 'org
|
||||
(my-leader-def "ol" #'org-clock-agg))
|
||||
:config
|
||||
(setq org-clock-agg-node-format
|
||||
"%-%(+ title-width)t %20c %8z %s/%S")
|
||||
(setq org-clock-agg-node-title-width-delta 47)
|
||||
(push
|
||||
(cons "Agenda+Archive"
|
||||
(append
|
||||
|
|
@ -5658,6 +5666,23 @@ And use the function to set the total clocked time.
|
|||
:infix "SPC"
|
||||
"C" #'my/org-clock-recent))
|
||||
#+end_src
|
||||
***** Fix tasks without TASK_KIND
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/org-fix-task-kind ()
|
||||
(interactive)
|
||||
(let ((entries (org-ql-query
|
||||
:select #'element-with-markers
|
||||
:from (current-buffer)
|
||||
:where '(and (olp "Tasks")
|
||||
(not (property "TASK_KIND"))
|
||||
(clocked)))))
|
||||
(org-fold-show-all)
|
||||
(dolist (entry entries)
|
||||
(let ((marker (org-element-property :org-marker entry)))
|
||||
(org-with-point-at marker
|
||||
(let ((value (org-read-property-value "TASK_KIND")))
|
||||
(org-set-property "TASK_KIND" value)))))))
|
||||
#+end_src
|
||||
**** org-super-agenda
|
||||
[[https://github.com/alphapapa/org-super-agenda][org-super-agenda]] is alphapapa's extension to group items in org-agenda. I don't use it instead of the standard agenda, but =org-ql= uses it for some of its views.
|
||||
|
||||
|
|
@ -5766,6 +5791,37 @@ A view to return all TODOs in a category.
|
|||
:super-groups '((:auto-outline-path-file t)))))
|
||||
#+end_src
|
||||
|
||||
***** Items clocked or closed today
|
||||
Some custom functions to account for =org-extend-today-until=. Needed because sometimes my daily reviews cross 00:00.
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/org-ql-clocked-today ()
|
||||
(interactive)
|
||||
(let ((today (format-time-string
|
||||
"%Y-%m-%d"
|
||||
(days-to-time
|
||||
(- (org-today) (time-to-days 0))))))
|
||||
(org-ql-search (org-agenda-files) `(clocked :from ,today)
|
||||
:title "Clocked today"
|
||||
:sort '(todo priority date)
|
||||
:super-groups '((:auto-outline-path-file t)
|
||||
(:auto-todo t)))))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/org-ql-closed-today ()
|
||||
(interactive)
|
||||
(let ((today (format-time-string
|
||||
"%Y-%m-%d"
|
||||
(days-to-time
|
||||
(- (org-today) (time-to-days 0))))))
|
||||
(org-ql-search (org-agenda-files) `(closed :from ,today)
|
||||
:title "Closed today"
|
||||
:sort '(todo priority date)
|
||||
:super-groups '((:auto-outline-path-file t)
|
||||
(:auto-todo t)))))
|
||||
#+end_src
|
||||
|
||||
***** Configuring views
|
||||
Putting all the above in =org-ql-views=.
|
||||
|
||||
|
|
@ -5791,13 +5847,8 @@ Putting all the above in =org-ql-views=.
|
|||
:sort '(todo priority date)
|
||||
:super-groups '((:auto-outline-path-file t))))
|
||||
(cons "Review: Recently timestamped" #'my/org-ql-view-recent-items)
|
||||
(cons "Review: Unlinked to meetings"
|
||||
(list :buffers-files #'org-agenda-files
|
||||
:query '(and (todo "DONE" "NO")
|
||||
(not (property "MEETING"))
|
||||
(ts :from -7))
|
||||
:super-groups '((:auto-outline-path-file t))))
|
||||
(cons "Review: Meeting" #'my/org-ql-meeting-tasks)
|
||||
(cons "Review: Clocked today" #'my/org-ql-clocked-today)
|
||||
(cons "Review: Closed today" #'my/org-ql-closed-today)
|
||||
(cons "Fix: tasks without TASK_KIND"
|
||||
(lambda ()
|
||||
(interactive)
|
||||
|
|
@ -6221,7 +6272,7 @@ First, I need to find and group and such headers. =org-ql= can help with that:
|
|||
(thread-last
|
||||
heading
|
||||
(substring-no-properties)
|
||||
(replace-regexp-in-string (rx (| "(" "[") (+ alnum) (| "]" ")")) "")
|
||||
(replace-regexp-in-string (rx (| "(" "[") (+ nonl) (| "]" ")")) "")
|
||||
(replace-regexp-in-string (rx " " (+ (or digit "."))) " ")
|
||||
(replace-regexp-in-string (rx (+ " ")) " ")
|
||||
(string-trim)))
|
||||
|
|
@ -6542,6 +6593,15 @@ And here's the function that creates a drawer with such information. At the mome
|
|||
(add-hook 'org-journal-after-entry-create-hook
|
||||
#'my/set-journal-header)
|
||||
#+end_src
|
||||
|
||||
Also, a function to decrypt the current file:
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/org-journal-decrypt ()
|
||||
"Decrypt the current org journal file."
|
||||
(interactive)
|
||||
(org-journal-tags--ensure-decrypted))
|
||||
#+end_src
|
||||
|
||||
*** Bibliography
|
||||
I use [[https://www.zotero.org/][Zotero]] to manage my bibliograhy.
|
||||
|
||||
|
|
@ -6668,12 +6728,63 @@ Capture templates for =org-roam-capture=. As for now, nothing too complicated he
|
|||
#+begin_src emacs-lisp
|
||||
(setq org-roam-capture-templates
|
||||
`(("d" "default" plain "%?"
|
||||
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
|
||||
:target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
|
||||
:unnarrowed t)
|
||||
("f" "fleeting" plain "%?"
|
||||
:target (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+filetags: :fleeting:\n")
|
||||
:unnarrowed t)
|
||||
("e" "encrypted" plain "%?"
|
||||
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org.gpg" "#+title: ${title}\n")
|
||||
:target (file+head "%<%Y%m%d%H%M%S>-${slug}.org.gpg" "#+title: ${title}\n")
|
||||
:unnarrowed t)))
|
||||
#+end_src
|
||||
**** org-roam-ql
|
||||
[[https://github.com/ahmed-shariff/org-roam-ql][org-roam-ql]] is a package to query =org-roam= files like =org-ql=.
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(use-package org-roam-ql
|
||||
:straight t
|
||||
:after (org-roam)
|
||||
:config
|
||||
(general-define-key
|
||||
:states '(normal visual)
|
||||
:keymaps '(org-roam-ql-mode-map)
|
||||
"s" #'org-roam-ql-buffer-dispatch))
|
||||
#+end_src
|
||||
|
||||
**** Finding nodes
|
||||
Find and insert permanent nodes:
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/org-roam-node-find-permanent (&optional other-window)
|
||||
(interactive current-prefix-arg)
|
||||
(org-roam-node-find
|
||||
other-window
|
||||
nil
|
||||
(lambda (node)
|
||||
(not
|
||||
(seq-contains-p
|
||||
"fleeting"
|
||||
(org-roam-node-tags node))))))
|
||||
|
||||
(defun my/org-roam-node-insert-permanent ()
|
||||
(interactive)
|
||||
(org-roam-node-insert
|
||||
(lambda (node)
|
||||
(not
|
||||
(seq-contains-p
|
||||
(org-roam-node-tags node)
|
||||
"fleeting")))))
|
||||
#+end_src
|
||||
|
||||
List unprocessed fleeting notes:
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/org-roam-ql-fleeting ()
|
||||
(interactive)
|
||||
(org-roam-ql-search
|
||||
'(tags "fleeting")
|
||||
"Fleeting notes"))
|
||||
#+end_src
|
||||
|
||||
**** Keybindings
|
||||
A set of keybindings to quickly access things in Org Roam.
|
||||
|
||||
|
|
@ -6682,11 +6793,13 @@ A set of keybindings to quickly access things in Org Roam.
|
|||
(my-leader-def
|
||||
:infix "or"
|
||||
"" '(:which-key "org-roam")
|
||||
"i" #'org-roam-node-insert
|
||||
"r" #'org-roam-node-find
|
||||
"i" #'my/org-roam-node-insert-permanent
|
||||
"r" #'my/org-roam-node-find-permanent
|
||||
"g" #'org-roam-graph
|
||||
"c" #'org-roam-capture
|
||||
"b" #'org-roam-buffer-toggle)
|
||||
"b" #'org-roam-buffer-toggle
|
||||
"q" #'org-roam-ql-search
|
||||
"f" #'my/org-roam-ql-fleeting)
|
||||
(general-define-key
|
||||
:keymaps 'org-roam-mode-map
|
||||
:states '(normal)
|
||||
|
|
@ -6707,7 +6820,8 @@ A set of keybindings to quickly access things in Org Roam.
|
|||
"a" #'org-roam-alias-add)
|
||||
(general-define-key
|
||||
:keymap 'org-mode-map
|
||||
"C-c i" 'org-roam-node-insert))
|
||||
"C-c i" #'my/org-roam-node-insert-permanent
|
||||
"C-c I" #'org-roam-node-insert))
|
||||
#+end_src
|
||||
**** Backlinks count display
|
||||
Occasionally I want to see how many backlinks a particular page has.
|
||||
|
|
@ -7007,7 +7121,7 @@ I'll use data from git to get the list of what I've been working on. The directo
|
|||
(org-roam-node-title (cdr c)))))
|
||||
(apply #'concat)))))
|
||||
#+end_src
|
||||
**** Org Journal integration
|
||||
**** General review logic
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/org-review-get-last-review-date (kind)
|
||||
(let* ((start-of-day (- (time-convert nil #'integer)
|
||||
|
|
@ -7027,9 +7141,7 @@ I'll use data from git to get the list of what I've been working on. The directo
|
|||
(pcase kind
|
||||
('weekly (- start-of-day (* 7 24 60 60)))))))
|
||||
#+end_src
|
||||
|
||||
|
||||
|
||||
**** Weekly review
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/org-review-set-weekly-record ()
|
||||
(save-excursion
|
||||
|
|
@ -7049,6 +7161,8 @@ Review checklist:
|
|||
- [ ] Reconcile ledger
|
||||
- [ ] Clear [[file:~/Downloads][downloads]] and [[file:~/00-Scratch][scratch]] folders
|
||||
- [ ] Process [[file:~/30-39 Life/35 Photos/35.00 Inbox/][photo inbox]]
|
||||
- [ ] Process new [[elisp:(my/org-roam-ql-fleeting)][fleeting notes]] (skip if tired)
|
||||
- [ ] Process new [[https://wallabag.sqrtminusone.xyz/tag/list/t:zk-inbox][zk-inbox]] (skip if tired)
|
||||
- [ ] Process [[file:../inbox.org][inbox]]
|
||||
- [ ] Create [[file:../recurring.org][recurring tasks]] for next week
|
||||
- [ ] Check agenda (-1 / +2 weeks): priorities, deadlines
|
||||
|
|
@ -7056,6 +7170,7 @@ Review checklist:
|
|||
- [[org-ql-search:todo%3A?buffers-files=%22org-agenda-files%22&super-groups=%28%28%3Aauto-outline-path-file%20t%29%29&sort=%28priority%20todo%20deadline%29][org-ql-search: All TODOs]]
|
||||
- [[org-ql-search:(and (todo) (not (tags \"nots\")) (not (ts :from -14)))?buffers-files=%22org-agenda-files%22&super-groups=%28%28%3Aauto-outline-path-file%20t%29%29&sort=%28priority%20todo%20deadline%29][org-ql-search: Stale tasks]]
|
||||
- [[org-ql-search:todo%3AWAIT?buffers-files=%22org-agenda-files%22&super-groups=%28%28%3Aauto-outline-path-file%20t%29%29&sort=%28priority%20todo%20deadline%29][org-ql-search: WAIT]]
|
||||
- [[org-ql-search:todo%3AMAYBE?buffers-files=%22org-agenda-files%22&super-groups=%28%28%3Aauto-outline-path-file%20t%29%29&sort=%28priority%20todo%20deadline%29][org-ql-search: MAYBE]]
|
||||
- [ ] Run auto-archiving
|
||||
- [ ] Review journal records
|
||||
")
|
||||
|
|
@ -7079,6 +7194,70 @@ TODO Write something, maybe? "))))
|
|||
(with-eval-after-load 'org-journal
|
||||
(my-leader-def "ojw" #'my/org-review-weekly))
|
||||
#+end_src
|
||||
**** Daily review
|
||||
My attempt at a daily review, or an end-of-day routine.
|
||||
|
||||
I try to keep it under 10-15 minutes.
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/kill-messengers ()
|
||||
(interactive)
|
||||
(when (get-buffer telega-root-buffer-name)
|
||||
(telega-kill t))
|
||||
(call-process-shell-command "pkill -f rocketchat-desktop")
|
||||
(call-process-shell-command "pkill -f 'bwrap --args 36 element'")
|
||||
(call-process-shell-command "pkill -f element-desktop"))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/org-review-set-daily-record ()
|
||||
(save-excursion
|
||||
(org-journal-tags-prop-apply-delta :add '("review.daily"))
|
||||
(insert "Daily Review")
|
||||
(goto-char (point-max))
|
||||
|
||||
(insert "
|
||||
Maintenance checklist (/delete this/):
|
||||
- [ ] [[elisp:(my/kill-messengers)][Close all messengers]]
|
||||
- [ ] Process [[file:../inbox.org][inbox]]
|
||||
- [ ] Check if clocked tasks are properly annotated
|
||||
- [[elisp:(my/org-ql-clocked-today)][Tasks clocked today]]
|
||||
- [[elisp:(my/org-ql-closed-today)][Tasks closed today]]
|
||||
- [ ] Check agenda for the current week
|
||||
|
||||
/Remember, all of the following headers are optional./
|
||||
|
||||
,*** Happened today
|
||||
Happened to me:
|
||||
- /Anything interesting?/
|
||||
Happened to the world:
|
||||
- /Anything important?/
|
||||
|
||||
,*** New ideas
|
||||
/Write them down in org-roam with the \"fleeting\" tag; leave links here. Perhaps note what sparked that idea?/
|
||||
|
||||
,*** Interactions today
|
||||
/Any meaninginful interactions, conflicts or tensions?/
|
||||
|
||||
,*** Emotions today
|
||||
/How did I feel?/
|
||||
")))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/org-review-daily ()
|
||||
(interactive)
|
||||
(let ((org-journal-after-entry-create-hook
|
||||
`(,@org-journal-after-entry-create-hook
|
||||
my/org-review-set-daily-record)))
|
||||
(org-journal-new-entry nil)
|
||||
(org-fold-show-subtree)))
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(with-eval-after-load 'org-journal
|
||||
(my-leader-def "ojd" #'my/org-review-daily))
|
||||
#+end_src
|
||||
|
||||
*** Contacts
|
||||
=org-contacts= is a package to store contacts in an org file.
|
||||
|
|
@ -7521,7 +7700,7 @@ My config mostly follows ranger's and vifm's keybindings which I'm used to.
|
|||
"M-r" #'wdired-change-to-wdired-mode
|
||||
"<left>" #'dired-up-directory
|
||||
"<right>" #'dired-find-file
|
||||
"M-<return>" #'dired-open-xdg))
|
||||
"M-<return>" #'my/dired-open-xdg))
|
||||
|
||||
(defun my/dired-home ()
|
||||
"Open dired at $HOME"
|
||||
|
|
@ -7630,7 +7809,9 @@ Display icons for files.
|
|||
:hook (dired-mode . (lambda ()
|
||||
(unless (or (file-remote-p default-directory)
|
||||
(string-match-p "/gnu/store" default-directory))
|
||||
(nerd-icons-dired-mode)))))
|
||||
(nerd-icons-dired-mode))))
|
||||
:config
|
||||
(advice-add #'dired-create-empty-file :around #'nerd-icons-dired--refresh-advice))
|
||||
#+end_src
|
||||
|
||||
Provides stuff like =dired-open-xdg=
|
||||
|
|
@ -7682,13 +7863,20 @@ Display git info, such as the last commit for file and stuff. It's pretty useful
|
|||
(my-leader-def "aa" #'avy-dired-goto-line))
|
||||
#+end_src
|
||||
|
||||
[[https://github.com/stsquad/dired-rsync][dired-rsync]] allows using =rsync= instead of the default synchronous copy operation.
|
||||
[[https://github.com/stsquad/dired-rsync][dired-rsync]] allows using =rsync= instead of the default synchronous copy operation. The only trouble is that it doesn't replace =dired-do-copy= completely, so...
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/dired-rsync--refresh ()
|
||||
(cl-loop for window being the windows
|
||||
do (with-current-buffer (window-buffer window)
|
||||
(when (derived-mode-p 'dired-mode)
|
||||
(revert-buffer)))))
|
||||
|
||||
(use-package dired-rsync
|
||||
:straight t
|
||||
:after (dired)
|
||||
:config
|
||||
(add-to-list 'global-mode-string '(:eval dired-rsync-modeline-status))
|
||||
(add-hook 'dired-rsync-success-hook #'my/dired-rsync--refresh)
|
||||
(general-define-key
|
||||
:states '(normal)
|
||||
:keymaps '(dired-mode-map)
|
||||
|
|
@ -7744,6 +7932,19 @@ Goto project root.
|
|||
:keymaps 'dired-mode-map
|
||||
"H" #'my/dired-goto-project-root))
|
||||
#+end_src
|
||||
|
||||
Open a file with =xdg-open=. I used =dired-open= for this before, but I've had to abandon the package because it switched from =start-process= to =call-process=, which blocks my EXWM.
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/dired-open-xdg ()
|
||||
"Try to run `xdg-open' to open the file under point."
|
||||
(interactive)
|
||||
(when (executable-find "xdg-open")
|
||||
(let ((file (ignore-errors (dired-get-file-for-visit))))
|
||||
(start-process "dired-open" nil
|
||||
"xdg-open" (file-truename file)))))
|
||||
#+end_src
|
||||
|
||||
*** Bookmarks
|
||||
A simple bookmark list for Dired, mainly to use with TRAMP. I may look into a proper bookmarking system later.
|
||||
|
||||
|
|
@ -9736,142 +9937,6 @@ The list will be in reverse order."
|
|||
(setq alists (cons alist alists)))
|
||||
alists)))
|
||||
#+end_src
|
||||
*** ytel
|
||||
[[https://github.com/gRastello/ytel][ytel]] is a YouTube (actually Invidious) frontend, which lets one search YouTube (whereas the setup with elfeed just lets one view the pre-defined subscriptions).
|
||||
|
||||
**** Package config
|
||||
The package doesn't provide evil bindings, so I define my own.
|
||||
#+begin_src emacs-lisp
|
||||
(use-package ytel
|
||||
:straight t
|
||||
:commands (ytel)
|
||||
:config
|
||||
(setq ytel-invidious-api-url "https://invidio.xamh.de/")
|
||||
(general-define-key
|
||||
:states '(normal)
|
||||
:keymaps 'ytel-mode-map
|
||||
"q" #'ytel-quit
|
||||
"s" #'ytel-search
|
||||
"L" #'ytel-search-next-page
|
||||
"H" #'ytel-search-previous-page
|
||||
"RET" #'my/ytel-add-emms))
|
||||
#+end_src
|
||||
|
||||
**** EMMS integration
|
||||
And here is the same kind of integration with EMMS as in the elfeed setup:
|
||||
#+begin_src emacs-lisp
|
||||
(with-eval-after-load 'emms
|
||||
(define-emms-source ytel (video)
|
||||
(let ((track (emms-track
|
||||
'url (concat "https://www.youtube.com/watch?v="
|
||||
(ytel-video-id video)))))
|
||||
(emms-track-set track 'info-title (ytel-video-title video))
|
||||
(emms-track-set track 'info-artist (ytel-video-author video))
|
||||
(emms-playlist-insert-track track))))
|
||||
|
||||
(defun my/ytel-add-emms ()
|
||||
(interactive)
|
||||
(emms-add-ytel (ytel-get-current-video)))
|
||||
#+end_src
|
||||
|
||||
**** Choosing instances
|
||||
Invidious instances aren't particularly reliable, but there plenty of them, and there's an API at =invidious.io= that returns the available instances and their health, so we can use that.
|
||||
|
||||
Inspired by [[https://github.com/grastello/ytel/issues/17#issuecomment-801745429][this comment]].
|
||||
#+begin_src emacs-lisp
|
||||
(setq my/invidious-instances-url
|
||||
"https://api.invidious.io/instances.json?pretty=1&sort_by=health")
|
||||
#+end_src
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/ytel-instances-fetch-json ()
|
||||
"Fetch list of invidious instances as json, sorted by health."
|
||||
(let
|
||||
((url-request-method "GET")
|
||||
(url-request-extra-headers
|
||||
'(("Accept" . "application/json"))))
|
||||
(with-current-buffer
|
||||
(url-retrieve-synchronously my/invidious-instances-url)
|
||||
(goto-char (point-min))
|
||||
(re-search-forward "^$")
|
||||
(let* ((json-object-type 'alist)
|
||||
(json-array-type 'list)
|
||||
(json-key-type 'string))
|
||||
(json-read)))))
|
||||
|
||||
(defun my/ytel-instances-alist-from-json ()
|
||||
"Make the json of invidious instances into an alist."
|
||||
(let ((jsonlist (my/ytel-instances-fetch-json))
|
||||
(inst ()))
|
||||
(while jsonlist
|
||||
(push (concat "https://" (caar jsonlist)) inst)
|
||||
(setq jsonlist (cdr jsonlist)))
|
||||
(nreverse inst)))
|
||||
|
||||
(defun my/ytel-choose-instance ()
|
||||
"Prompt user to choose an invidious instance to use."
|
||||
(interactive)
|
||||
(setq ytel-invidious-api-url
|
||||
(or (condition-case nil
|
||||
(completing-read "Using instance: "
|
||||
(cl-subseq (my/ytel-instances-alist-from-json) 0 11) nil "confirm" "https://")
|
||||
(error nil))
|
||||
"https://invidious.synopyta.org")))
|
||||
#+end_src
|
||||
|
||||
**** Some fixes
|
||||
At some point in the last 2 years, Invidious started to return videos with =null= fields. I have no idea what causes that, but I suspect it's related to YouTube Music.
|
||||
|
||||
=ytel= hasn't been updated in these two years, so it doesn't account for that change.
|
||||
|
||||
So, let's skip videos with null titles.
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/ytel-draw--buffer-nil-videos-fix ()
|
||||
(let ((inhibit-read-only t)
|
||||
(current-line (line-number-at-pos)))
|
||||
(erase-buffer)
|
||||
(setf header-line-format
|
||||
(concat "Search results for "
|
||||
(propertize ytel-search-term 'face 'ytel-video-published-face)
|
||||
", page "
|
||||
(number-to-string ytel-current-page)))
|
||||
(seq-do
|
||||
(lambda (v)
|
||||
(ytel--insert-video v)
|
||||
(insert "\n"))
|
||||
(seq-filter
|
||||
(lambda (v)
|
||||
(ytel-video-title v))
|
||||
ytel-videos))
|
||||
(goto-char (point-min))))
|
||||
|
||||
(with-eval-after-load 'ytel
|
||||
(advice-add #'ytel--draw-buffer :override #'my/ytel-draw--buffer-nil-videos-fix))
|
||||
#+end_src
|
||||
|
||||
And render other potentially =null= fields as "unknown".
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/ytel--format-unknown-fix (fun &rest args)
|
||||
(if (car args)
|
||||
(apply fun args)
|
||||
"unknown "))
|
||||
|
||||
(with-eval-after-load 'ytel
|
||||
(advice-add #'ytel--format-video-length :around #'my/ytel--format-unknown-fix)
|
||||
(advice-add #'ytel--format-video-published :around #'my/ytel--format-unknown-fix)
|
||||
(advice-add #'ytel--format-video-views :around #'my/ytel--format-unknown-fix))
|
||||
#+end_src
|
||||
**** Some functions
|
||||
Also, a function to copy a URL to the video under cursor.
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/ytel-kill-url ()
|
||||
(interactive)
|
||||
(kill-new
|
||||
(concat
|
||||
"https://www.youtube.com/watch?v="
|
||||
(ytel-video-id (ytel-get-current-video)))))
|
||||
#+end_src
|
||||
|
||||
*** EWW
|
||||
Emacs built-in web browser. +I wonder if anyone actually uses it.+
|
||||
|
||||
|
|
@ -10019,25 +10084,31 @@ The default UI is rather rough, but Nicolas Rougier's [[https://github.com/rougi
|
|||
(setq mastodon-active-user "sqrtminusone")
|
||||
(my/persp-add-rule mastodon-mode 0 "mastodon")
|
||||
;; Hide spoilers by default
|
||||
(setq-default mastodon-toot--content-warning t)
|
||||
;; (setq-default mastodon-toot--content-warning nil)
|
||||
(setq mastodon-media--avatar-height 40)
|
||||
(setq mastodon-tl--timeline-posts-count "40")
|
||||
(setq mastodon-tl--show-avatars t)
|
||||
(setq mastodon-tl--horiz-bar
|
||||
(make-string shr-max-width
|
||||
(if (char-displayable-p ?―) ?― ?-)))
|
||||
;; The default emojis take two characters for me
|
||||
(setq mastodon-tl--symbols
|
||||
'((reply "" . "R")
|
||||
(boost "" . "B")
|
||||
(favourite "" . "F")
|
||||
(bookmark "" . "K")
|
||||
(media "" . "[media]")
|
||||
(verified "" . "V")
|
||||
(locked "" . "[locked]")
|
||||
(private "" . "[followers]")
|
||||
(direct "" . "[direct]")
|
||||
(edited "" . "[edited]"))))
|
||||
(mapcar (lambda (item)
|
||||
(setf (alist-get (car item) mastodon-tl--symbols)
|
||||
(cdr item)))
|
||||
'((reply "" . "R")
|
||||
(boost "" . "B")
|
||||
(favourite "" . "F")
|
||||
(bookmark "" . "K")
|
||||
(media "" . "[media]")
|
||||
(verified "" . "V")
|
||||
(locked "" . "[locked]")
|
||||
(private "" . "[followers]")
|
||||
(direct "" . "[direct]")
|
||||
(edited "" . "[edited]"))))
|
||||
|
||||
(use-package mastodon-alt
|
||||
:straight (:host github :repo "rougier/mastodon-alt")
|
||||
:disabled t
|
||||
:after (mastodon)
|
||||
:config
|
||||
(mastodon-alt-tl-activate))
|
||||
|
|
@ -10201,12 +10272,15 @@ So here's a custom update function:
|
|||
(lambda (toot)
|
||||
(and
|
||||
(or (not hide-replies)
|
||||
;; Why is the original function inverted??
|
||||
(mastodon-tl--is-reply toot))
|
||||
(not (mastodon-tl--is-reply toot)))
|
||||
(or (not hide-boosts)
|
||||
(not (alist-get 'reblog toot)))))
|
||||
toots)))
|
||||
(mapc #'mastodon-tl--toot toots))))
|
||||
toots))
|
||||
(start-pos (point)))
|
||||
(mapc #'mastodon-tl--toot toots)
|
||||
(when mastodon-tl--display-media-p
|
||||
(save-excursion
|
||||
(mastodon-media--inline-images start-pos (point)))))))
|
||||
#+end_src
|
||||
|
||||
In order to use it, the function has to be passed to =mastodon-tl--init=:
|
||||
|
|
@ -10336,8 +10410,8 @@ And the prefix itself:
|
|||
("o" "Thread" mastodon-tl--thread)
|
||||
("w" "Browser" my/mastodon-toot--browse)
|
||||
("le" "List edits" mastodon-toot--view-toot-edits)
|
||||
("lf" "List favouriters" mastodon-toot--list-toot-favouriters)
|
||||
("lb" "List boosters" mastodon-toot--list-toot-boosters)]
|
||||
("lf" "List favouriters" mastodon-toot--list-favouriters)
|
||||
("lb" "List boosters" mastodon-toot--list-boosters)]
|
||||
["Toot Actions"
|
||||
:class transient-row
|
||||
("r" "Reply" mastodon-toot--reply)
|
||||
|
|
@ -10577,14 +10651,16 @@ Or you can load up Element for a moment to see what the mention was, if that's e
|
|||
*** Telega
|
||||
[[https://github.com/zevlg/telega.el/][telega.el]] is a Telegam client for Emacs.
|
||||
|
||||
| Guix dependency |
|
||||
|-------------------|
|
||||
| tdlib-1.8.16 |
|
||||
| font-gnu-unifont |
|
||||
| font-gnu-freefont |
|
||||
| Guix dependency |
|
||||
|--------------------|
|
||||
| emacs-telega-sever |
|
||||
| font-gnu-unifont |
|
||||
| font-gnu-freefont |
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(use-package telega
|
||||
;; :straight (:type built-in)
|
||||
;; For now emacs-telega-server is compatible with the latest telega.el
|
||||
:straight t
|
||||
:if (not (or my/remote-server))
|
||||
:commands (telega)
|
||||
|
|
@ -10596,6 +10672,10 @@ Or you can load up Element for a moment to see what the mention was, if that's e
|
|||
(telega-webpage-chat-link :foreground (my/color-value 'base0)
|
||||
:background (my/color-value 'fg)))
|
||||
:config
|
||||
(when (file-directory-p "~/.guix-extra-profiles/emacs/")
|
||||
(setq telega-server-command
|
||||
(expand-file-name
|
||||
"~/.guix-extra-profiles/emacs/emacs/bin/telega-server")))
|
||||
(setq telega-emoji-use-images nil)
|
||||
(setq telega-chat-fill-column 80)
|
||||
(setq telega-completing-read-function #'completing-read)
|
||||
|
|
@ -10661,7 +10741,7 @@ Configuring company backends for the chat buffer, as recommended in the manual:
|
|||
(company-mode 1)
|
||||
(setopt visual-fill-column-width
|
||||
(+ telega-chat-fill-column
|
||||
(if (display-graphic-p) 4 5)))
|
||||
(if (display-graphic-p) 5 6)))
|
||||
(setq-local split-width-threshold 1))
|
||||
(add-hook 'telega-chat-mode-hook #'my/telega-chat-setup)
|
||||
#+end_src
|
||||
|
|
@ -10777,10 +10857,9 @@ References:
|
|||
(setq biome-api-try-parse-error-as-response t))
|
||||
:config
|
||||
(add-to-list 'biome-query-coords
|
||||
'("Saint-Petersburg, Russia" 59.93863 30.31413))
|
||||
'("Saint-Petersburg, Russia" 59.942651 30.229930))
|
||||
(add-to-list 'biome-query-coords
|
||||
'("Tyumen, Russia" 57.15222 65.52722)))
|
||||
|
||||
#+end_src
|
||||
** Reading documentation
|
||||
*** tldr
|
||||
|
|
@ -10914,7 +10993,7 @@ There is a package called =devdocs= that does more or less the same, but I like
|
|||
(add-hook 'sx-question-list-mode-hook #'doom-modeline-mode))
|
||||
#+end_src
|
||||
** Not-an-AI
|
||||
Workflows, which are sometimes referred as "AI", go in here.
|
||||
Workflows, which are sometimes referred to as "AI", go in here.
|
||||
|
||||
I'm technically writing a PhD on a related topic, so I'm a bit more receptive towards the whole thing than most of the community. But I'm still not calling it AI.
|
||||
|
||||
|
|
@ -10939,20 +11018,31 @@ I don't have access to any proprietary APIs, but LLaMA 3.1 8b with [[https://oll
|
|||
(setq gptel-backend (gptel-make-ollama "Ollama"
|
||||
:host "localhost:11434"
|
||||
:stream t
|
||||
:models '("llama3.1:latest" "llama3.1:instruct")))
|
||||
:models '("llama3.1:8b" "deepseek-r1:32b"
|
||||
"qwen2.5:32b" "qwen2.5-coder:32b"
|
||||
"eva-qwen2.5-q4_k_l-32b:latest"
|
||||
"t-pro-1.0-q4_k_m:latest")))
|
||||
(gptel-make-openai "OpenRouter"
|
||||
:host "openrouter.ai/api"
|
||||
:key (lambda () (my/password-store-get-field
|
||||
"My_Online/Accounts/openrouter" "api-key"))
|
||||
:stream t
|
||||
:models '("anthropic/claude-3.5-haiku"))
|
||||
(setq gptel--known-backends
|
||||
(seq-filter
|
||||
(lambda (cell)
|
||||
(not (equal (car cell) "ChatGPT")))
|
||||
gptel--known-backends))
|
||||
(setq gptel-response-prefix-alist
|
||||
'((markdown-mode . "[Response] ")
|
||||
(org-mode . "*** Response: ")
|
||||
(text-mode . "[Response]")))
|
||||
|
||||
;; (my/gptel-switch-backend "llama3.1:latest")
|
||||
(general-define-key
|
||||
:keymaps '(gptel-mode-map)
|
||||
:states '(insert normal)
|
||||
"C-<return>" 'gptel-send)
|
||||
(general-define-key
|
||||
:keymaps '(gptel-mode-map)
|
||||
:states '(normal)
|
||||
"?" #'gptel-menu)
|
||||
(gptel-make-gemini "Gemini"
|
||||
:key (my/password-store-get-field "My_Online/Accounts/google-gemini" "api")
|
||||
:stream t))
|
||||
"C-<return>" 'gptel-send
|
||||
"M-o" #'gptel-menu))
|
||||
#+end_src
|
||||
|
||||
**** ellama
|
||||
|
|
@ -10968,19 +11058,26 @@ I don't have access to any proprietary APIs, but LLaMA 3.1 8b with [[https://oll
|
|||
(require 'llm-ollama)
|
||||
;; I've looked for this option for 1.5 hours
|
||||
(setq ellama-long-lines-length 100000)
|
||||
(my-leader-def
|
||||
"aie" '(:wk "ellama" :keymap ellama-command-map))
|
||||
|
||||
(setq ellama-provider (make-llm-ollama
|
||||
:chat-model "llama3.1:instruct"
|
||||
:embedding-model "llama3.1:instruct"))
|
||||
:chat-model "qwen2.5:32b"
|
||||
:embedding-model "qwen2.5:32b"))
|
||||
(setq ellama-coding-provider (make-llm-ollama
|
||||
:chat-model "qwen2.5-coder:32b"
|
||||
:embedding-model "qwen2.5-coder:32b"))
|
||||
(setq ellama-providers
|
||||
`(("llama3.1:8b" . ,(make-llm-ollama
|
||||
:chat-model "llama3.1:latest"
|
||||
:embedding-model "llama3.1:latest"))
|
||||
("llama3.1:instruct" . ,(make-llm-ollama
|
||||
:chat-model "llama3.1:instruct"
|
||||
:embedding-model "llama3.1:instruct")))))
|
||||
:chat-model "llama3.1:latest"
|
||||
:embedding-model "llama3.1:latest"))
|
||||
("phi4:latest" . ,(make-llm-ollama
|
||||
:chat-model "phi4:latest"
|
||||
:embedding-model "phi4:latest"))
|
||||
("qwen2.5:32b" . ,(make-llm-ollama
|
||||
:chat-model "qwen2.5:32b"
|
||||
:embedding-model "qwen2.5:32b"))
|
||||
("qwen2.5-coder:32b" . ,(make-llm-ollama
|
||||
:chat-model "qwen2.5-coder:32b"
|
||||
:embedding-model "qwen2.5-coder:32b")))))
|
||||
#+end_src
|
||||
|
||||
The keybindings are a bit crazy to use even with =which-key=, so here goes transient.el.
|
||||
|
|
@ -11000,9 +11097,7 @@ The keybindings are a bit crazy to use even with =which-key=, so here goes trans
|
|||
("ci" "Improve" ellama-code-improve)]
|
||||
["Natural Language"
|
||||
:class transient-row
|
||||
("np" "Proof-read" my/ellama-proof-read)
|
||||
("nw" "Improve wording" my/ellama-improve-wording)
|
||||
("nc" "Improve conciseness" my/ellama-improve-concise)]
|
||||
("np" "Proof-read" my/ellama-proof-read)]
|
||||
["Formatting"
|
||||
:class transient-row
|
||||
("ff" "Format" ellama-make-format)
|
||||
|
|
@ -11027,7 +11122,6 @@ The keybindings are a bit crazy to use even with =which-key=, so here goes trans
|
|||
("sr" "Rename ression" ellama-session-rename)
|
||||
("sd" "Delete session" ellama-session-remove)]))
|
||||
|
||||
|
||||
(defun my/ellama ()
|
||||
(interactive)
|
||||
(require 'ellama)
|
||||
|
|
@ -11036,13 +11130,13 @@ The keybindings are a bit crazy to use even with =which-key=, so here goes trans
|
|||
(my-leader-def "aie" #'my/ellama)
|
||||
#+end_src
|
||||
|
||||
**** Change natural text & diff against the results
|
||||
**** Change natural-language text & diff against the results
|
||||
One pattern I often want is to change the given text and compare it to the old version.
|
||||
|
||||
LLMs aren't perfectly good at saying what changes they have done, so the pattern here is to query the model and show the changed text together with the diff.
|
||||
|
||||
So first, I need to diff two strings.
|
||||
#+begin_src emacs-lisp :noweb yes
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/diff-strings (str1 str2)
|
||||
(let ((file1 (make-temp-file "diff1"))
|
||||
(file2 (make-temp-file "diff2")))
|
||||
|
|
@ -11062,22 +11156,30 @@ So first, I need to diff two strings.
|
|||
#+end_src
|
||||
|
||||
And the function to do the prompting iself. Llama tends to output in Markdown, so I use a function from Ellama to convert the output back to Org-mode, if necessary.
|
||||
#+begin_src emacs-lisp :noweb yes
|
||||
(defun my/ellama-text-with-diff (text is-org-mode prompt)
|
||||
(require 'ellama)
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/ellama-proof-read--display (text is-org-mode prompt)
|
||||
(llm-chat-async
|
||||
ellama-provider
|
||||
(llm-make-chat-prompt
|
||||
(format prompt text))
|
||||
(lambda (changed-text)
|
||||
(when is-org-mode
|
||||
(setq changed-text (ellama--translate-markdown-to-org-filter changed-text)))
|
||||
(let ((buffer (generate-new-buffer "*ellama-diff*")))
|
||||
(lambda (response)
|
||||
(let* ((parts (split-string response "-FIXED TEXT ENDS-"))
|
||||
(changed-text (nth 0 parts))
|
||||
(comments (nth 1 parts))
|
||||
(buffer (generate-new-buffer "*ellama-diff*")))
|
||||
(when is-org-mode
|
||||
(setq changed-text (ellama--translate-markdown-to-org-filter changed-text)))
|
||||
(with-current-buffer buffer
|
||||
(text-mode)
|
||||
(insert changed-text)
|
||||
(insert "\n\n")
|
||||
(insert (my/diff-strings text changed-text)))
|
||||
(insert
|
||||
(propertize "Changed text:\n" 'face 'transient-heading)
|
||||
(string-trim changed-text)
|
||||
"\n\n"
|
||||
(propertize "Comments:\n" 'face 'transient-heading)
|
||||
(string-trim comments)
|
||||
"\n\n"
|
||||
(propertize "Diff:\n" 'face 'transient-heading)
|
||||
(my/diff-strings text changed-text)))
|
||||
(display-buffer buffer)))
|
||||
(lambda (&rest err)
|
||||
(message "Error: %s" err))))
|
||||
|
|
@ -11086,7 +11188,18 @@ And the function to do the prompting iself. Llama tends to output in Markdown, s
|
|||
As for prompts, I like the following prompt to proof-read text. It's pretty conservative, but good for fixing typos, missing commas, articles, etc.
|
||||
#+begin_src emacs-lisp
|
||||
(setq my/ellama-proof-read-prompt
|
||||
"Proof-read the following text. Fix any errors but keep the original style and punctuation, including linebreaks. Print the changed text and nothing else, not even \"Here's the proof-read text\".\n\n %s")
|
||||
"Proof-read the following text. Follow these rules:
|
||||
- Fix all grammar errors
|
||||
- Keep the original style and punctuation, including linebreaks.
|
||||
- Use British spelling
|
||||
- Do not replace ' with ’, and do not touch other such symbols
|
||||
|
||||
Output the following and nothing else:
|
||||
- The fixed text
|
||||
- The string -FIXED TEXT ENDS-
|
||||
- List of found errors
|
||||
- List of style suggestions
|
||||
%s")
|
||||
|
||||
(defun my/ellama--text ()
|
||||
(if (region-active-p)
|
||||
|
|
@ -11095,27 +11208,8 @@ As for prompts, I like the following prompt to proof-read text. It's pretty cons
|
|||
|
||||
(defun my/ellama-proof-read (text is-org-mode)
|
||||
(interactive (list (my/ellama--text) (derived-mode-p 'org-mode)))
|
||||
(my/ellama-text-with-diff text is-org-mode my/ellama-proof-read-prompt))
|
||||
#+end_src
|
||||
|
||||
The following is more expansive, but preserves less of the original text. For instance, it tends to replace my /id est/ and /exempli gratia/. But sometimes it has good ideas.
|
||||
#+begin_src emacs-lisp
|
||||
(setq my/ellama-improve-wording-prompt
|
||||
"Proof-read the following text. Fix any errors and improve wording. Print the changed text and nothing else, not even \"Here's the improved text\".\n\n %s")
|
||||
|
||||
(defun my/ellama-improve-wording (text is-org-mode)
|
||||
(interactive (list (my/ellama--text) (derived-mode-p 'org-mode)))
|
||||
(my/ellama-text-with-diff text is-org-mode my/ellama-improve-wording-prompt))
|
||||
#+end_src
|
||||
|
||||
Also, a prompt to make a text more concise.
|
||||
#+begin_src emacs-lisp
|
||||
(setq my/ellama-improve-concise-prompt
|
||||
"Make the following text more concise. Print the changed text and nothing else, not even \"Here's the improved text\".\n\n %s")
|
||||
|
||||
(defun my/ellama-improve-concise (text is-org-mode)
|
||||
(interactive (list (my/ellama--text) (derived-mode-p 'org-mode)))
|
||||
(my/ellama-text-with-diff text is-org-mode my/ellama-improve-concise-prompt))
|
||||
(require 'ellama)
|
||||
(my/ellama-proof-read--display text is-org-mode my/ellama-proof-read-prompt))
|
||||
#+end_src
|
||||
|
||||
*** Podcast transcripts
|
||||
|
|
@ -11148,11 +11242,13 @@ First, some functions to process the output. These take a JSON formed by =insane
|
|||
|
||||
#+begin_src emacs-lisp
|
||||
(defun my/whisper--format-vtt-seconds (seconds)
|
||||
(let* ((hours (/ (floor seconds) (* 60 60)))
|
||||
(minutes (/ (- (floor seconds) (* hours 60 60)) 60))
|
||||
(sec (% (floor seconds) 60))
|
||||
(ms (floor (* 1000 (- seconds (floor seconds))))))
|
||||
(format "%.2d:%.2d:%.2d.%.3d" hours minutes sec ms)))
|
||||
(if (numberp seconds)
|
||||
(let* ((hours (/ (floor seconds) (* 60 60)))
|
||||
(minutes (/ (- (floor seconds) (* hours 60 60)) 60))
|
||||
(sec (% (floor seconds) 60))
|
||||
(ms (floor (* 1000 (- seconds (floor seconds))))))
|
||||
(format "%.2d:%.2d:%.2d.%.3d" hours minutes sec ms))
|
||||
""))
|
||||
|
||||
(defun my/whisper--save-chucks-vtt (path data)
|
||||
(with-temp-file path
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue