site

The source code for ntgs.me website
Log | Files | Refs | README | LICENSE

commit c11a8a39ffb0eb5abd1a4066b8d20a153bb912ec
Author: Shivakumar <[email protected]>
Date:   Mon, 12 Oct 2026 19:42:29 +0530

Initial Commit

Signed-off-by: Shivakumar <[email protected]>

Diffstat:
A.gitignore | 2++
Aorg/blog/index.org | 9+++++++++
Aorg/blog/learning-org.org | 309+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aorg/contact.org | 43+++++++++++++++++++++++++++++++++++++++++++
Aorg/index.org | 28++++++++++++++++++++++++++++
Aorg/sitemap.org | 2++
Aorg/style.css | 272+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aorg/theme.js | 12++++++++++++
Apublish.el | 180+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 857 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +html/ +*~ diff --git a/org/blog/index.org b/org/blog/index.org @@ -0,0 +1,9 @@ +#+TITLE: Blog + +:PROPERTIES: +#+HTML_HEAD_EXTRA: <style>a:link, a:visited { text-decoration: none; } .content h1, .content h4 { border-bottom: none; padding-bottom: 2mm; } .content h2 { border-bottom: 3px dotted var(--fg); }</style> +:END: + +#+begin_blog-full +#+INCLUDE: "../sitemap.org" +#+end_blog-full diff --git a/org/blog/learning-org.org b/org/blog/learning-org.org @@ -0,0 +1,309 @@ +#+TITLE: Learning org mode +#+AUTHOR: Shivakumar +#+EMAIL: shivakumar AT disroot DOT org +#+DATE: <2026-10-08 Thu> + +This is *bold text*. And this is /italics/. +For underlines you have to, well _underline_. +For links we have to use [[]], like this [[https://example.com][example]]. +Unordered lists start with '*', '+' or '-'. +Ordered lists start with '1.' or '1)'. + +* FUNDAMENTAL +#+BEGIN_SRC org + * TOP LEVEL HEADING + ** Second level heading + some text under second level heading. + *** Third level heading + some text under third level heading. + * ANOTHER TOP LEVEL HEADING +#+END_SRC +This is *bold text*. And this is /italics/. +For underlines you have to, well _underline_. +For links we have to use [[]], like this [[https://example.com][example]]. +* LISTS EXAMPLE +Unordered lists start with '*', '+' or '-'. +Ordered lists start with '1.' or '1)'. + +For example: +#+BEGIN_EXAMPLE + * List of things on my table + My table is messy rn. But, we'll ignore it. + 1. My laptop + 2. My mobile phone + 3. Misc + + My wallet + + Lot of instant coffee sackets + + Sunscreen, etc + Things to keep in mind: + - My wallet :: Actually a card holder. + - Instant coffee :: 3-in-1 instant coffee. +#+END_EXAMPLE +* TABLES +Org mode supports tables. + +example: + | slno | Name | Phone | + |------+------------+--------| + | 1 | Shivakumar | 123456 | + | 2 | Tanaya | 654321 | + +'C-c |' to create a table. +* HYPERLINKS +'C-c C-l' to insert any type of links. + +*** Internal Links +If you want to link something from the current document. We use ~‘[[*headline][description]]’~ like this [[*External Links][External Links]]. + +*** External Links +Org supports differnt types links like links to file, links to website, etc. For more information refer [[info:orgguide#Hyperlinks][info "(orgguide) Hyperlinks"]]. + +To go to more specific section of a file we use double colon '::'. +Like this: + 'file:~/code/main.c::255' +* TODO's +Any headline becomes a TODO item when it starts with the word ‘TODO’. + +To toggle Todo we have to 'C-c C-t'. It also cycles: +#+begin_src + ,-> (unmarked) -> TODO -> DONE --. + '--------------------------------' +#+END_SRC +We can also move through the cycle with S-<RIGHT/LEFT>. + +example: +*** TODO This is an example + +‘M-x org-agenda t’ (‘org-todo-list’) to track all global TODO's in one buffer. +‘S-M-<RET>’ (‘org-insert-todo-heading’) to insert TODO just below. + +To add todo in the current file we use this '#+TODO: TODO(t) | DONE(d)' + +*** Progress logging +‘C-u C-c C-t’ (‘org-todo’) to log the time with the TODO. + +*** Tasks to subtasks +Breaking down tasks to subtasks is done as follows: + +example: +#+BEGIN_SRC org + * Organize Party [33%] + ** TODO Call people [1/2] + *** TODO Peter + *** DONE Sarah + ** TODO Buy food + ** DONE Talk to neighbor +#+END_SRC +Add [%] or [/] to these to track the cookies, and can be updated with ‘C-c C-c’. + +*** Checkboxes +Checkboxes are done by adding [ ] infront of the TODO. But, checkboxes won't be seen in the global TODO list. + +‘C-c C-c’, ‘C-u C-c C-c’ (‘org-toggle-checkbox’) to toggle checkboxes. +* TAGS +Tags are labels added to headlines to refer and cross-corelate information. + +Tags must be wrapped with colon (::). +example: ‘:work:’ + +** Tag inheritance +Tags are hierarchical structures of outline trees. i.e. if the heading contains the tag then the subheadings will contain the same parent tag. + +example: +#+BEGIN_SRC org + * Meeting with the French group :work: + ** Summary by Frank :boss:notes: + *** TODO Prepare slides for him :action: +#+END_SRC + + +In here the TODO heading will contain ‘work’, ‘boss’, ‘notes’, and ‘action’. + +To make sure all entires in the current file should inherit certain tags, we need to set: + + #+FILETAGS: :all:entires:contains:these:tags + +** Setting tags +M-<TAB> offers completion for the tags. To insert a Tag we need to: + +‘C-c C-q’ (‘org-set-tags-command’) +‘C-c C-c’ (‘org-set-tags-command’) + when the point is in a headline, it does the same as the above command. + +We can set default tags for a given file using the ‘TAGS’ keyword, like + + #+TAGS: @work @home @boom + #+TAGS: laptop car pc sailboat +** Tag groups +you can add a group tag by using brackets and inserting a colon in between the tag and the related item. + +example: + #+TAGS: [ group : tag1 tag2 ] + #+TAGS: { group : @tag1 @tag2 } +** Tag search +‘C-c / m’ or ‘C-c \’ (‘org-match-sparse-tree’) + Creates a sparse tree with all headlines matching a tag search. + +Refer to [[info:orgguide#Tag searches][info "(orgguide)#Tag searches"]]. +* PROPERTIES +Properties are key-value pairs associated with an entry. They live in a special drawer with the name ‘PROPERTIES’. + +example: + * Some headline + ** Some sub-headline + :PROPERTIES: + :Title: Leanring org mode + :Author: Shivakumar + :Key: Value + :END: + +If we want to define allowed values for a particular property ‘XYZ’, we have to append ~‘_ALL’~ to it, hence it becomes ~‘XYZ_ALL’~. And if we want all the entires to inherit the some property we have to set it in the root level. Like this: + + #+PROPERTY: XYZ_ALL 1 2 3 4 + +To set a property we use this command: + ‘C-c C-x p’ (‘org-set-property’) + +And, to remove a property we use: + ‘C-c C-c d’ (‘org-delete-property’) +* DATES AND TIME +We can use timestamps in multiple things like planning, in TODO’s, schedule tasks, set deadlines, track time, and more. + +** Timestamps +A simple timestamp can be applied to an item by assigning date/time to it. + +example: + * Some headline + <2026-09-05 > + +The above is a plain timestamp. + +Timestamps with a repeater interval: + +example: + * Gotta do some task + <2007-05-16 Wed 12:30 +1w> + +For more formats refer [[info:orgguide#Timestamps][info "(orgguide)#Timestamps"]]. +** Creating timestamps +‘C-c .’ (‘org-timestamp’) + To create a timestamp. When the point is a existing timestamp it would modify the timestamp. + +‘C-c !’ (‘org-timestamp-inactive’) + Creates an inactive timestamp, i.e. it won’t be added in the agenda entry. + +For more info refer [[info:orgguide#Creating Timestamps][info "(orgguide)#Creating Timestamps"]]. +** Deadlines and Scheduling + +‘C-c C-d’ (‘org-deadline’) + To insert a deadline to the task. + +example: + *** TODO Do some task + DEADLINE: <2026-09-21 Mon> + +‘C-c C-s’ (‘org-schedule’) + Ditto, but for schedule. +** Clocking Work Time +Org mode allows you to clock the time you spend on specific tasks in a project. + +For commands refer [[info:orgguide#Clocking Work Time][info "(orgguide)#Clocking Work Time"]]. +* TODO Capture, Refile, Archive +** Capture +Captures lets you quickly jot down or /capture/ the ideas or interruptions of your workflow. Its like quick note. You can define templates for your captures. + +To set up capture refer [[info:orgguide#Setting up capture][info "(orgguide)#Setting up capture"]]. + +‘M-x org-capture’ (‘org-capture’) + This command lets you create and note down the capture. You will be narrowed buffer to edit. + +‘C-c C-c’ (‘org-capture-finalize’) + This command is to lets you finilize the noting down the buffer. This lets you confirm the edit you were noting down and then resume the work without any further distraction. + +‘C-c C-k’ (‘org-capture-kill’) + Abort the capture process and return to the previous state. +** Capture templates +You can use templates to capture different types of capture items, and to different capture locations. You can set this for general TODO tasks. etc. + +For more info refer [[info:orgguide#Capture templates][info "(orgguide)#Capture templates"]] +* TODO ... +* WORKING WITH SOURCE CODE +Org mode lets you include source code. + +A source code block conforms to this structure: +#+BEGIN_VERSE + #+NAME: <name> + ~#+BEGIN_SRC~ <language> <switches> <header arguments> + <body> + ~#+END_SRC~ +#+END_VERSE + +For more info refer [[info:orgguide#Working with Source Code][Info "orgguide(#Working with Source Code)"]] + +Use ‘C-c '’ to edit the current source code block. + +** Header arguments +A header argument is specified with an initial colon followed by the argument's name in lowercase. + + +- System-wide header arguments :: + Those are specified by customizing ‘org-babel-default-header-args’ variable. + +- Header arguments in properties :: + You can set them using ‘header-args’ property or ‘header-args:LANG’ for language LANG. + +- Header arguments in code blocks :: + Header arguments are most commonly set at the source code block level, on the ~‘BEGIN_SRC’~ line: + + #+BEGIN_VERSE + #+NAME: factorial + ~#+BEGIN_SRC~ haskell :results silent :exports code :var n=0 + fac 0 = 1 + fac n = n * fac (n-1) + ~#+END_SRC~ + #+END_VERSE + Code block header arguments can span multiple lines using ‘HEADER’ keyword on each line. +** Evaluating code blocks +Use ‘C-c C-c’ to evaluate the current code block and then insert the results in the same org document. + +example: +#+NAME: Example +#+BEGIN_SRC emacs-lisp :exports both :eval no-export + (+ 2 2 2) +#+END_SRC + +Output: +#+RESULTS: Example +: 6 + +We can use this as an replacement for jupyter notebooks, cause jupyter is heavy and bloated. + +Python example: +#+NAME: Python example +#+BEGIN_SRC python :results output :exports both :eval no-export + def main(): + print("This is run from org!") + + if __name__ == "__main__": + main() +#+END_SRC + +Output: +#+RESULTS: Python example +: This is run from org! + +** Result of Evaluation +Org handles the results of the code snippet by the headers given to it. The primary determinant, however, is the ‘results’ header argument. It controls the _collection_, _type_, _format_, and _handling_ of code block results. + +- Collection :: + How the result should be collected from the code block. +- Type :: + What result type to except from the execution of the code block. You may choose among ‘table’, ‘list’, ‘scalar’, and ‘file’. +- Format :: + How org processes the result. It can be ‘code’, ‘drawer’, ‘html’, etc. +- Handling :: + How to insert the result once formated. Allowed values are ‘silent’, ‘replace’ (default), ‘append’ and ‘prepend’. +** Exporting code blocks +It is possible to export the _code_ of the code block, the result of the code block execution. +** Extracting source code +Use ‘C-c C-v t’ to create pure source code files by extracting code from source blocks in the current buffer. diff --git a/org/contact.org b/org/contact.org @@ -0,0 +1,43 @@ +#+TITLE: How to contact me? +#+AUTHOR: Shivakumar +#+EMAIL: shivakumar AT disroot DOT org + +:PROPERTIES: +#+OPTIONS: ^:nil +#+HTML_HEAD_EXTRA: <style>a:link, a:visited { text-decoration: none; } .content h1, .content h2 { border-bottom: 1px solid var(--fg); } td { padding: 0.6em; }</style> +:END: + +You may email me at [[mailto:[email protected]][shivakumar AT disroot DOT org]]. Be warned that I may be slow to respond, I don't check my emails often ;-;. + +Feedbacks on how to improve the site are welcome. + +** My socials +You can ping me on discord, I would respond almost immediately. I keep github only as my backup git registry, and keep my important and most proud projects in [[https://git.ntgs.me/][my personal git server]]. Check it out :D. +| Discord | [[https://discord.com/users/639147288115150848][dr_tox_ic]] | +| Github | [[https://github.com/drtoxic69][drtoxic69]] | +| Mastodon | [[https://mastodon.social/@shivakumarjm][shivakumarjm]] | + +** My PGP public key +You can get my public key from the following command: + +#+BEGIN_SRC +gpg --receive-keys 9464CC8ED8B73102 +#+END_SRC + +or if you can copy the key: +#+BEGIN_SRC +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEabwQ4RYJKwYBBAHaRw8BAQdAz5phOPgBZnvj5X/wIotLw1f6OKHT/VBN0ExF +cN9jnLC0I1NoaXZha3VtYXIgPHNoaXZha3VtYXJAZGlzcm9vdC5vcmc+iK8EExYK +AFcWIQSYkN1VkARhbcAc8ZyUZMyO2LcxAgUCabwQ4RsUgAAAAAAEAA5tYW51Miwy +LjUrMS4xMiwwLDMCGwMFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQlGTM +jti3MQInJAEA9Gg0DfG7iBQrGPZS0oKUpB008+v3cM94gVt03G60atYA/11zHZpE +8VJZ1q22Dvlvl401BYnys2NCVHUrSn+fxigMuDgEabwQ4RIKKwYBBAGXVQEFAQEH +QIAHr9uuh09K4y03ISbGV/cjBpb5601B45rHf6y7C/kcAwEIB4iUBBgWCgA8FiEE +mJDdVZAEYW3AHPGclGTMjti3MQIFAmm8EOEbFIAAAAAABAAObWFudTIsMi41KzEu +MTIsMCwzAhsMAAoJEJRkzI7YtzEC6u4BAMhqIqoUYxJnbF3ljkRCZMmBVlqGrLYZ +8Esfl4sj/J5HAQDqwSJCwMK+1Mibj59lUbNy7eLB7Uwm/zKXFabpixPWBg== +=Q3GA +-----END PGP PUBLIC KEY BLOCK----- +#+END_SRC diff --git a/org/index.org b/org/index.org @@ -0,0 +1,28 @@ +#+TITLE: Shivakumar +#+AUTHOR: Shivakumar +#+EMAIL: shivakumar AT disroot DOT org + +:PROPERTIES: +#+OPTIONS: ^:nil +#+HTML_HEAD_EXTRA: <style>a:link, a:visited { text-decoration: none; } .content h1 .content h4 { border-bottom: none; padding: 0; } .content h2 { border-bottom: 1px solid var(--fg); } td { padding: 0.6em;} </style> +:END: + +*Welcome to my website!* This website is where I write my [[https://ntgs.me/blog][blogs]], and host my [[https://git.ntgs.me/][git repos]]. This website is made using [[https://orgmode.org/manual/HTML-Export.html][Emacs Org-mode]]'s HTML export, and a little bit of CSS to not make it an eye-sore. + +I like to build my project from scratch, cause I believe that you do not completely understand it unless you build it. + +#+begin_quote +“What I cannot create, I do not understand.” /- Richard Feynman/ +#+end_quote + +I try to follow the [[https://suckless.org][suckless]] phylisophy for all my projects. I like to keep my projects minimal, and simple. + +* Projects +| [[https://git.ntgs.me/ecc][ECC]] | Elliptic Curve Cryptography library writen from scratch in python. | +| [[https://git.ntgs.me/certichain][certichain]] | Document anchoring and verification with blockchain written from scratch in go-lang. | +| [[https://git.ntgs.me/site][site]] | This website's source code. Written using emacs org-mode. | + +* Blogs +#+begin_blog-compact +#+INCLUDE: "sitemap.org" :lines "-6" +#+end_blog-compact diff --git a/org/sitemap.org b/org/sitemap.org @@ -0,0 +1,2 @@ +- @@html:<a class="entry-title" href="/blog/ecc-from-scratch.html">Building ECC from scratch</a> <time class="post-date" datetime="2026-10-11">2026-10-11</time>@@ +- @@html:<a class="entry-title" href="/blog/learning-org.html">Learning org mode</a> <time class="post-date" datetime="2026-10-08">2026-10-08</time>@@ diff --git a/org/style.css b/org/style.css @@ -0,0 +1,272 @@ +/* -*- tab-width: 8; indent-tabs-mode: nil; -*- */ + +:root { + --bg: #10100E; + --fg: #FFFFE6; + --line: #30302B; + --link: #8AA499; + --link-active: #C8899B; + --bright-red: #BF281B; + --red: #981E13; + --off-white: #FAF7F1; + --off-black: #0c0c0b; + --shadow: transparent; + --border-width: 0.25ex; +} + +html, body { + min-height: 100%; +} + +body { + display: flex; + flex-direction: column; + min-height: 100vh; + + font-family: monospace, "Iosevka"; + font-size: 120%; + max-width: 900px; + margin: 0 auto; + padding: 0 1em; + line-height: 1.6; + color: var(--fg); + background-color: var(--bg); +} + +.site-title { + font-family: "Iosevka"; + font-size: clamp(1.25em, 5vw, 2em); + color: var(--fg); + border-bottom: 2px solid var(--fg); +} + +.cursor { + color: var(--fg); + animation: blink 1s steps(1) infinite; +} + +@keyframes blink { + 50% { + visibility: hidden; + } +} + +@media (prefers-reduced-motion: reduce) { + .cursor { + animation: none; + } +} + +#site-header { + flex-shrink: 0; +} + +#site-nav { + display: flex; + align-items: center; + + font-family: monospace; + font-size: 140%; + margin-top: 0.5rem; + padding-bottom: 1rem; +} + +@media (max-width: 600px) { + #site-nav { + font-size: 110%; + flex-wrap: wrap; + gap: 0.25em 0.75em; + } +} + +#site-nav a { + color: var(--link); + text-decoration: none; +} + +#site-nav a:hover { + color: var(--bright-red); +} + +#site-nav .bracket { + color: var(--fg); +} + +.blog-compact ul, .blog-full ul { list-style: none; padding-left: 0; } +.blog-compact a:link, .blog-compact a:visited, +.blog-full a:link, .blog-full a:visited { text-decoration: none; } + +/* HOME blog index */ +.blog-compact li { font-size: 1.2em; display: flex; gap: 1ch; margin-bottom: 0; } +.blog-compact .post-date { order: -1; font-size: 1em; } +.blog-compact .post-date::before { content: "[ "; } +.blog-compact .post-date::after { content: " ]"; } + +/* BLOG index */ +.blog-full li { margin-bottom: 1.2em; } +.blog-full .entry-title { display: block; font-size: 1.5em; line-height: 1.3; } +.blog-full .post-date { display: block; font-size: 0.8em; } +.blog-full .post-date::before { content: "posted on "; } + +.theme-toggle { + position: absolute; + opacity: 0; + width: 1px; + height: 1px; +} + +.theme-label { + display: inline-flex; + align-items: center; + justify-content: center; + + margin-left: auto; + + width: 1.5em; + height: 1.5em; + + color: var(--link); + cursor: pointer; +} + +.theme-label:hover { + color: var(--bright-red); +} + +.icon { + width: 1em; + height: 1em; + + fill: none; + stroke: currentColor; + stroke-width: 2; + stroke-linecap: round; + stroke-linejoin: round; +} + +.icon-moon { + display: none; +} + +.theme-toggle:checked + .theme-label .icon-sun { + display: none; +} + +.theme-toggle:checked + .theme-label .icon-moon { + display: block; +} + +.theme-toggle:focus-visible + .theme-label { + outline: 1px dotted var(--fg); + outline-offset: 0.2em; +} + +body:has(.theme-toggle:checked) { + --bg: #FFFFE6; + --fg: #10100E; + --shadow: #10100E59; +} + +.sr { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +#content { + flex: 1; +} + +.title { + font-family: monospace; + font-size: 2.8em; + margin-top: 0; + color: var(--fg); +} + +.content h1 { + /* border-bottom: 2px solid var(--fg); */ + margin: 0; + border-bottom: none; +} + +.content h2, +.content h3, +.content h4, +.content h5, +.content h6 { + border-bottom: dotted var(--fg); + padding-bottom: 0.4ex; +} + +/* "# posted on …": dimmed like a code comment. Derived from --fg, + so it flips with the theme toggle by itself. */ +.post-date { + font-family: "Iosevka", monospace; + /* font-style: italic; */ + font-size: 0.90em; + color: var(--fg); + opacity: 0.75; +} + +p.post-date { + margin: 0.25em 0 1.5em; +} + +#content li { + margin-bottom: 0.6em; +} + +li .post-date { + display: block; +} + +a:link, +a:visited { + color: var(--link); +} + +a:hover, +a:active { + color: var(--bright-red); + text-decoration: none; +} + +table { + border-collapse: collapse; + border: var(--border-width) solid var(--fg); + box-shadow: 4px 4px 6px var(--shadow); +} + +th { + color: var(--fg); + background-color: var(--bg); + border: 1px solid var(--fg); + padding: 0.25em; +} + +td { + border: 1px solid var(--fg); + padding: 0.25em; +} + +pre { + font-family: "Iosevka"; + overflow: auto; + scrollbar-color: var(--line) var(--bg); + scrollbar-width: thin; + border: var(--border-width) solid var(--fg); + box-shadow: 4px 4px 6px var(--shadow); + padding: 1em; +} + +.todo { font-family: "Iosevka"; color: var(--bright-red); } +.done { font-family: "Iosevka"; color: var(--link); } + +footer { margin-top: auto; } diff --git a/org/theme.js b/org/theme.js @@ -0,0 +1,12 @@ +const toggle = document.getElementById("light-mode"); + +if (localStorage.getItem("theme") === "light") { + toggle.checked = true; +} + +toggle.addEventListener("change", () => { + localStorage.setItem( + "theme", + toggle.checked ? "light" : "dark" + ); +}); diff --git a/publish.el b/publish.el @@ -0,0 +1,180 @@ +;;; publish.el --- generate and publish my blog -*- lexical-binding: t; -*- + +;; Copyright (C) 2026 Shivakumar + +;; Author: Shivakumar <shivakumar AT disroot DOT org> +;; Keywords: org-mode, html + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This file is used to generate my HTMLs from my Org files for my blog +;; website. These functions works for my liking and my setup, but it can +;; be helpful for others to refer. + +;;; Code: + +(require 'org) +(require 'ox-publish) +(require 'htmlize) + +(defvar my/root + (file-name-directory (or load-file-name buffer-file-name))) + +(defun my/preamble-path (info) + (let ((rel (file-relative-name (plist-get info :input-file) + (expand-file-name "org/" my/root)))) + (cond ((string= rel "index.org") "~") + ((string= rel "contact.org") "~/contact") + ((string-prefix-p "blog/" rel) "~/blog") + (t (concat "~/" (or (file-name-directory rel) "")))))) + +(defun my/preamble (info) + (format "<header id=\"site-header\"> + <div class=\"site-title\"> + [ shivakumar@ntgs %s ]$ <span class=\"cursor\">\u2588</span> + </div> + <nav id=\"site-nav\"> + <a href=\"/\"><span class=\"bracket\">[</span> HOME <span class=\"bracket\">]</span></a> + <a href=\"/blog/\"><span class=\"bracket\">[</span> BLOG <span class=\"bracket\">]</span></a> + <a href=\"https://git.ntgs.me/\"><span class=\"bracket\">[</span> GIT <span class=\"bracket\">]</span></a> + <a href=\"/contact.html\"><span class=\"bracket\">[</span> CONTACT <span class=\"bracket\">]</span></a> + + <input id=\"light-mode\" class=\"theme-toggle\" type=\"checkbox\"> + <label for=\"light-mode\" class=\"theme-label\"> + <svg class=\"icon icon-sun\" viewBox=\"0 0 24 24\" aria-hidden=\"true\"> + <circle cx=\"12\" cy=\"12\" r=\"4\"></circle> + <path d=\"M12 2v2\"></path> + <path d=\"M12 20v2\"></path> + <path d=\"m4.93 4.93 1.41 1.41\"></path> + <path d=\"m17.66 17.66 1.41 1.41\"></path> + <path d=\"M2 12h2\"></path> + <path d=\"M20 12h2\"></path> + <path d=\"m6.34 17.66-1.41 1.41\"></path> + <path d=\"m19.07 4.93-1.41 1.41\"></path> + </svg> + <svg class=\"icon icon-moon\" viewBox=\"0 0 24 24\" aria-hidden=\"true\"> + <path d=\"M21 12.79A9 9 0 1 1 11.21 3 + A7 7 0 0 0 21 12.79z\"></path> + </svg> + + </label> + </nav> +</header> +" (my/preamble-path info))) + +(setq org-export-use-babel nil) + +(defun my/tab-title (output backend info) + "Wrap the <title> Org already rendered as \"X | ntgs.me\"." + (when (org-export-derived-backend-p backend 'html) + (let* ((rel (file-relative-name (plist-get info :input-file) + (expand-file-name "org/" my/root))) + (new (cond ((string= rel "index.org") "~") + ((string= rel "contact.org") "contact") + ((string= rel "blog/index.org") "blog") + (t "\\1")))) + (replace-regexp-in-string + "<title>\\([^<]*\\)</title>" + (format "<title>%s | ntgs.me</title>" new) + output)))) + +(add-to-list 'org-export-filter-final-output-functions #'my/tab-title) + +(defun my/post-date (output backend info) + (when (org-export-derived-backend-p backend 'html) + (let ((date (org-export-data (org-export-get-date info "%Y-%m-%d") info))) + (if (string-empty-p date) + output + (replace-regexp-in-string + "\\(<h1 class=\"title\">\\(?:.\\|\n\\)*?</h1>\\)" + (format "\\1\n<p class=\"post-date\">posted on %s</p>" date) + output t))))) + +(add-to-list 'org-export-filter-final-output-functions #'my/post-date) + +(defun my/sitemap-entry (entry _style project) + (if (string= entry "index.org") + "" + (let ((date (format-time-string "%Y-%m-%d" (org-publish-find-date entry project)))) + (format "@@html:<a class=\"entry-title\" href=\"/blog/%s.html\">%s</a> <time class=\"post-date\" datetime=\"%s\">%s</time>@@" + (file-name-sans-extension entry) + (org-html-encode-plain-text (org-publish-find-title entry project)) + date date)))) + +(defun my/sitemap (_title list) + (concat (mapconcat (lambda (item) (concat "- " (car item))) + (cl-remove-if (lambda (item) (string-empty-p (car item))) + (cdr list)) + "\n") + "\n")) + +(defun my/publish-page (plist filename pub-dir) + (unless (string= (file-name-nondirectory filename) "sitemap.org") + (org-html-publish-to-html plist filename pub-dir))) + +(defvar my/html-opts + '(:publishing-function org-html-publish-to-html + :html-preamble my/preamble + :html-postamble "<footer> + <hr> + <p>Created by a human.</p> + <p>Copyright © 2026 Shivakumar AT disroot DOT org.</p> + </footer>" + :html-head-include-default-style nil + :html-head-include-scripts nil + :html-head "<link rel=\"stylesheet\" type=\"text/css\" href=\"/style.css\" /> + <script src=\"/theme.js\" defer></script>" + :section-numbers nil + :with-toc nil)) + +(setq org-publish-project-alist + `(("pages" + :base-directory ,(expand-file-name "org/" my/root) + :base-extension "org" + :recursive nil + :publishing-directory ,(expand-file-name "html/" my/root) + ,@my/html-opts) + + ("blog" + :base-directory ,(expand-file-name "org/blog/" my/root) + :base-extension "org" + :recursive nil + :publishing-directory ,(expand-file-name "html/blog/" my/root) + :auto-sitemap t + :sitemap-filename "../sitemap.org" + :sitemap-function my/sitemap + :sitemap-sort-files anti-chronologically + :sitemap-format-entry my/sitemap-entry + ,@my/html-opts) + + ("static" + :base-directory ,(expand-file-name "org/" my/root) + :base-extension "css\\|js" + :recursive nil + :publishing-directory ,(expand-file-name "html/" my/root) + :publishing-function org-publish-attachment) + + ("site" + :components ("blog" "pages" "static")))) + +;;;###autoload +(defun publish-blog () + (interactive) + (org-publish "site" t) + (message "Site published to %s" (expand-file-name "html/" my/root))) + +(provide 'publish) +;;; publish.el ends here