Consolidates container builds from colonelpanic-org-agenda-api repo: - Add org-agenda-api input to nixos flake - Add container-colonelpanic and container-kat package outputs - Add org-agenda-api cachix as substituter - Add org-agenda-api devShell for deployment work New org-agenda-api directory contains: - container.nix: Container build logic using mkContainer - configs/: Instance configs (custom-config.el, fly.toml, secrets) - deploy.sh: Fly.io deployment script - secrets.nix: agenix secret declarations Build with: nix build .#container-colonelpanic Deploy with: cd org-agenda-api && ./deploy.sh colonelpanic Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
172 lines
7.8 KiB
EmacsLisp
172 lines
7.8 KiB
EmacsLisp
;;; custom-config.el --- Container config loader -*- lexical-binding: t; -*-
|
|
|
|
;; Helper function used by org-config (must be defined before loading preface)
|
|
(defun imalison:join-paths (&rest paths)
|
|
"Join PATHS together into a single path."
|
|
(let ((result (car paths)))
|
|
(dolist (p (cdr paths))
|
|
(setq result (expand-file-name p result)))
|
|
result))
|
|
|
|
;; Load tangled config files in order
|
|
(let ((config-dir (file-name-directory load-file-name)))
|
|
;; Load preface first (defines variables with default values)
|
|
(when (file-exists-p (expand-file-name "org-config-preface.el" config-dir))
|
|
(load (expand-file-name "org-config-preface.el" config-dir)))
|
|
|
|
;; Override paths for container environment AFTER loading preface
|
|
;; Use setq to ensure we override the defvar values from preface
|
|
(setq imalison:org-dir "/data/org")
|
|
(setq imalison:shared-org-dir "/data/shared")
|
|
|
|
;; Re-derive all path variables using the container org-dir
|
|
(setq imalison:org-gtd-file (imalison:join-paths imalison:org-dir "gtd.org"))
|
|
(setq imalison:org-habits-file (imalison:join-paths imalison:org-dir "habits.org"))
|
|
(setq imalison:org-calendar-file (imalison:join-paths imalison:org-dir "calendar.org"))
|
|
(setq imalison:org-inbox-file (imalison:join-paths imalison:org-dir "inbox.org"))
|
|
|
|
;; Shared paths derived from shared-org-dir
|
|
(setq imalison:shared-org-gtd-file (imalison:join-paths imalison:shared-org-dir "gtd.org"))
|
|
(setq imalison:shared-habits-file (imalison:join-paths imalison:shared-org-dir "habits.org"))
|
|
(setq imalison:shared-calendar-file (imalison:join-paths imalison:shared-org-dir "calendar.org"))
|
|
(setq imalison:shared-shopping-file (imalison:join-paths imalison:shared-org-dir "shopping.org"))
|
|
(setq imalison:shared-repeating-file (imalison:join-paths imalison:shared-org-dir "repeating.org"))
|
|
(setq imalison:orgzly-files (list (imalison:join-paths imalison:org-dir "orgzly.org")))
|
|
(setq imalison:repeating-org-files (list imalison:org-habits-file
|
|
imalison:shared-habits-file
|
|
imalison:shared-repeating-file))
|
|
|
|
;; org-config-custom.el uses customize format (var value), convert to setq
|
|
(when (file-exists-p (expand-file-name "org-config-custom.el" config-dir))
|
|
(with-temp-buffer
|
|
(insert-file-contents (expand-file-name "org-config-custom.el" config-dir))
|
|
(goto-char (point-min))
|
|
(condition-case nil
|
|
(while t
|
|
(let ((form (read (current-buffer))))
|
|
(when (and (listp form) (symbolp (car form)))
|
|
(set (car form) (eval (cadr form))))))
|
|
(end-of-file nil))))
|
|
|
|
;; Load main config (sets up org-agenda-files using the variables we just set)
|
|
(when (file-exists-p (expand-file-name "org-config-config.el" config-dir))
|
|
(load (expand-file-name "org-config-config.el" config-dir)))
|
|
|
|
;; Load optional overrides (instance-specific customizations)
|
|
(when (file-exists-p (expand-file-name "overrides.el" config-dir))
|
|
(load (expand-file-name "overrides.el" config-dir))))
|
|
|
|
;; Define no-op stubs for unavailable packages (overwrite autoloads)
|
|
(defun org-bullets-mode (&optional _arg)
|
|
"No-op stub for org-bullets-mode (package not available in container)."
|
|
nil)
|
|
|
|
;; Override shared-org-file-p to handle nil imalison:shared-org-dir
|
|
;; The original calls (file-truename imalison:shared-org-dir) which errors when nil
|
|
(defun imalison:shared-org-file-p ()
|
|
"Check if current file is in the shared org directory.
|
|
Returns nil if imalison:shared-org-dir is not set."
|
|
(and imalison:shared-org-dir
|
|
(string-prefix-p (file-truename imalison:shared-org-dir)
|
|
(file-truename default-directory))))
|
|
|
|
;; Helper functions used by org-agenda-custom-commands
|
|
;; These are defined in README.org but needed for custom views
|
|
(defun imalison:compare-int-list (a b)
|
|
"Compare two lists of integers lexicographically."
|
|
(when (and a b)
|
|
(cond ((> (car a) (car b)) 1)
|
|
((< (car a) (car b)) -1)
|
|
(t (imalison:compare-int-list (cdr a) (cdr b))))))
|
|
|
|
(defun get-date-created-from-agenda-entry (agenda-entry)
|
|
"Get the CREATED property timestamp from an agenda entry."
|
|
(org-time-string-to-time
|
|
(org-entry-get (get-text-property 1 'org-marker agenda-entry) "CREATED")))
|
|
|
|
;; Auto-convert org-capture-templates to org-agenda-api-capture-templates
|
|
(defun imalison:extract-template-string (template-spec)
|
|
"Extract the template string from TEMPLATE-SPEC.
|
|
Handles string templates, function templates, and file templates."
|
|
(let ((template-part (nth 4 template-spec)))
|
|
(cond
|
|
((stringp template-part) template-part)
|
|
((and (listp template-part)
|
|
(eq (car template-part) 'function))
|
|
;; Try to evaluate the function to get template string
|
|
(condition-case nil
|
|
(let ((result (funcall (eval (cadr template-part)))))
|
|
(if (stringp result) result ""))
|
|
(error "")))
|
|
((and (listp template-part)
|
|
(eq (car template-part) 'file))
|
|
;; File template - read file contents
|
|
(condition-case nil
|
|
(with-temp-buffer
|
|
(insert-file-contents (eval (cadr template-part)))
|
|
(buffer-string))
|
|
(error "")))
|
|
(t ""))))
|
|
|
|
(defun imalison:infer-prompt-type (prompt-match)
|
|
"Infer the prompt type from PROMPT-MATCH.
|
|
PROMPT-MATCH is the full match string like \"%^{Name}t\" or \"%^{Title}\"."
|
|
(let ((suffix (substring prompt-match (1- (length prompt-match)))))
|
|
(cond
|
|
((string-match-p "[tTuU]$" prompt-match) 'date)
|
|
((string= "g" suffix) 'tag)
|
|
((string= "G" suffix) 'tag)
|
|
((string= "C" suffix) 'string) ; completion
|
|
(t 'string))))
|
|
|
|
(defun imalison:extract-prompts-from-template (template-string)
|
|
"Extract prompt definitions from TEMPLATE-STRING.
|
|
Returns a list of (NAME :type TYPE :required t) for each %^{...} found."
|
|
(let ((prompts '())
|
|
(seen-names '())
|
|
(pos 0))
|
|
(while (string-match "%\\^{\\([^}|]+\\)\\(?:|[^}]*\\)?}\\([tTuUgGC]\\)?" template-string pos)
|
|
(let* ((name (match-string 1 template-string))
|
|
(full-match (match-string 0 template-string))
|
|
(type (imalison:infer-prompt-type full-match)))
|
|
(unless (member name seen-names)
|
|
(push name seen-names)
|
|
(push (list name :type type :required t) prompts))
|
|
(setq pos (match-end 0))))
|
|
(nreverse prompts)))
|
|
|
|
(defun imalison:convert-capture-template (template-spec)
|
|
"Convert a single org-capture TEMPLATE-SPEC to API format.
|
|
Returns nil for non-entry templates or templates that can't be converted."
|
|
(when (and (listp template-spec)
|
|
(>= (length template-spec) 4))
|
|
(let* ((key (nth 0 template-spec))
|
|
(description (nth 1 template-spec))
|
|
(type (nth 2 template-spec)))
|
|
;; Only convert entry-type templates, skip menu items (no type)
|
|
(when (and (stringp key)
|
|
(stringp description)
|
|
(eq type 'entry))
|
|
(let* ((template-string (imalison:extract-template-string template-spec))
|
|
(prompts (imalison:extract-prompts-from-template template-string))
|
|
;; Create a unique API key from the hotkey
|
|
(api-key (concat "capture-" key)))
|
|
(list api-key
|
|
:name description
|
|
:template template-spec
|
|
:prompts prompts))))))
|
|
|
|
(defun imalison:convert-all-capture-templates ()
|
|
"Convert all org-capture-templates to org-agenda-api-capture-templates format."
|
|
(let ((converted '()))
|
|
(dolist (template org-capture-templates)
|
|
(let ((api-template (imalison:convert-capture-template template)))
|
|
(when api-template
|
|
(push api-template converted))))
|
|
(nreverse converted)))
|
|
|
|
;; Auto-generate API capture templates from org-capture-templates
|
|
(setq org-agenda-api-capture-templates (imalison:convert-all-capture-templates))
|
|
|
|
;;; custom-config.el ends here
|