This commit is contained in:
4
.obsidian/community-plugins.json
vendored
Normal file
4
.obsidian/community-plugins.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[
|
||||||
|
"obsidian-excalidraw-plugin",
|
||||||
|
"dataview"
|
||||||
|
]
|
||||||
20876
.obsidian/plugins/dataview/main.js
vendored
Normal file
20876
.obsidian/plugins/dataview/main.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11
.obsidian/plugins/dataview/manifest.json
vendored
Normal file
11
.obsidian/plugins/dataview/manifest.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"id": "dataview",
|
||||||
|
"name": "Dataview",
|
||||||
|
"version": "0.5.68",
|
||||||
|
"minAppVersion": "0.13.11",
|
||||||
|
"description": "Complex data views for the data-obsessed.",
|
||||||
|
"author": "Michael Brenan <blacksmithgu@gmail.com>",
|
||||||
|
"authorUrl": "https://github.com/blacksmithgu",
|
||||||
|
"helpUrl": "https://blacksmithgu.github.io/obsidian-dataview/",
|
||||||
|
"isDesktopOnly": false
|
||||||
|
}
|
||||||
141
.obsidian/plugins/dataview/styles.css
vendored
Normal file
141
.obsidian/plugins/dataview/styles.css
vendored
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
.block-language-dataview {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************/
|
||||||
|
/** Table Views **/
|
||||||
|
/*****************/
|
||||||
|
|
||||||
|
/* List View Default Styling; rendered internally as a table. */
|
||||||
|
.table-view-table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-view-table > thead > tr, .table-view-table > tbody > tr {
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-view-table > tbody > tr:hover {
|
||||||
|
background-color: var(--table-row-background-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-view-table > thead > tr > th {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: larger;
|
||||||
|
border-top: none;
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
border-bottom: solid;
|
||||||
|
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-view-table > tbody > tr > td {
|
||||||
|
text-align: left;
|
||||||
|
border: none;
|
||||||
|
font-weight: 400;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-view-table ul, .table-view-table ol {
|
||||||
|
margin-block-start: 0.2em !important;
|
||||||
|
margin-block-end: 0.2em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Rendered value styling for any view. */
|
||||||
|
.dataview-result-list-root-ul {
|
||||||
|
padding: 0em !important;
|
||||||
|
margin: 0em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataview-result-list-ul {
|
||||||
|
margin-block-start: 0.2em !important;
|
||||||
|
margin-block-end: 0.2em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generic grouping styling. */
|
||||||
|
.dataview.result-group {
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************/
|
||||||
|
/** Inline Fields **/
|
||||||
|
/*******************/
|
||||||
|
|
||||||
|
.dataview.inline-field-key {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
font-family: var(--font-monospace);
|
||||||
|
background-color: var(--background-primary-alt);
|
||||||
|
color: var(--nav-item-color-selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataview.inline-field-value {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
font-family: var(--font-monospace);
|
||||||
|
background-color: var(--background-secondary-alt);
|
||||||
|
color: var(--nav-item-color-selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataview.inline-field-standalone-value {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
font-family: var(--font-monospace);
|
||||||
|
background-color: var(--background-secondary-alt);
|
||||||
|
color: var(--nav-item-color-selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************/
|
||||||
|
/** Task View **/
|
||||||
|
/***************/
|
||||||
|
|
||||||
|
.dataview.task-list-item, .dataview.task-list-basic-item {
|
||||||
|
margin-top: 3px;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
transition: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataview.task-list-item:hover, .dataview.task-list-basic-item:hover {
|
||||||
|
background-color: var(--text-selection);
|
||||||
|
box-shadow: -40px 0 0 var(--text-selection);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************/
|
||||||
|
/** Error Views **/
|
||||||
|
/*****************/
|
||||||
|
|
||||||
|
div.dataview-error-box {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 150px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 4px dashed var(--background-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataview-error-message {
|
||||||
|
color: var(--text-muted);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************/
|
||||||
|
/** Additional Metadata **/
|
||||||
|
/*************************/
|
||||||
|
|
||||||
|
.dataview.small-text {
|
||||||
|
font-size: smaller;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataview.small-text::before {
|
||||||
|
content: "(";
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataview.small-text::after {
|
||||||
|
content: ")";
|
||||||
|
}
|
||||||
1082
.obsidian/plugins/obsidian-excalidraw-plugin/data.json
vendored
Normal file
1082
.obsidian/plugins/obsidian-excalidraw-plugin/data.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
10
.obsidian/plugins/obsidian-excalidraw-plugin/main.js
vendored
Normal file
10
.obsidian/plugins/obsidian-excalidraw-plugin/main.js
vendored
Normal file
File diff suppressed because one or more lines are too long
12
.obsidian/plugins/obsidian-excalidraw-plugin/manifest.json
vendored
Normal file
12
.obsidian/plugins/obsidian-excalidraw-plugin/manifest.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"id": "obsidian-excalidraw-plugin",
|
||||||
|
"name": "Excalidraw",
|
||||||
|
"version": "2.23.8",
|
||||||
|
"minAppVersion": "1.8.7",
|
||||||
|
"description": "Sketch Your Mind. Edit and view Excalidraw drawings. Enter the world of 4D Visual PKM.",
|
||||||
|
"author": "Zsolt Viczian",
|
||||||
|
"authorUrl": "https://excalidraw-obsidian.online",
|
||||||
|
"fundingUrl": "https://ko-fi.com/zsolt",
|
||||||
|
"helpUrl": "https://github.com/zsviczian/obsidian-excalidraw-plugin#readme",
|
||||||
|
"isDesktopOnly": false
|
||||||
|
}
|
||||||
1
.obsidian/plugins/obsidian-excalidraw-plugin/styles.css
vendored
Normal file
1
.obsidian/plugins/obsidian-excalidraw-plugin/styles.css
vendored
Normal file
File diff suppressed because one or more lines are too long
46
.obsidian/workspace.json
vendored
46
.obsidian/workspace.json
vendored
@@ -13,12 +13,12 @@
|
|||||||
"state": {
|
"state": {
|
||||||
"type": "markdown",
|
"type": "markdown",
|
||||||
"state": {
|
"state": {
|
||||||
"file": "Career/CI-CD Summary.md",
|
"file": "Daily Notes.md",
|
||||||
"mode": "source",
|
"mode": "source",
|
||||||
"source": false
|
"source": false
|
||||||
},
|
},
|
||||||
"icon": "lucide-file",
|
"icon": "lucide-file",
|
||||||
"title": "CI-CD Summary"
|
"title": "Daily Notes"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
"state": {
|
"state": {
|
||||||
"type": "backlink",
|
"type": "backlink",
|
||||||
"state": {
|
"state": {
|
||||||
"file": "Career/CI-CD Summary.md",
|
"file": "Daily Notes.md",
|
||||||
"collapseAll": false,
|
"collapseAll": false,
|
||||||
"extraContext": false,
|
"extraContext": false,
|
||||||
"sortOrder": "alphabetical",
|
"sortOrder": "alphabetical",
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
"unlinkedCollapsed": true
|
"unlinkedCollapsed": true
|
||||||
},
|
},
|
||||||
"icon": "links-coming-in",
|
"icon": "links-coming-in",
|
||||||
"title": "Backlinks for CI-CD Summary"
|
"title": "Backlinks for Daily Notes"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -178,37 +178,39 @@
|
|||||||
"daily-notes:Open today's daily note": false,
|
"daily-notes:Open today's daily note": false,
|
||||||
"templates:Insert template": false,
|
"templates:Insert template": false,
|
||||||
"command-palette:Open command palette": false,
|
"command-palette:Open command palette": false,
|
||||||
"bases:Create new base": false
|
"bases:Create new base": false,
|
||||||
|
"obsidian-excalidraw-plugin:New drawing": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"active": "0aa065275eb81420",
|
"active": "0aa065275eb81420",
|
||||||
"lastOpenFiles": [
|
"lastOpenFiles": [
|
||||||
|
"Technical/Emacs/Org Roam.md",
|
||||||
|
"Technical/Emacs/Org Publish.md",
|
||||||
|
"Technical/Emacs/Org Noter.md",
|
||||||
|
"Technical/Emacs/Magit.md",
|
||||||
|
"Technical/Emacs/Keybindings Emacs.md",
|
||||||
|
"Technical/Emacs/GTD.md",
|
||||||
|
"Technical/Emacs/Evil Mode.md",
|
||||||
|
"Technical/Emacs/Emacs MOC.md",
|
||||||
|
"Technical/Emacs/Emacs Config.md",
|
||||||
|
"Technical/Emacs/Emacs Calendar.md",
|
||||||
|
"Technical/Emacs/Elisp.md",
|
||||||
|
"Daily Notes.md",
|
||||||
|
"Index.md",
|
||||||
|
"Career/20260513142352-aritificial_intelligence.md",
|
||||||
|
"Technical/Emacs",
|
||||||
|
"Technical",
|
||||||
|
"Career/CI-CD Summary.md",
|
||||||
"Career/Microlise/Microlise MOC.md",
|
"Career/Microlise/Microlise MOC.md",
|
||||||
"Career/Microlise/ESS ESP/Microlise - ESS.md",
|
"Career/Microlise/ESS ESP/Microlise - ESS.md",
|
||||||
"Career/Concepts.md",
|
"Career/Concepts.md",
|
||||||
"Career/Big (O) - Time and Space Complexity.md",
|
"Career/Big (O) - Time and Space Complexity.md",
|
||||||
"Career/Microlise/Microlise Assessment.md",
|
"Career/Microlise/Microlise Assessment.md",
|
||||||
"Career/Microlise/Session Stored XSS PENTEST.md",
|
"Career/Microlise/Session Stored XSS PENTEST.md",
|
||||||
"Index.md",
|
|
||||||
"Career/Career MOC.md",
|
"Career/Career MOC.md",
|
||||||
"Daily Notes.md",
|
|
||||||
"Career/Test Driven Development.md",
|
"Career/Test Driven Development.md",
|
||||||
"Career/Windows Services.md",
|
"Career/Windows Services.md",
|
||||||
"Career/Solid Principles.md",
|
"Career/Solid Principles.md",
|
||||||
"Career/Software Development Methodologies.md",
|
|
||||||
"Career/Restful API.md",
|
|
||||||
"Career/Powershell MOC.md",
|
|
||||||
"Career/MVP + MVT.md",
|
|
||||||
"Career/Job applications.md",
|
|
||||||
"Career/Java Portswrigger Test.md",
|
|
||||||
"Career/DLL's.md",
|
|
||||||
"Career/Design Patterns.md",
|
|
||||||
"Career/Data camp AI training.md",
|
|
||||||
"Career/Cross Site Scripting (XSS).md",
|
|
||||||
"Career/CI-CD Summary.md",
|
|
||||||
"Career/CI-CD Example (site visits).md",
|
|
||||||
"Career/Bowling Kata.md",
|
|
||||||
"Career/ASP.NET Core Web API Fundamental Notes.md",
|
|
||||||
"Career/Database - SQL",
|
"Career/Database - SQL",
|
||||||
"Career/20260305130457-database_perms_roles_accounts.org",
|
"Career/20260305130457-database_perms_roles_accounts.org",
|
||||||
"Career/Attachments/APIOps.png",
|
"Career/Attachments/APIOps.png",
|
||||||
@@ -227,8 +229,6 @@
|
|||||||
"home/zaine/master-folder/CV's/Cover Letter/Santec",
|
"home/zaine/master-folder/CV's/Cover Letter/Santec",
|
||||||
"home/zaine/master-folder/CV's/Cover Letter",
|
"home/zaine/master-folder/CV's/Cover Letter",
|
||||||
"home/zaine/master-folder/CV's",
|
"home/zaine/master-folder/CV's",
|
||||||
"home/zaine/master-folder",
|
|
||||||
"home/zaine",
|
|
||||||
"Untitled.canvas"
|
"Untitled.canvas"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -2,28 +2,9 @@
|
|||||||
|
|
||||||
All daily journal entries, linked automatically.
|
All daily journal entries, linked automatically.
|
||||||
|
|
||||||
[[2026-04-14]]
|
```dataview
|
||||||
[[2026-04-18]]
|
LIST
|
||||||
[[2026-04-20]]
|
FROM "Dailies"
|
||||||
[[2026-04-21]]
|
WHERE regexmatch("^\\d{4}-\\d{2}-\\d{2}$", file.name)
|
||||||
[[2026-04-28]]
|
SORT file.name ASC
|
||||||
[[2026-04-29]]
|
```
|
||||||
[[2026-04-30]]
|
|
||||||
[[2026-05-01]]
|
|
||||||
[[2026-05-05]]
|
|
||||||
[[2026-05-06]]
|
|
||||||
[[2026-05-12]]
|
|
||||||
[[2026-05-13]]
|
|
||||||
[[2026-05-20]]
|
|
||||||
[[2026-05-21]]
|
|
||||||
[[2026-05-22]]
|
|
||||||
[[2026-05-28]]
|
|
||||||
[[2026-05-29]]
|
|
||||||
[[2026-06-01]]
|
|
||||||
[[2026-06-02]]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
63
Technical/Emacs/Elisp.md
Normal file
63
Technical/Emacs/Elisp.md
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
– use `<s` followed by `TAB`
|
||||||
|
|
||||||
|
``` elisp
|
||||||
|
(defun my-echo-input ()
|
||||||
|
"Prompt the user for input and echo it back."
|
||||||
|
(interactive)
|
||||||
|
(message "hello"))
|
||||||
|
```
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
(shell-command "thunar ~/Documents/ &")
|
||||||
|
(run-at-time "1 sec" nil #'delete-other-windows)
|
||||||
|
```
|
||||||
|
|
||||||
|
``` elisp
|
||||||
|
(defun open-current-dir()
|
||||||
|
(interactive)
|
||||||
|
(cond ((string-equal system-type "gnu/linux")
|
||||||
|
(shell-command (concat "thunar " ".")))
|
||||||
|
((string-equal system-type "darwin")
|
||||||
|
(shell-command (concat "open " "."))))
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
``` elisp
|
||||||
|
|
||||||
|
(defun test()
|
||||||
|
(interactive)
|
||||||
|
(cond ((string-equal system-type "gnu/linux")
|
||||||
|
(message "linux"))
|
||||||
|
((string-equal system-type "darwin")
|
||||||
|
(message "mac"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defun kill-other-buffers-startup ()
|
||||||
|
"Kill all buffers except the current one."
|
||||||
|
(interactive)
|
||||||
|
(mapc (lambda (buffer)
|
||||||
|
(unless (eq buffer (current-buffer))
|
||||||
|
(kill-buffer buffer)))
|
||||||
|
(buffer-list)))
|
||||||
|
|
||||||
|
|
||||||
|
;(add-hook 'emacs-startup-hook #'kill-other-buffers-startup)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
``` elisp
|
||||||
|
|
||||||
|
(defun create-weekly-entry ()
|
||||||
|
(interactive)
|
||||||
|
(let* ((current-date-time-format "[%Y-week-%V]")
|
||||||
|
(timestamp (format-time-string current-date-time-format))
|
||||||
|
(entry (concat "* Weekly Entry: " timestamp "\n"
|
||||||
|
"** Tasks:\n"
|
||||||
|
"** Notes:\n"
|
||||||
|
|
||||||
|
)))
|
||||||
|
(write-region entry nil "~/master-folder/org_files/todo/master-tl.org" 'append)))
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
62
Technical/Emacs/Emacs Calendar.md
Normal file
62
Technical/Emacs/Emacs Calendar.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
****For the config, see [[Emacs Config]] for the Source of truth****
|
||||||
|
|
||||||
|
# Config:
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
(require 'org-caldav)
|
||||||
|
(require 'url)
|
||||||
|
(require 'icalendar)
|
||||||
|
(modify-coding-system-alist 'file "caldav-work\\.org\\'" 'utf-8-unix)
|
||||||
|
(setq org-icalendar-timezone "Europe/London")
|
||||||
|
(setq calendar-time-zone 60) ;; minutes offset from UTC (BST = +60)
|
||||||
|
(setq calendar-standard-time-zone-name "GMT")
|
||||||
|
(setq calendar-daylight-time-zone-name "BST")
|
||||||
|
(setq org-caldav-timezone "Europe/London")
|
||||||
|
(defvar my/outlook-ics-url
|
||||||
|
"https://outlook.office365.com/owa/calendar/b910ea835e414663831e505f3d10baa2@microlise.com/d2568a0134fb49af92d39c1669c4729317662288011094411305/calendar.ics")
|
||||||
|
|
||||||
|
(defvar my/outlook-org-file
|
||||||
|
"D:\\_nextcloud\\master-folder\\org_files\\calendar\\caldav-work.org")
|
||||||
|
|
||||||
|
(defvar my/ics-python-script
|
||||||
|
"D:\\_nextcloud\\master-folder\\org_files\\calendar\\ics_to_org.py")
|
||||||
|
|
||||||
|
(defun my/sync-outlook-calendar ()
|
||||||
|
"Fetch and expand Outlook ICS (including recurrences) into org file."
|
||||||
|
(interactive)
|
||||||
|
(message "Syncing Outlook calendar...")
|
||||||
|
(let* ((cmd (format "python \"%s\" \"%s\" \"%s\""
|
||||||
|
my/ics-python-script
|
||||||
|
my/outlook-ics-url
|
||||||
|
my/outlook-org-file))
|
||||||
|
(result (shell-command-to-string cmd)))
|
||||||
|
(message "Outlook calendar sync: %s" (string-trim result))))
|
||||||
|
|
||||||
|
;; Sync on startup and every 30 minutes
|
||||||
|
(my/sync-outlook-calendar)
|
||||||
|
(run-with-timer (* 30 60) (* 30 60) #'my/sync-outlook-calendar)
|
||||||
|
;; --- CalDAV for native Nextcloud calendars ---
|
||||||
|
(setq org-caldav-url "https://nextcloud.zainezq.com/remote.php/dav/calendars/zaine/")
|
||||||
|
(setq org-caldav-calendars
|
||||||
|
'((:calendar-id "personal"
|
||||||
|
:inbox "D:\\_nextcloud\\master-folder\\org_files\\calendar\\caldav-personal.org"
|
||||||
|
:files nil)
|
||||||
|
(:calendar-id "zxh"
|
||||||
|
:inbox "D:\\_nextcloud\\master-folder\\org_files\\calendar\\caldav-family.org"
|
||||||
|
:files nil)))
|
||||||
|
|
||||||
|
;; --- Agenda files ---
|
||||||
|
(setq org-agenda-files
|
||||||
|
'("D:\\_nextcloud\\master-folder\\org_files\\calendar\\caldav-personal.org"
|
||||||
|
"D:\\_nextcloud\\master-folder\\org_files\\calendar\\caldav-work.org"
|
||||||
|
"D:\\_nextcloud\\master-folder\\org_files\\calendar\\caldav-family.org"
|
||||||
|
"D:\\_nextcloud\\master-folder\\org_files\\org_roam\\Books\\20250724230557-books_org_agenda.org"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Explanation:
|
||||||
|
|
||||||
|
- This configuration sets up Emacs to sync with both a Nextcloud calendar (via CalDAV) and an Outlook calendar (via ICS). It uses a Python script to fetch and convert the Outlook ICS data into an org file format that Emacs can read. The Nextcloud calendar is synced directly using the org-caldav package. Both calendars are included in the org-agenda files, so that I can view all events in one place. The sync for the Outlook calendar runs every 30 minutes to keep it up to date.
|
||||||
|
- To sync the Outlook calendar, you call the `my/sync-outlook-calendar` function, which runs the Python script to fetch and convert the ICS data. The Nextcloud calendar is synced automatically by org-caldav whenever you open Emacs or refresh the calendar.
|
||||||
822
Technical/Emacs/Emacs Config.md
Normal file
822
Technical/Emacs/Emacs Config.md
Normal file
@@ -0,0 +1,822 @@
|
|||||||
|
# Org Noter
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
|
||||||
|
(setq org-noter-notes-search-path
|
||||||
|
'("D:\\_nextcloud\\master-folder\\org_files\\org_roam\\"))
|
||||||
|
(setq org-noter-always-create-frame nil)
|
||||||
|
(setq org-noter-property-doc-file "NOTER_DOCUMENT")
|
||||||
|
|
||||||
|
(require 'pdf-tools)
|
||||||
|
(pdf-tools-install)
|
||||||
|
(use-package pdf-view-restore
|
||||||
|
:after pdf-tools
|
||||||
|
:config
|
||||||
|
(add-hook 'pdf-view-mode-hook 'pdf-view-restore-mode)
|
||||||
|
(setq pdf-view-restore-filename "~/.emacs.d/.pdf-view-restore"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Saving images in epdf mode (org roam):
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
|
||||||
|
|
||||||
|
;; epdf stuff
|
||||||
|
|
||||||
|
(require 'seq)
|
||||||
|
(require 'subr-x)
|
||||||
|
|
||||||
|
(defvar zaine/pdf-image-save-dir
|
||||||
|
"d:/_nextcloud/master-folder/org_files/org_roam/assets/_books/"
|
||||||
|
"Folder where cropped PDF images should be saved.")
|
||||||
|
|
||||||
|
(defvar zaine/org-roam-root
|
||||||
|
"d:/_nextcloud/master-folder/org_files/org_roam/"
|
||||||
|
"Root folder used for relative Org image links.")
|
||||||
|
|
||||||
|
(defvar zaine/pdf-image-kill-buffer-after-save t
|
||||||
|
"If non-nil, close the *PDF image* buffer after saving.")
|
||||||
|
|
||||||
|
(defvar zaine/pdf-image-insert-link-into-org t
|
||||||
|
"If non-nil, insert the image link into a visible Org buffer.")
|
||||||
|
|
||||||
|
(defun zaine/pdf-image--slugify (s)
|
||||||
|
"Turn S into a safe filename slug."
|
||||||
|
(let* ((s (downcase (string-trim (or s ""))))
|
||||||
|
(s (replace-regexp-in-string "[’'`]" "" s))
|
||||||
|
(s (replace-regexp-in-string "[^[:alnum:]]+" "-" s))
|
||||||
|
(s (replace-regexp-in-string "-+" "-" s))
|
||||||
|
(s (replace-regexp-in-string "\\`-+\\|-+\\'" "" s)))
|
||||||
|
(if (string-empty-p s) "pdf-image" s)))
|
||||||
|
|
||||||
|
(defun zaine/pdf-image--current-pdf-title ()
|
||||||
|
"Try to get the title from the most recently used PDF buffer."
|
||||||
|
(when-let ((buf
|
||||||
|
(seq-find
|
||||||
|
(lambda (b)
|
||||||
|
(with-current-buffer b
|
||||||
|
(derived-mode-p 'pdf-view-mode)))
|
||||||
|
(delq (current-buffer) (buffer-list)))))
|
||||||
|
(with-current-buffer buf
|
||||||
|
(if buffer-file-name
|
||||||
|
(file-name-base buffer-file-name)
|
||||||
|
(buffer-name buf)))))
|
||||||
|
|
||||||
|
(defun zaine/pdf-image--unique-file (dir stem ext)
|
||||||
|
"Return a unique filename in DIR using STEM and EXT."
|
||||||
|
(let ((file (expand-file-name (concat stem ext) dir))
|
||||||
|
(n 1))
|
||||||
|
(while (file-exists-p file)
|
||||||
|
(setq file
|
||||||
|
(expand-file-name
|
||||||
|
(format "%s-%02d%s" stem n ext)
|
||||||
|
dir))
|
||||||
|
(setq n (1+ n)))
|
||||||
|
file))
|
||||||
|
|
||||||
|
(defun zaine/pdf-image--org-link (file)
|
||||||
|
"Create a relative Org file link for FILE."
|
||||||
|
(format "[[file:%s]]"
|
||||||
|
(file-relative-name
|
||||||
|
(expand-file-name file)
|
||||||
|
(file-name-as-directory (expand-file-name zaine/org-roam-root)))))
|
||||||
|
|
||||||
|
(defun zaine/pdf-image--visible-org-buffer ()
|
||||||
|
"Find a visible Org buffer, usually your org-noter notes buffer."
|
||||||
|
(seq-find
|
||||||
|
(lambda (buf)
|
||||||
|
(with-current-buffer buf
|
||||||
|
(derived-mode-p 'org-mode)))
|
||||||
|
(delete-dups (mapcar #'window-buffer (window-list)))))
|
||||||
|
|
||||||
|
(defun zaine/pdf-image--insert-link-into-org (link)
|
||||||
|
"Insert LINK into a visible Org buffer, if one exists."
|
||||||
|
(when-let ((buf (zaine/pdf-image--visible-org-buffer)))
|
||||||
|
(with-current-buffer buf
|
||||||
|
(goto-char (point))
|
||||||
|
(unless (bolp)
|
||||||
|
(insert "\n"))
|
||||||
|
(insert link "\n")
|
||||||
|
(when (fboundp 'org-display-inline-images)
|
||||||
|
(org-display-inline-images)))
|
||||||
|
t))
|
||||||
|
|
||||||
|
(defun zaine/pdf-image--visible-org-window ()
|
||||||
|
"Find a visible Org window, usually the org-noter notes window."
|
||||||
|
(seq-find
|
||||||
|
(lambda (win)
|
||||||
|
(with-current-buffer (window-buffer win)
|
||||||
|
(derived-mode-p 'org-mode)))
|
||||||
|
(window-list nil 'no-minibuf)))
|
||||||
|
|
||||||
|
(defun zaine/pdf-image-save-and-insert (&optional ask-title)
|
||||||
|
"Save the current *PDF image* buffer into `_books` and insert an Org link.
|
||||||
|
|
||||||
|
With prefix argument, prompt for the title/name instead of using the PDF filename.
|
||||||
|
Also closes the temporary *PDF image* window afterwards."
|
||||||
|
(interactive "P")
|
||||||
|
(unless (or (derived-mode-p 'image-mode)
|
||||||
|
(string-match-p "\\`\\*PDF image\\*" (buffer-name)))
|
||||||
|
(user-error "Run this from the *PDF image* buffer"))
|
||||||
|
|
||||||
|
(let* ((image-buffer (current-buffer))
|
||||||
|
(image-windows (get-buffer-window-list image-buffer nil t))
|
||||||
|
(org-window (zaine/pdf-image--visible-org-window))
|
||||||
|
(default-title (or (zaine/pdf-image--current-pdf-title)
|
||||||
|
"pdf-image"))
|
||||||
|
(title (if ask-title
|
||||||
|
(read-string "Image/article name: " default-title)
|
||||||
|
default-title))
|
||||||
|
(stem (concat (format-time-string "%Y-%m-%d")
|
||||||
|
"-"
|
||||||
|
(zaine/pdf-image--slugify title)))
|
||||||
|
(file (zaine/pdf-image--unique-file
|
||||||
|
zaine/pdf-image-save-dir
|
||||||
|
stem
|
||||||
|
".png"))
|
||||||
|
(link (zaine/pdf-image--org-link file)))
|
||||||
|
|
||||||
|
(make-directory (file-name-directory file) t)
|
||||||
|
|
||||||
|
;; Save the raw image data from the *PDF image* buffer.
|
||||||
|
(let ((coding-system-for-write 'binary)
|
||||||
|
(require-final-newline nil))
|
||||||
|
(with-current-buffer image-buffer
|
||||||
|
(save-restriction
|
||||||
|
(widen)
|
||||||
|
(write-region (point-min) (point-max) file nil 'silent))
|
||||||
|
(set-buffer-modified-p nil)))
|
||||||
|
|
||||||
|
;; Copy the link just in case.
|
||||||
|
(kill-new link)
|
||||||
|
|
||||||
|
;; Insert the link into the visible Org notes window.
|
||||||
|
(when org-window
|
||||||
|
(with-selected-window org-window
|
||||||
|
(unless (bolp)
|
||||||
|
(insert "\n"))
|
||||||
|
(insert link "\n")
|
||||||
|
(when (fboundp 'org-display-inline-images)
|
||||||
|
(org-display-inline-images))))
|
||||||
|
|
||||||
|
;; Close the temporary *PDF image* window instead of letting Emacs reuse it.
|
||||||
|
(dolist (win image-windows)
|
||||||
|
(when (window-live-p win)
|
||||||
|
(ignore-errors
|
||||||
|
(delete-window win))))
|
||||||
|
|
||||||
|
;; Kill the temporary image buffer after its window has gone.
|
||||||
|
(when (buffer-live-p image-buffer)
|
||||||
|
(kill-buffer image-buffer))
|
||||||
|
|
||||||
|
;; Put focus back on the notes window if it still exists.
|
||||||
|
(when (and org-window (window-live-p org-window))
|
||||||
|
(select-window org-window))
|
||||||
|
|
||||||
|
(message "Saved %s and inserted Org link"
|
||||||
|
(file-name-nondirectory file))))
|
||||||
|
|
||||||
|
|
||||||
|
(with-eval-after-load 'image-mode
|
||||||
|
(define-key image-mode-map (kbd "C-c i p") #'zaine/pdf-image-save-and-insert))
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# CALDAV and org agenda calendars:
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
(require 'org-caldav)
|
||||||
|
(require 'url)
|
||||||
|
(require 'icalendar)
|
||||||
|
(modify-coding-system-alist 'file "caldav-work\\.org\\'" 'utf-8-unix)
|
||||||
|
(setq org-icalendar-timezone "Europe/London")
|
||||||
|
(setq calendar-time-zone 60) ;; minutes offset from UTC (BST = +60)
|
||||||
|
(setq calendar-standard-time-zone-name "GMT")
|
||||||
|
(setq calendar-daylight-time-zone-name "BST")
|
||||||
|
(setq org-caldav-timezone "Europe/London")
|
||||||
|
(defvar my/outlook-ics-url
|
||||||
|
"https://outlook.office365.com/owa/calendar/b910ea835e414663831e505f3d10baa2@microlise.com/d2568a0134fb49af92d39c1669c4729317662288011094411305/calendar.ics")
|
||||||
|
|
||||||
|
(defvar my/outlook-org-file
|
||||||
|
"D:\\_nextcloud\\master-folder\\org_files\\calendar\\caldav-work.org")
|
||||||
|
|
||||||
|
(defvar my/ics-python-script
|
||||||
|
"D:\\_nextcloud\\master-folder\\org_files\\calendar\\ics_to_org.py")
|
||||||
|
|
||||||
|
(defun my/sync-outlook-calendar ()
|
||||||
|
"Fetch and expand Outlook ICS (including recurrences) into org file."
|
||||||
|
(interactive)
|
||||||
|
(message "Syncing Outlook calendar...")
|
||||||
|
(let* ((cmd (format "python \"%s\" \"%s\" \"%s\""
|
||||||
|
my/ics-python-script
|
||||||
|
my/outlook-ics-url
|
||||||
|
my/outlook-org-file))
|
||||||
|
(result (shell-command-to-string cmd)))
|
||||||
|
(message "Outlook calendar sync: %s" (string-trim result))))
|
||||||
|
|
||||||
|
;; Sync on startup and every 30 minutes
|
||||||
|
(my/sync-outlook-calendar)
|
||||||
|
(run-with-timer (* 30 60) (* 30 60) #'my/sync-outlook-calendar)
|
||||||
|
;; --- CalDAV for native Nextcloud calendars ---
|
||||||
|
(setq org-caldav-url "https://nextcloud.zainezq.com/remote.php/dav/calendars/zaine/")
|
||||||
|
(setq org-caldav-calendars
|
||||||
|
'((:calendar-id "personal"
|
||||||
|
:inbox "D:\\_nextcloud\\master-folder\\org_files\\calendar\\caldav-personal.org"
|
||||||
|
:files nil)
|
||||||
|
(:calendar-id "zxh"
|
||||||
|
:inbox "D:\\_nextcloud\\master-folder\\org_files\\calendar\\caldav-family.org"
|
||||||
|
:files nil)))
|
||||||
|
|
||||||
|
;; --- Agenda files ---
|
||||||
|
(setq org-agenda-files
|
||||||
|
'("D:\\_nextcloud\\master-folder\\org_files\\calendar\\caldav-personal.org"
|
||||||
|
"D:\\_nextcloud\\master-folder\\org_files\\calendar\\caldav-work.org"
|
||||||
|
"D:\\_nextcloud\\master-folder\\org_files\\calendar\\caldav-family.org"
|
||||||
|
"D:\\_nextcloud\\master-folder\\org_files\\org_roam\\Books\\20250724230557-books_org_agenda.org"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Notes when reading physical book
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
("b" "Book Note" entry
|
||||||
|
(file+headline (lambda () (buffer-file-name)) "Notes")
|
||||||
|
"** p. %^{Page}\n[%^{Type|Insight|Important|Quote|Confusing}] %?\n")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Org image inserter:
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
;;; --- Org image inserter (improved search UX) -----------------------------
|
||||||
|
(use-package vertico
|
||||||
|
:init (vertico-mode))
|
||||||
|
|
||||||
|
(use-package orderless
|
||||||
|
:custom
|
||||||
|
(completion-styles '(orderless basic))
|
||||||
|
(completion-category-defaults nil))
|
||||||
|
|
||||||
|
|
||||||
|
(defgroup zq/org-assets nil
|
||||||
|
"Insert image links from predefined asset profiles."
|
||||||
|
:group 'org)
|
||||||
|
|
||||||
|
(defcustom zq/org-asset-profiles
|
||||||
|
'(("org-roam" . "D:\\_nextcloud\\master-folder\\org_files\\org_roam\\assets")
|
||||||
|
("org-web" . "D:\\_nextcloud\\master-folder\\org_files\\org_web\\assets"))
|
||||||
|
"Alist of (PROFILE-NAME . ASSETS-DIR)."
|
||||||
|
:type '(alist :key-type string :value-type directory)
|
||||||
|
:group 'zq/org-assets)
|
||||||
|
|
||||||
|
(defcustom zq/org-image-extensions
|
||||||
|
'("png" "jpg" "jpeg" "gif" "svg" "webp" "mov" "mp4" "mp3")
|
||||||
|
"File extensions considered when offering candidates."
|
||||||
|
:type '(repeat string)
|
||||||
|
:group 'zq/org-assets)
|
||||||
|
|
||||||
|
(defun zq/org--image-regexp ()
|
||||||
|
(concat "\\." (regexp-opt zq/org-image-extensions t) "\\'"))
|
||||||
|
|
||||||
|
(defun zq/org--image-files (dir)
|
||||||
|
"Return image/media files in DIR, newest modified first."
|
||||||
|
(when (and dir (file-directory-p dir))
|
||||||
|
(sort
|
||||||
|
(directory-files-recursively dir (zq/org--image-regexp))
|
||||||
|
#'file-newer-than-file-p)))
|
||||||
|
|
||||||
|
(defun zq/org--parse-name (filename)
|
||||||
|
"Split FILENAME into (DATE . REST)."
|
||||||
|
(if (string-match "^\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)-\\(.*\\)" filename)
|
||||||
|
(cons (match-string 1 filename)
|
||||||
|
(match-string 2 filename))
|
||||||
|
(cons nil filename)))
|
||||||
|
|
||||||
|
(defun zq/org--display-name (path)
|
||||||
|
(let* ((name (file-name-nondirectory path))
|
||||||
|
(dir (file-name-nondirectory (directory-file-name (file-name-directory path))))
|
||||||
|
(parsed (zq/org--parse-name name))
|
||||||
|
(date (car parsed))
|
||||||
|
(rest (cdr parsed)))
|
||||||
|
(format "%s (%s | %s)" rest (or date "no-date") dir)))
|
||||||
|
|
||||||
|
(defun zq/org--build-candidates (files)
|
||||||
|
"Return an alist of (DISPLAY . FULLPATH), handling duplicates.
|
||||||
|
|
||||||
|
Preserves the order of FILES, so newest files can appear first."
|
||||||
|
(let ((table (make-hash-table :test 'equal))
|
||||||
|
result)
|
||||||
|
(dolist (path files)
|
||||||
|
(let* ((display (zq/org--display-name path))
|
||||||
|
(existing (gethash display table)))
|
||||||
|
(if existing
|
||||||
|
;; duplicate → include full filename to disambiguate
|
||||||
|
(push (cons (file-name-nondirectory path) path) result)
|
||||||
|
(puthash display t table)
|
||||||
|
(push (cons display path) result))))
|
||||||
|
(nreverse result)))
|
||||||
|
|
||||||
|
(defun zq/org-insert-asset-image (&optional profile use-file-prefix)
|
||||||
|
"Insert an Org link to an image chosen from a PROFILE’s assets dir.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Fuzzy search (with Vertico/Orderless/Ivy/Helm)
|
||||||
|
- Clean names (date stripped)
|
||||||
|
- Relative paths inserted
|
||||||
|
|
||||||
|
With prefix arg (C-u), use file: links."
|
||||||
|
(interactive
|
||||||
|
(list (completing-read
|
||||||
|
"Profile: "
|
||||||
|
(mapcar #'car zq/org-asset-profiles)
|
||||||
|
nil t nil nil "org-roam")
|
||||||
|
current-prefix-arg))
|
||||||
|
(let* ((base-dir (cdr (assoc profile zq/org-asset-profiles)))
|
||||||
|
(_ (unless (and base-dir (file-directory-p base-dir))
|
||||||
|
(user-error "Invalid directory for profile '%s'" profile)))
|
||||||
|
|
||||||
|
(files (or (zq/org--image-files base-dir)
|
||||||
|
(user-error "No assets found in %s" base-dir)))
|
||||||
|
|
||||||
|
(candidates (zq/org--build-candidates files))
|
||||||
|
|
||||||
|
;; user selects clean name, we resolve full path
|
||||||
|
(selected (completing-read "Image: " candidates nil t))
|
||||||
|
(abs-path (cdr (assoc selected candidates)))
|
||||||
|
|
||||||
|
(here (or (buffer-file-name) default-directory))
|
||||||
|
(rel (file-relative-name abs-path (file-name-directory here)))
|
||||||
|
|
||||||
|
(link (format "[[%s%s]]"
|
||||||
|
(if use-file-prefix "file:" "")
|
||||||
|
rel)))
|
||||||
|
|
||||||
|
(insert link)
|
||||||
|
|
||||||
|
(when (derived-mode-p 'org-mode)
|
||||||
|
(org-display-inline-images t t))))
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Weekly org review file:
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
;; Create weekly review files for Sundays, with catch-up command to fill in any missing ones up to the current week.
|
||||||
|
(require 'seq)
|
||||||
|
|
||||||
|
(defvar zaine/weekly-review-base-dir
|
||||||
|
"D:/_nextcloud/master-folder/org_files/org_web/blogs/"
|
||||||
|
"Base directory for weekly review blog entries.")
|
||||||
|
|
||||||
|
(defun zaine/weekly-review--month-dir (time)
|
||||||
|
"Return the directory for TIME in the format YYYY/MM-month."
|
||||||
|
(let ((year (format-time-string "%Y" time))
|
||||||
|
(month-num (format-time-string "%m" time))
|
||||||
|
(month-name (downcase (format-time-string "%B" time))))
|
||||||
|
(expand-file-name (format "%s/%s-%s" year month-num month-name)
|
||||||
|
zaine/weekly-review-base-dir)))
|
||||||
|
|
||||||
|
(defun zaine/weekly-review--normalize-to-review-time (time)
|
||||||
|
"Normalize TIME to 12:00 on the same date."
|
||||||
|
(let* ((decoded (decode-time time))
|
||||||
|
(day (nth 3 decoded))
|
||||||
|
(month (nth 4 decoded))
|
||||||
|
(year (nth 5 decoded)))
|
||||||
|
(encode-time 0 0 12 day month year)))
|
||||||
|
|
||||||
|
(defun zaine/weekly-review--sunday-of-week (&optional time)
|
||||||
|
"Return the most recent Sunday on or before TIME, normalized to 12:00."
|
||||||
|
(let* ((time (or time (current-time)))
|
||||||
|
(decoded (decode-time time))
|
||||||
|
(dow (nth 6 decoded))
|
||||||
|
(day (nth 3 decoded))
|
||||||
|
(month (nth 4 decoded))
|
||||||
|
(year (nth 5 decoded)))
|
||||||
|
(encode-time 0 0 12 (- day dow) month year)))
|
||||||
|
|
||||||
|
(defun zaine/weekly-review--next-sunday (&optional time)
|
||||||
|
"Return the next Sunday strictly after TIME, normalized to 12:00.
|
||||||
|
If TIME is a Sunday, returns the following Sunday."
|
||||||
|
(let* ((time (or time (current-time)))
|
||||||
|
(decoded (decode-time time))
|
||||||
|
(dow (nth 6 decoded))
|
||||||
|
(days-ahead (if (= dow 0) 7 (- 7 dow))))
|
||||||
|
(zaine/weekly-review--normalize-to-review-time
|
||||||
|
(time-add time (days-to-time days-ahead)))))
|
||||||
|
|
||||||
|
(defun zaine/weekly-review--coming-sunday (&optional time)
|
||||||
|
"Return the coming Sunday relative to TIME, normalized to 12:00.
|
||||||
|
If TIME is a Sunday, return that day. Otherwise return the next Sunday."
|
||||||
|
(let* ((time (or time (current-time)))
|
||||||
|
(decoded (decode-time time))
|
||||||
|
(dow (nth 6 decoded)))
|
||||||
|
(if (= dow 0)
|
||||||
|
(zaine/weekly-review--normalize-to-review-time time)
|
||||||
|
(zaine/weekly-review--next-sunday time))))
|
||||||
|
|
||||||
|
(defun zaine/weekly-review--file-name (time)
|
||||||
|
"Return the weekly review filename for TIME."
|
||||||
|
(format-time-string "%d-%m-week-review.org" time))
|
||||||
|
|
||||||
|
(defun zaine/weekly-review--slug (time)
|
||||||
|
"Return the weekly review slug for TIME."
|
||||||
|
(format-time-string "%d-%m-%y-week-review" time))
|
||||||
|
|
||||||
|
(defun zaine/weekly-review--title (time)
|
||||||
|
"Return the title for TIME."
|
||||||
|
(format "%s - Weekly Review"
|
||||||
|
(format-time-string "[%d-%m-%Y]" time)))
|
||||||
|
|
||||||
|
(defun zaine/weekly-review--header (time)
|
||||||
|
"Return Org header text for weekly review at TIME."
|
||||||
|
(concat
|
||||||
|
(format "#+TITLE: %s\n" (zaine/weekly-review--title time))
|
||||||
|
"#+OPTIONS: num:nil\n"
|
||||||
|
(format "#+DATE: %s\n" (format-time-string "<%Y-%m-%d %a 12:00>" time))
|
||||||
|
"#+filetags: :review:\n"
|
||||||
|
"#+COMMENTS: t\n"
|
||||||
|
(format "#+SLUG: %s\n\n" (zaine/weekly-review--slug time))))
|
||||||
|
|
||||||
|
(defun zaine/weekly-review--all-review-files ()
|
||||||
|
"Return all weekly review files under `zaine/weekly-review-base-dir`."
|
||||||
|
(when (file-directory-p zaine/weekly-review-base-dir)
|
||||||
|
(directory-files-recursively
|
||||||
|
zaine/weekly-review-base-dir
|
||||||
|
"^[0-9]\\{2\\}-[0-9]\\{2\\}-week-review\\.org$")))
|
||||||
|
|
||||||
|
(defun zaine/weekly-review--extract-date-from-file (file)
|
||||||
|
"Extract review date from FILE path.
|
||||||
|
Expected path like:
|
||||||
|
.../blogs/YYYY/MM-month/DD-MM-week-review.org"
|
||||||
|
(when (string-match
|
||||||
|
"/blogs/\\([0-9]\\{4\\}\\)/[0-9]\\{2\\}-[[:lower:]]+/\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\)-week-review\\.org$"
|
||||||
|
file)
|
||||||
|
(let* ((year (string-to-number (match-string 1 file)))
|
||||||
|
(day (string-to-number (match-string 2 file)))
|
||||||
|
(month (string-to-number (match-string 3 file))))
|
||||||
|
(encode-time 0 0 12 day month year))))
|
||||||
|
|
||||||
|
(defun zaine/weekly-review--latest-existing-sunday ()
|
||||||
|
"Return the latest Sunday review time found, or nil if none exist."
|
||||||
|
(let* ((files (zaine/weekly-review--all-review-files))
|
||||||
|
(dates (delq nil (mapcar #'zaine/weekly-review--extract-date-from-file files))))
|
||||||
|
(when dates
|
||||||
|
(seq-reduce
|
||||||
|
(lambda (latest current)
|
||||||
|
(if (time-less-p latest current) current latest))
|
||||||
|
(cdr dates)
|
||||||
|
(car dates)))))
|
||||||
|
|
||||||
|
(defun zaine/weekly-review--create-file-for-sunday (sunday)
|
||||||
|
"Create a weekly review file for SUNDAY if it doesn't exist.
|
||||||
|
Opens the file and returns its path. If it already exists, just opens it."
|
||||||
|
(let* ((target-dir (zaine/weekly-review--month-dir sunday))
|
||||||
|
(file-name (zaine/weekly-review--file-name sunday))
|
||||||
|
(file-path (expand-file-name file-name target-dir)))
|
||||||
|
(make-directory target-dir t)
|
||||||
|
(unless (file-exists-p file-path)
|
||||||
|
(with-temp-file file-path
|
||||||
|
(insert (zaine/weekly-review--header sunday))))
|
||||||
|
file-path))
|
||||||
|
|
||||||
|
;;; Public commands
|
||||||
|
|
||||||
|
(defun zaine/weekly-review-catchup ()
|
||||||
|
"Create all missing weekly review files from the oldest gap up to this week's Sunday.
|
||||||
|
If today is Monday 30 March, catches up through Sunday 29 March.
|
||||||
|
Creates one file per call so you can review each before continuing."
|
||||||
|
(interactive)
|
||||||
|
(let* ((past-sunday (zaine/weekly-review--sunday-of-week (current-time)))
|
||||||
|
(latest (zaine/weekly-review--latest-existing-sunday))
|
||||||
|
(next-needed (if latest
|
||||||
|
(zaine/weekly-review--normalize-to-review-time
|
||||||
|
(time-add latest (days-to-time 7)))
|
||||||
|
past-sunday)))
|
||||||
|
(if (time-less-p past-sunday next-needed)
|
||||||
|
(message "Weekly reviews are already up to date through %s."
|
||||||
|
(format-time-string "%d-%m-%Y" past-sunday))
|
||||||
|
(let ((file-path (zaine/weekly-review--create-file-for-sunday next-needed)))
|
||||||
|
(find-file file-path)
|
||||||
|
(message "Created missing review for Sunday %s — call again to catch up further."
|
||||||
|
(format-time-string "%d-%m-%Y" next-needed))))))
|
||||||
|
|
||||||
|
(defun zaine/weekly-review-create-coming ()
|
||||||
|
"Create the weekly review for the coming Sunday.
|
||||||
|
If today is Wednesday 1 April, creates the file for Sunday 5 April
|
||||||
|
in the April directory, creating it if needed.
|
||||||
|
If today is Sunday, creates today's review.
|
||||||
|
If the file already exists, just opens it."
|
||||||
|
(interactive)
|
||||||
|
(let* ((coming-sunday (zaine/weekly-review--coming-sunday (current-time)))
|
||||||
|
(file-path (zaine/weekly-review--create-file-for-sunday coming-sunday)))
|
||||||
|
(find-file file-path)
|
||||||
|
(message "%s review for Sunday %s."
|
||||||
|
(if (file-exists-p file-path) "Opened existing" "Created")
|
||||||
|
(format-time-string "%d-%m-%Y" coming-sunday))))
|
||||||
|
|
||||||
|
(global-set-key (kbd "C-c w r") #'zaine/weekly-review-catchup)
|
||||||
|
(global-set-key (kbd "C-c w c") #'zaine/weekly-review-create-coming)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Insert org web entries
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
;; Create org web entries for blogs and posts with a consistent template and naming convention.
|
||||||
|
(defun zaine/create-org-web-entry ()
|
||||||
|
"Create a new org web entry in either blogs or posts."
|
||||||
|
(interactive)
|
||||||
|
(let* ((entry-type (completing-read "Choose type (blogs/posts): " '("blogs" "posts") nil t))
|
||||||
|
(slug (read-string "Filename / slug: "))
|
||||||
|
(title (read-string "Title: "))
|
||||||
|
(now (current-time))
|
||||||
|
(date-for-title (format-time-string "[%d-%m-%Y]" now))
|
||||||
|
(date-for-org (format-time-string "<%Y-%m-%d %a %H:%M>" now))
|
||||||
|
(year (format-time-string "%Y" now))
|
||||||
|
(month-num (format-time-string "%m" now))
|
||||||
|
(month-name (downcase (format-time-string "%B" now)))
|
||||||
|
(blog-dir (format "D:/_nextcloud/master-folder/org_files/org_web/blogs/%s/%s-%s"
|
||||||
|
year month-num month-name))
|
||||||
|
(post-dir "D:/_nextcloud/master-folder/org_files/org_web/posts/career")
|
||||||
|
(target-dir (if (string= entry-type "blogs") blog-dir post-dir))
|
||||||
|
(file-path (expand-file-name (concat slug ".org") target-dir))
|
||||||
|
(filetags (if (string= entry-type "blogs") ":life:" ":post:")))
|
||||||
|
|
||||||
|
;; Create directory if it does not exist
|
||||||
|
(unless (file-directory-p target-dir)
|
||||||
|
(make-directory target-dir t))
|
||||||
|
|
||||||
|
;; Open file and insert template if new
|
||||||
|
(find-file file-path)
|
||||||
|
(when (= (buffer-size) 0)
|
||||||
|
(insert (format "#+TITLE: %s\n" title))
|
||||||
|
(insert "#+OPTIONS: num:nil\n")
|
||||||
|
(insert (format "#+DATE: %s\n" date-for-org))
|
||||||
|
(insert (format "#+filetags: %s\n" filetags))
|
||||||
|
(insert "#+COMMENTS: t\n")
|
||||||
|
(insert (format "#+SLUG: %s\n\n" slug))
|
||||||
|
(save-buffer))))
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Insert org macros:
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
(defun org-insert-macro ()
|
||||||
|
"Interactively select and insert an Org macro with prompted values."
|
||||||
|
(interactive)
|
||||||
|
(let* ((macro-defs
|
||||||
|
'(("sidenote" . ("ID" "Content"))
|
||||||
|
("epigraph" . ("Quote" "Footer"))
|
||||||
|
("epigraph_single" . ("Quote"))
|
||||||
|
("epigraph3" . ("Quote" "Author" "Work"))
|
||||||
|
("kbd" . ("Key"))
|
||||||
|
("margimg" . ("Image URL" "Alt text" "Caption (optional)"))
|
||||||
|
("countdown" . ("ISO datetime (e.g. 2025-12-31T00:00:00)" "Label"))))
|
||||||
|
(name (completing-read "Insert macro: " (mapcar #'car macro-defs) nil t))
|
||||||
|
(args (cdr (assoc name macro-defs)))
|
||||||
|
(values (mapcar (lambda (prompt)
|
||||||
|
(read-string (format "%s: " prompt)))
|
||||||
|
args))
|
||||||
|
(macro (format "{{{%s(%s)}}}"
|
||||||
|
name
|
||||||
|
(mapconcat #'identity values ","))))
|
||||||
|
(insert macro)))
|
||||||
|
|
||||||
|
(define-key org-mode-map (kbd "C-c m") #'org-insert-macro)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Insert Dailies into `20260421100223-dailies.org`
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
;; ─── org-roam daily auto-linker ───────────────────────────────────────────
|
||||||
|
|
||||||
|
(defvar my/org-roam-last-capture-file nil
|
||||||
|
"Stores the daily file path captured via org-roam-capture-new-node-hook.")
|
||||||
|
|
||||||
|
(defun my/org-roam-capture-store-file ()
|
||||||
|
"Store file path when a new org-roam node is created."
|
||||||
|
(when-let ((f (buffer-file-name)))
|
||||||
|
(setq my/org-roam-last-capture-file f)))
|
||||||
|
|
||||||
|
(defun my/org-roam-daily-already-linked-p (index-file date-str)
|
||||||
|
"Return t if DATE-STR already appears in INDEX-FILE."
|
||||||
|
(when (file-exists-p index-file)
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert-file-contents index-file)
|
||||||
|
(search-forward date-str nil t))))
|
||||||
|
|
||||||
|
(defun my/org-roam-daily-add-link (index-file date-str)
|
||||||
|
"Append a heading linking DATE-STR daily into INDEX-FILE."
|
||||||
|
(message " Adding link for %s..." date-str)
|
||||||
|
(with-current-buffer (find-file-noselect index-file)
|
||||||
|
(save-excursion
|
||||||
|
(goto-char (point-max))
|
||||||
|
(unless (bolp) (insert "\n"))
|
||||||
|
(insert (format "** [[file:daily/%s.org][%s]]\n" date-str date-str)))
|
||||||
|
(save-buffer))
|
||||||
|
(org-roam-db-update-file index-file))
|
||||||
|
|
||||||
|
(defun my/org-roam-daily-link-to-index ()
|
||||||
|
"After capture, link today's daily into the index file."
|
||||||
|
(let ((captured-file
|
||||||
|
(or my/org-roam-last-capture-file
|
||||||
|
(ignore-errors
|
||||||
|
(plist-get org-capture-current-plist :original-file)))))
|
||||||
|
(when (and captured-file
|
||||||
|
(string-match-p (regexp-quote "daily") captured-file))
|
||||||
|
(let* ((today-date (file-name-base captured-file))
|
||||||
|
(index-file (expand-file-name
|
||||||
|
"20260421100223-dailies.org"
|
||||||
|
org-roam-directory)))
|
||||||
|
(if (my/org-roam-daily-already-linked-p index-file today-date)
|
||||||
|
(message " %s already linked, skipping." today-date)
|
||||||
|
(my/org-roam-daily-add-link index-file today-date)))
|
||||||
|
(setq my/org-roam-last-capture-file nil))))
|
||||||
|
|
||||||
|
(add-hook 'org-roam-capture-new-node-hook #'my/org-roam-capture-store-file)
|
||||||
|
(add-hook 'org-capture-after-finalize-hook #'my/org-roam-daily-link-to-index)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Old stuff:
|
||||||
|
|
||||||
|
## org image inserter:
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
;; Core group & settings (dedupe: define these only once)
|
||||||
|
;; (defgroup zq/org-assets nil
|
||||||
|
;; "Insert image links from predefined asset profiles."
|
||||||
|
;; :group 'org)
|
||||||
|
|
||||||
|
;; (defcustom zq/org-asset-profiles
|
||||||
|
;; '(("org-roam" . "D:\\_nextcloud\\master-folder\\org_files\\org_roam\\assets")
|
||||||
|
;; ("org-web" . "D:\\_nextcloud\\master-folder\\org_files\\org_web\\assets"))
|
||||||
|
;; "Alist of (PROFILE-NAME . ASSETS-DIR)."
|
||||||
|
;; :type '(alist :key-type string :value-type directory)
|
||||||
|
;; :group 'zq/org-assets)
|
||||||
|
|
||||||
|
;; (defcustom zq/org-image-extensions '("png" "jpg" "jpeg" "gif" "svg" "webp" "mov" "mp4" "mp3")
|
||||||
|
;; "Image file extensions considered when offering candidates."
|
||||||
|
;; :type '(repeat string)
|
||||||
|
;; :group 'zq/org-assets)
|
||||||
|
|
||||||
|
;; (defun zq/org--image-regexp ()
|
||||||
|
;; "Build a regexp that matches configured image extensions."
|
||||||
|
;; (concat "\\." (regexp-opt zq/org-image-extensions t) "\\'"))
|
||||||
|
|
||||||
|
;; (defun zq/org--image-files (dir)
|
||||||
|
;; "Return a list of image files under DIR (recursively)."
|
||||||
|
;; (when (and dir (file-directory-p dir))
|
||||||
|
;; (directory-files-recursively dir (zq/org--image-regexp))))
|
||||||
|
|
||||||
|
;; (defun zq/org-insert-asset-image (&optional profile use-file-prefix)
|
||||||
|
;; "Insert an Org link to an image chosen from a PROFILE’s assets dir.
|
||||||
|
|
||||||
|
;; Interactively:
|
||||||
|
;; - Prompts for PROFILE (from `zq/org-asset-profiles`).
|
||||||
|
;; - Presents only image files under that profile’s assets directory.
|
||||||
|
;; - Inserts a relative link like [[path/to/image.png]].
|
||||||
|
;; - With prefix arg (C-u), uses [[file:path/to/image.png]].
|
||||||
|
|
||||||
|
;; Non-interactively:
|
||||||
|
;; PROFILE is the profile name string, USE-FILE-PREFIX non-nil forces file: prefix."
|
||||||
|
;; (interactive
|
||||||
|
;; (list (completing-read "Profile: "
|
||||||
|
;; (mapcar #'car zq/org-asset-profiles)
|
||||||
|
;; nil t nil nil "org-roam")
|
||||||
|
;; current-prefix-arg))
|
||||||
|
;; (let* ((base-dir (cdr (assoc profile zq/org-asset-profiles)))
|
||||||
|
;; (_ (unless (and base-dir (file-directory-p base-dir))
|
||||||
|
;; (user-error "Profile '%s' has no valid directory: %s" profile base-dir)))
|
||||||
|
;; (candidates (or (zq/org--image-files base-dir)
|
||||||
|
;; (user-error "No images found in %s" base-dir)))
|
||||||
|
;; (abs-path (expand-file-name (completing-read "Image: " candidates nil t)))
|
||||||
|
;; (here (or (buffer-file-name) default-directory))
|
||||||
|
;; (rel (file-relative-name abs-path (file-name-directory here)))
|
||||||
|
;; (link (format "[[%s%s]]"
|
||||||
|
;; (if use-file-prefix "file:" "")
|
||||||
|
;; rel)))
|
||||||
|
;; (insert link)
|
||||||
|
;; (when (derived-mode-p 'org-mode)
|
||||||
|
;; (org-display-inline-images t t))))
|
||||||
|
|
||||||
|
|
||||||
|
;;; --- Robust paste-into-profile without org-download--clipboard-image-p ---
|
||||||
|
|
||||||
|
;; (require 'url) ;; for url-copy-file
|
||||||
|
;; (require 'org-download) ;; for org-download-clipboard
|
||||||
|
|
||||||
|
;; (defun zq/org--ensure-dir (dir)
|
||||||
|
;; "Ensure DIR exists and is a directory; error if invalid."
|
||||||
|
;; (unless (and dir (file-directory-p dir))
|
||||||
|
;; (user-error "Profile directory invalid or not found: %s" dir))
|
||||||
|
;; (make-directory dir t)
|
||||||
|
;; dir)
|
||||||
|
|
||||||
|
;; (defun zq/org--unique-image-filename (&optional ext)
|
||||||
|
;; "Return a unique filename like 20260227_160404.png. Default EXT is png."
|
||||||
|
;; (concat (format-time-string "%Y%m%d_%H%M%S") "." (or ext "png")))
|
||||||
|
|
||||||
|
;; (defun zq/org--rel-link-to (abs-path)
|
||||||
|
;; "Return a relative Org link for ABS-PATH from current buffer."
|
||||||
|
;; (let* ((here (or (buffer-file-name) default-directory))
|
||||||
|
;; (rel (file-relative-name abs-path (file-name-directory here))))
|
||||||
|
;; (format "[[%s]]" rel)))
|
||||||
|
|
||||||
|
;; (defun zq/org--clipboard-text ()
|
||||||
|
;; "Return current clipboard text (or nil) without altering kill ring."
|
||||||
|
;; (let ((str (current-kill 0 t)))
|
||||||
|
;; (when (and str (stringp str) (> (length str) 0)) str)))
|
||||||
|
|
||||||
|
;; (defun zq/org--grab-clipboard-image-to-temp ()
|
||||||
|
;; "Try to capture clipboard image into a temporary PNG file.
|
||||||
|
;; Return the temp filename on success; nil otherwise. Temp file is caller-managed."
|
||||||
|
;; (let ((tmp (make-temp-file "zq-clip-" nil ".png"))
|
||||||
|
;; (ok nil))
|
||||||
|
;; (unwind-protect
|
||||||
|
;; (progn
|
||||||
|
;; ;; Try to save clipboard to TMP; some systems signal error when no image
|
||||||
|
;; (condition-case _err
|
||||||
|
;; (org-download-clipboard tmp)
|
||||||
|
;; (error
|
||||||
|
;; ;; ignore; ok remains nil
|
||||||
|
;; ))
|
||||||
|
;; ;; Validate: file exists, non-empty, and Emacs recognizes image type
|
||||||
|
;; (when (and (file-exists-p tmp)
|
||||||
|
;; (> (nth 7 (file-attributes tmp)) 0)
|
||||||
|
;; (ignore-errors (image-type-from-file tmp)))
|
||||||
|
;; (setq ok t)))
|
||||||
|
;; (unless ok
|
||||||
|
;; (when (file-exists-p tmp) (delete-file tmp))))
|
||||||
|
;; (when ok tmp)))
|
||||||
|
|
||||||
|
;; (defun zq/org--save-data-uri (data-uri target)
|
||||||
|
;; "Save a data:image/...;base64,... DATA-URI to TARGET."
|
||||||
|
;; (unless (string-match "\\`data:image/\\([a-zA-Z0-9+.-]+\\);base64,\\(.*\\)\\'" data-uri)
|
||||||
|
;; (user-error "Clipboard looks like a data URI but not an image/base64"))
|
||||||
|
;; (let ((bytes (base64-decode-string (match-string 2 data-uri))))
|
||||||
|
;; (with-temp-file target
|
||||||
|
;; (set-buffer-multibyte nil)
|
||||||
|
;; (insert bytes))))
|
||||||
|
|
||||||
|
;; (defun zq/org--download-url-to-file (url target)
|
||||||
|
;; "Download image from URL to TARGET (synchronously)."
|
||||||
|
;; (url-copy-file url target t))
|
||||||
|
|
||||||
|
;; (defun zq/org-paste-image-into-profile (&optional profile)
|
||||||
|
;; "Paste image (clipboard/URL/data-URI) into PROFILE assets and insert a relative Org link."
|
||||||
|
;; (interactive
|
||||||
|
;; (list (completing-read "Profile: "
|
||||||
|
;; (mapcar #'car zq/org-asset-profiles)
|
||||||
|
;; nil t nil nil "org-roam")))
|
||||||
|
;; (let* ((assets-dir (cdr (assoc profile zq/org-asset-profiles))))
|
||||||
|
;; (zq/org--ensure-dir assets-dir)
|
||||||
|
|
||||||
|
;; (let* ((filename (zq/org--unique-image-filename "png"))
|
||||||
|
;; (target (expand-file-name filename assets-dir))
|
||||||
|
;; (handled nil))
|
||||||
|
|
||||||
|
;; ;; 1) Preferred: raw image from clipboard via org-download backends
|
||||||
|
;; (let ((tmp (zq/org--grab-clipboard-image-to-temp)))
|
||||||
|
;; (when tmp
|
||||||
|
;; (rename-file tmp target t) ;; move tmp → target
|
||||||
|
;; (setq handled t)))
|
||||||
|
|
||||||
|
;; ;; 2) If no raw image, try clipboard text: data URI or URL
|
||||||
|
;; (unless handled
|
||||||
|
;; (let ((clip (zq/org--clipboard-text)))
|
||||||
|
;; (cond
|
||||||
|
;; ;; data URI
|
||||||
|
;; ((and clip (string-prefix-p "data:image/" clip))
|
||||||
|
;; (zq/org--save-data-uri clip target)
|
||||||
|
;; (setq handled t))
|
||||||
|
;; ;; http(s) URL
|
||||||
|
;; ((and clip (string-match-p "\\`https?://" clip))
|
||||||
|
;; (zq/org--download-url-to-file clip target)
|
||||||
|
;; (setq handled t)))))
|
||||||
|
|
||||||
|
;; (unless handled
|
||||||
|
;; (user-error
|
||||||
|
;; (concat
|
||||||
|
;; "No image found in clipboard.\n\n"
|
||||||
|
;; "Tips:\n"
|
||||||
|
;; "• In your browser, use **Copy image** (not “Copy image address”)\n"
|
||||||
|
;; "• Or copy from an image viewer / screenshot tool (to clipboard)\n"
|
||||||
|
;; "• On Wayland: install `wl-clipboard` (provides `wl-paste`)\n"
|
||||||
|
;; "• On X11: install `xclip` or `xsel`\n"
|
||||||
|
;; "• Alternatively: copy an image URL or a data:image/...;base64,... and retry")))
|
||||||
|
|
||||||
|
;; ;; Insert the relative link and display inline
|
||||||
|
;; (insert (zq/org--rel-link-to target))
|
||||||
|
;; (when (derived-mode-p 'org-mode)
|
||||||
|
;; (org-display-inline-images t t))
|
||||||
|
;; (message "Saved image → %s" target))))
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
28
Technical/Emacs/Emacs MOC.md
Normal file
28
Technical/Emacs/Emacs MOC.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
The purpose of this file is to store things related to emacs (that being packages, or community projects that i stumble across).
|
||||||
|
|
||||||
|
# Links
|
||||||
|
|
||||||
|
- [[Org Roam]] for roam related things
|
||||||
|
- [[GTD]]
|
||||||
|
- [[Keybindings Emacs]]
|
||||||
|
- [[Elisp]]
|
||||||
|
- [[Magit]]
|
||||||
|
- [[Evil Mode]]
|
||||||
|
- [[Org Publish]]
|
||||||
|
- [[Org Noter]]
|
||||||
|
- [[Emacs Calendar]]
|
||||||
|
- [[Emacs Config]]
|
||||||
|
|
||||||
|
# Resources
|
||||||
|
|
||||||
|
- A link that contains all the key bindings for emacs: [Keybindings list](http://xahlee.info/emacs/emacs/emacs_keybinding_list.html)
|
||||||
|
- the theme 'ef-dream' repository: [ef-themes](https://github.com/protesilaos/ef-themes)
|
||||||
|
- something to look into: [combobulate](https://github.com/mickeynp/combobulate)
|
||||||
|
- the use package git hub: [use-package](https://github.com/jwiegley/use-package)
|
||||||
|
- hamacs (someones emacs config) [hamacs](https://www.howardabrams.com/hamacs/)
|
||||||
|
- projectile doc [proj doc](https://docs.projectile.mx/projectile/configuration.html)
|
||||||
|
- doom style dashboard [doom dash](https://gist.github.com/DevelopmentCool2449/ffc91d1d9b7b16e0f48402b698386f3d#file-dashboard-doom-style-el)
|
||||||
|
- soft charcoal theme [sc](https://github.com/mswift42/soft-charcoal-theme?tab=readme-ov-file)
|
||||||
|
- the theme im currently using: [kaolin](https://github.com/ogdenwebb/emacs-kaolin-themes?tab=readme-ov-file)
|
||||||
|
- config: [config-dwt](https://gitlab.com/dwt1/configuring-emacs)
|
||||||
|
- simplenote to self hosted: [Link](https://ru2saig.github.io/posts/switching-from-simplenote-to-a-self-hosted-approach-nextcloud-orgzly-and-emacs/)
|
||||||
40
Technical/Emacs/Evil Mode.md
Normal file
40
Technical/Emacs/Evil Mode.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
C-u Cc . - timestamp
|
||||||
|
|
||||||
|
# Keybindings for moving:
|
||||||
|
|
||||||
|
## Search
|
||||||
|
|
||||||
|
To search use
|
||||||
|
Then if you want to get the hits, hit enter, then you can use 'n' for next and 'N' for previous hits.
|
||||||
|
|
||||||
|
## moving
|
||||||
|
|
||||||
|
- To move forward use 'e' and 'E'
|
||||||
|
- To move forward use 'b' and 'B'
|
||||||
|
|
||||||
|
(for beginning and end)
|
||||||
|
you can also use '{' and '}' for paragraph movement
|
||||||
|
|
||||||
|
- To move to the next line use: 'j'
|
||||||
|
- To move to the previous line use: 'k'
|
||||||
|
|
||||||
|
## replacement
|
||||||
|
|
||||||
|
that is a sample text
|
||||||
|
|
||||||
|
- Use 'r' To replace a single letter
|
||||||
|
- Use 'R' To enter replace mode and use ESC to exit it
|
||||||
|
|
||||||
|
To do a replace all:
|
||||||
|
|
||||||
|
- :%s//replacement/g
|
||||||
|
|
||||||
|
thats if the '/' has stored the last search, the /g is for all. if you dont use it, itll just replace the first one in each line. you can also use /gc which will prompt for confirmation.
|
||||||
|
|
||||||
|
- :'\<,'\>s//bar/g
|
||||||
|
|
||||||
|
thats if you are in visual mode
|
||||||
|
|
||||||
|
- :s/\<search<sub>string</sub>\>/\<replace<sub>string</sub>\>/g
|
||||||
|
|
||||||
|
thats a more generic one
|
||||||
1
Technical/Emacs/GTD.md
Normal file
1
Technical/Emacs/GTD.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
- article: [gtd](https://hamberg.no/gtd)
|
||||||
14
Technical/Emacs/Keybindings Emacs.md
Normal file
14
Technical/Emacs/Keybindings Emacs.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
Keybindings I have come across:
|
||||||
|
|
||||||
|
# Get help for a major mode:
|
||||||
|
|
||||||
|
Type \`C-h m\` to get information for a major mode.
|
||||||
|
|
||||||
|
# TO indent a region n amount of spaces (where n is a number): \`C-u n C-x Tab\`
|
||||||
|
|
||||||
|
# Projectile:
|
||||||
|
|
||||||
|
- \`SPC-p p\` - switch project
|
||||||
|
- \`SPC-p f\` - find project
|
||||||
|
|
||||||
|
# Use C-u SPC X P to run powershell with args
|
||||||
71
Technical/Emacs/Magit.md
Normal file
71
Technical/Emacs/Magit.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# Magit Key Guide (Quick Reference)
|
||||||
|
|
||||||
|
## Open Magit
|
||||||
|
|
||||||
|
- C-x g
|
||||||
|
Open Magit status buffer
|
||||||
|
|
||||||
|
## Staging & Unstaging
|
||||||
|
|
||||||
|
- s
|
||||||
|
Stage (file or hunk)
|
||||||
|
- u
|
||||||
|
Unstage
|
||||||
|
- S
|
||||||
|
Stage all
|
||||||
|
- U
|
||||||
|
Unstage all
|
||||||
|
|
||||||
|
## Committing
|
||||||
|
|
||||||
|
- c c
|
||||||
|
Commit
|
||||||
|
- C-c C-c
|
||||||
|
Confirm commit (in commit buffer)
|
||||||
|
- c a
|
||||||
|
Amend last commit
|
||||||
|
|
||||||
|
## Branching
|
||||||
|
|
||||||
|
- b b
|
||||||
|
Switch branch
|
||||||
|
- b c
|
||||||
|
Create new branch
|
||||||
|
- b m
|
||||||
|
Merge into current
|
||||||
|
- b M
|
||||||
|
Rebase onto another branch
|
||||||
|
|
||||||
|
## Push / Pull
|
||||||
|
|
||||||
|
- P u
|
||||||
|
Push to upstream
|
||||||
|
- F u
|
||||||
|
Pull from upstream
|
||||||
|
- f u
|
||||||
|
Fetch from upstream
|
||||||
|
|
||||||
|
## Logs & Diffs
|
||||||
|
|
||||||
|
- l l
|
||||||
|
Show log for current branch
|
||||||
|
- d
|
||||||
|
Show diff (between commits, etc.)
|
||||||
|
- RET
|
||||||
|
View diff at point
|
||||||
|
|
||||||
|
## Help & Dispatch
|
||||||
|
|
||||||
|
- ?
|
||||||
|
Show help for current buffer
|
||||||
|
- x
|
||||||
|
Open Magit dispatch menu (magit-dispatch)
|
||||||
|
- \!
|
||||||
|
Run Git command (rebase, cherry-pick, etc.)
|
||||||
|
|
||||||
|
## Other Essentials
|
||||||
|
|
||||||
|
- k
|
||||||
|
Discard changes (careful)
|
||||||
|
- q
|
||||||
|
Quit Magit buffer
|
||||||
79
Technical/Emacs/Org Noter.md
Normal file
79
Technical/Emacs/Org Noter.md
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
****For the config, see [[Emacs Config]] for the Source of truth****
|
||||||
|
|
||||||
|
In order to create notes for a book:
|
||||||
|
|
||||||
|
1. Create an empty org roam file
|
||||||
|
2. Add the relative file path in metadata for that node, with the title `NOTER_DOCUMENT`
|
||||||
|
See the example below (auto save and page will get filled in)
|
||||||
|
3. Hover over the Notes heading created and click `SPC-n n` to open org noter
|
||||||
|
|
||||||
|
<!-- end list -->
|
||||||
|
|
||||||
|
``` org
|
||||||
|
* Ikigai Notes
|
||||||
|
:PROPERTIES:
|
||||||
|
:NOTER_DOCUMENT: ../../../pdfs/_misc/Ikigai.pdf
|
||||||
|
:NOTER_AUTO_SAVE_LAST_LOCATION: t
|
||||||
|
:NOTER_PAGE: 61
|
||||||
|
:END:
|
||||||
|
```
|
||||||
|
|
||||||
|
# Config needed:
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
|
||||||
|
(setq org-noter-notes-search-path
|
||||||
|
'("D:\\_nextcloud\\master-folder\\org_files\\org_roam\\"))
|
||||||
|
(setq org-noter-always-create-frame nil)
|
||||||
|
(setq org-noter-property-doc-file "NOTER_DOCUMENT")
|
||||||
|
|
||||||
|
(require 'pdf-tools)
|
||||||
|
(pdf-tools-install)
|
||||||
|
(use-package pdf-view-restore
|
||||||
|
:after pdf-tools
|
||||||
|
:config
|
||||||
|
(add-hook 'pdf-view-mode-hook 'pdf-view-restore-mode)
|
||||||
|
(setq pdf-view-restore-filename "~/.emacs.d/.pdf-view-restore"))
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
For non pdfs notes:
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
|
||||||
|
(setq org-capture-templates
|
||||||
|
'(("d" "Daily TODO" entry
|
||||||
|
(file+headline (lambda () (z/find-current-week-file)) "TODOs")
|
||||||
|
"* TODO %?\nSCHEDULED: %t\n"
|
||||||
|
:empty-lines 1)
|
||||||
|
|
||||||
|
("c" "Add to calendar.org" entry
|
||||||
|
(file+headline "~/master-folder/org_files/calendar.org" "Calendar")
|
||||||
|
"* TODO %?\nSCHEDULED: %^t\n"
|
||||||
|
:empty-lines 1)
|
||||||
|
|
||||||
|
("n" "Add notes" entry
|
||||||
|
(file+headline (lambda () (z/find-current-week-file)) "Notes")
|
||||||
|
"* Note: %?\n"
|
||||||
|
:empty-lines 1)
|
||||||
|
|
||||||
|
("w" "Weekly Review" entry
|
||||||
|
(file+function (lambda () (z/find-current-week-file)) z/goto-weekly-review)
|
||||||
|
"* Review: %?\nEntered on %U\n"
|
||||||
|
:empty-lines 1)
|
||||||
|
|
||||||
|
("b" "Book Note" entry
|
||||||
|
(file+headline (lambda () (buffer-file-name)) "Notes")
|
||||||
|
"** p. %^{Page}\n[%^{Type|Insight|Important|Quote|Confusing}] %?\n")
|
||||||
|
))
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Keybindings:
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
(dt/leader-keys
|
||||||
|
"n" '(:ignore t :wk "Org Noter")
|
||||||
|
"n n" '(org-noter :wk "Open org-noter")
|
||||||
|
)
|
||||||
|
```
|
||||||
163
Technical/Emacs/Org Publish.md
Normal file
163
Technical/Emacs/Org Publish.md
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
# How to insert footnotes:
|
||||||
|
|
||||||
|
The Org website\[1\] now looks a lot better than it used to.
|
||||||
|
|
||||||
|
# Resources:
|
||||||
|
|
||||||
|
<https://ogbe.net/blog/blogging_with_org>
|
||||||
|
|
||||||
|
<https://systemcrafters.net/publishing-websites-with-org-mode/building-the-site/>
|
||||||
|
|
||||||
|
<https://orgmode.org/manual/Publishing-options.html>
|
||||||
|
|
||||||
|
<https://www.orgroam.com/manual.html#Configuration>
|
||||||
|
|
||||||
|
<https://www.reddit.com/r/emacs/comments/1j7febh/my_static_website_is_generated_from_org_mode_and/?chainedPosts=t3_116yit2>
|
||||||
|
|
||||||
|
<https://orgmode.org/worg/org-tutorials/org-publish-html-tutorial.html>
|
||||||
|
|
||||||
|
<https://taingram.org/blog/org-mode-blog.html>
|
||||||
|
|
||||||
|
<https://karthinks.com/software/emacs-window-management-almanac/>
|
||||||
|
|
||||||
|
<https://gongzhitaao.org/orgcss/>
|
||||||
|
|
||||||
|
<https://ogbe.net/blog/emacs_org_static_site>
|
||||||
|
|
||||||
|
<https://edwardtufte.github.io/tufte-css/>
|
||||||
|
|
||||||
|
<https://zilongli.org/code/org-tufte-example>
|
||||||
|
|
||||||
|
# Extra snippets of code:
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
;; Commented out things:
|
||||||
|
|
||||||
|
;; (defvar z-head
|
||||||
|
;; "<link rel=\"stylesheet\" type=\"text/css\" href=\"https://gongzhitaao.org/orgcss/org.css\" />
|
||||||
|
;; <link rel=\"stylesheet\" href=\"/style.css\" />")
|
||||||
|
|
||||||
|
;; (setq org-html-validation-link nil
|
||||||
|
;; org-html-head-include-scripts nil
|
||||||
|
;; org-html-head-include-default-style nil
|
||||||
|
;; org-html-head z-head)
|
||||||
|
|
||||||
|
;; (type-of (car '(rose violet daisy buttercup)))
|
||||||
|
;; (cadr (assoc "FILETAGS" (org-collect-keywords '("FILETAGS"))))
|
||||||
|
|
||||||
|
;; (let ((list '(("post1.org") ("post2.org"))))
|
||||||
|
;; (mapconcat (lambda (entry)
|
||||||
|
;; (format "- [[file:%s]]" (car entry)))
|
||||||
|
;; list
|
||||||
|
;; "\n"))
|
||||||
|
|
||||||
|
|
||||||
|
;; ("website"
|
||||||
|
;; :components ("org-main" "org-categories-sitemap" "org-assets" "org-posts" "org-blogs" "org-static"))
|
||||||
|
;; :html-head "
|
||||||
|
;; <link rel=\"stylesheet\" type=\"text/css\" href=\"https://gongzhitaao.org/orgcss/org.css\" />
|
||||||
|
;; <link rel=\"stylesheet\" href=\"../style.css\" />
|
||||||
|
;; <script src=\"../script.js\" defer></script>
|
||||||
|
;; "
|
||||||
|
|
||||||
|
|
||||||
|
(defun z/write-tag-pages ()
|
||||||
|
"Generate tags/*.org pages listing posts for each tag."
|
||||||
|
(let* ((site-root (expand-file-name "~/master-folder/org_files/org_web/"))
|
||||||
|
(tags-dir (expand-file-name "tags" site-root)))
|
||||||
|
(unless (file-directory-p tags-dir)
|
||||||
|
(make-directory tags-dir t))
|
||||||
|
(let ((idx (z/gather-tag-index)))
|
||||||
|
(maphash
|
||||||
|
(lambda (tag items)
|
||||||
|
(let* ((slug (z/tag-slug tag))
|
||||||
|
(outfile (expand-file-name (format "%s.org" slug) tags-dir)))
|
||||||
|
(with-temp-file outfile
|
||||||
|
(insert (format "#+TITLE: Tag: %s\n#+OPTIONS: toc:nil num:nil title:nil \n\n* Posts tagged %s\n"
|
||||||
|
tag tag))
|
||||||
|
;; sort newest first if DATE present
|
||||||
|
(setq items (sort items (lambda (a b) (string> (nth 2 a) (nth 2 b)))))
|
||||||
|
(dolist (it items)
|
||||||
|
(let* ((file (nth 0 it))
|
||||||
|
(title (nth 1 it))
|
||||||
|
(rel (file-relative-name file tags-dir)))
|
||||||
|
;; link to the source .org; org-publish will rewrite to the .html
|
||||||
|
(insert (format "- [[file:%s][%s]]\n" rel title)))))))
|
||||||
|
idx)
|
||||||
|
;; Also write an index page listing all tag pages
|
||||||
|
;; (let* ((all ()))
|
||||||
|
;; (maphash (lambda (tag _items) (push tag all)) (z/gather-tag-index))
|
||||||
|
;; (with-temp-file (expand-file-name "index.org" (expand-file-name "tags" site-root))
|
||||||
|
;; (insert "#+TITLE: All Tags\n#+OPTIONS: toc:nil num:nil\n\n* Tags\n")
|
||||||
|
;; (dolist (tag (sort all #'string-lessp))
|
||||||
|
;; (insert (format "- [[file:%s.org][%s]]\n" (z/tag-slug tag) tag)))))
|
||||||
|
|
||||||
|
)))
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Options stuff:
|
||||||
|
|
||||||
|
In Org mode, you can control per-file publishing behavior using special keywords (known as ****file-local metadata****) at the top of the file. These are written like:
|
||||||
|
|
||||||
|
\`\`\`org
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
—
|
||||||
|
|
||||||
|
Here are ****useful options**** you can set ****per file****:
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| -------------------------------- | --------------------------------------------------------- |
|
||||||
|
| Keyword | Purpose |
|
||||||
|
| ——————– | —————————————————– |
|
||||||
|
| \`\#+TITLE:\` | Title of the document |
|
||||||
|
| \`\#+AUTHOR:\` | Author name |
|
||||||
|
| \`\#+EMAIL:\` | Email address |
|
||||||
|
| \`\#+DATE:\` | Date |
|
||||||
|
| \`\#+OPTIONS:\` | Control export behavior (e.g. toc, num, author, etc.) |
|
||||||
|
| \`\#+HTML<sub>HEAD</sub>:\` | Custom HTML for \`\<head\>\` (like styles or scripts) |
|
||||||
|
| \`\#+HTML<sub>HEADEXTRA</sub>:\` | Extra HTML inserted in \`\<head\>\` |
|
||||||
|
| \`\#+HTML<sub>PREAMBLE</sub>:\` | Whether to include the preamble (\`t\`, \`nil\`, or HTML) |
|
||||||
|
| \`\#+HTML<sub>POSTAMBLE</sub>:\` | Same but for postamble |
|
||||||
|
| \`\#+LANGUAGE:\` | Language used for export |
|
||||||
|
| \`\#+DESCRIPTION:\` | Adds a \`\<meta name="description"\>\` tag |
|
||||||
|
| \`\#+KEYWORDS:\` | Adds a \`\<meta name="keywords"\>\` tag |
|
||||||
|
|
||||||
|
—
|
||||||
|
|
||||||
|
\#\#\# ✅ Example: Disable TOC and Section Numbers **for one file**
|
||||||
|
|
||||||
|
You can use the \`\#+OPTIONS:\` line like so:
|
||||||
|
|
||||||
|
\`\`\`org
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
This disables the Table of Contents and section numbers ****just for that file****.
|
||||||
|
|
||||||
|
Your updated file might look like this:
|
||||||
|
|
||||||
|
``` commonlisp
|
||||||
|
#+TITLE: Welcome to My Org Website
|
||||||
|
#+OPTIONS: toc:nil num:nil
|
||||||
|
#+HTML_HEAD: <link rel="stylesheet" href="style.css">
|
||||||
|
#+HTML_HEAD: <script src="theme-toggle.js" defer></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's a reference for some values you can tweak via \`\#+OPTIONS:\`:
|
||||||
|
|
||||||
|
| | | |
|
||||||
|
| --------------- | ------------------------------------ | ------------------ |
|
||||||
|
| Option | Meaning | Example |
|
||||||
|
| ————- | ——————————– | —————- |
|
||||||
|
| \`toc:\` | Table of contents (\`t\` or \`nil\`) | \`toc:nil\` |
|
||||||
|
| \`num:\` | Section numbering | \`num:nil\` |
|
||||||
|
| \`author:\` | Show author name | \`author:nil\` |
|
||||||
|
| \`creator:\` | Show Emacs/Org creator info | \`creator:nil\` |
|
||||||
|
| \`date:\` | Show date | \`date:nil\` |
|
||||||
|
| \`html-style:\` | Disable default style | \`html-style:nil\` |
|
||||||
|
|
||||||
|
1. The link is: <https://orgmode.org>
|
||||||
38
Technical/Emacs/Org Roam.md
Normal file
38
Technical/Emacs/Org Roam.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
This base will contain information on org roam. Although its closely linked to the [[GTD]] node, that one will contain articles, notes and videos related to how to get things done.
|
||||||
|
|
||||||
|
# Main
|
||||||
|
|
||||||
|
[User Manual](https://www.orgroam.com/manual.html)
|
||||||
|
|
||||||
|
Here is the current config in my init.el
|
||||||
|
|
||||||
|
``` elisp
|
||||||
|
;;roam config
|
||||||
|
(use-package org-roam
|
||||||
|
:ensure t
|
||||||
|
:custom
|
||||||
|
(org-roam-directory "~/master-folder/org_files/org_roam")
|
||||||
|
:bind (("C-c n l" . org-roam-buffer-toggle)
|
||||||
|
("C-c n f" . org-roam-node-find)
|
||||||
|
("C-c n i" . org-roam-node-insert))
|
||||||
|
:config
|
||||||
|
(org-roam-setup))
|
||||||
|
```
|
||||||
|
|
||||||
|
- for the org-roam-ui (the graphical interface for nodes): [org-roam-ui](https://github.com/org-roam/org-roam-ui)
|
||||||
|
- an article: [example-org-roam-workflow.org](https://gist.github.com/nickanderson/00005b5b03e323a65ada98c5fa5ebb11)
|
||||||
|
- An article to read: [How i use org in 2023](https://cmdln.org/2023/03/25/how-i-org-in-2023/)
|
||||||
|
- An article: [org-roam-guide](https://jethrokuan.github.io/org-roam-guide/)
|
||||||
|
- An article: [Own your second brain](https://ianjones.info/own-your-second-brain)
|
||||||
|
- article: [my-org-roam-workflows-for-taking-notes-and-writing-articles](https://honnef.co/articles/my-org-roam-workflows-for-taking-notes-and-writing-articles/)
|
||||||
|
- article: [how-i-use-org-roam-to-takes-notes-for-cs](https://michaelneuper.com/posts/how-i-use-org-roam-to-takes-notes-for-cs/)
|
||||||
|
|
||||||
|
The command below starts emacs in a terminal and kills it after 10 seconds. this is useful for when I add a note in the main machine, but the server's DB doesn't pick up the changes until Emacs is restarted in the server.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
timeout 10 emacs >/dev/null 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
# Articles
|
||||||
|
|
||||||
|
- <https://orgmode.org/worg/index.html>
|
||||||
Reference in New Issue
Block a user