--- /dev/null
+Except where otherwise noted, site content is licensed under the Creative Commons Attribution 4.0 International license:
+https://creativecommons.org/licenses/by/4.0/
+
+All else is licensed under the GNU General Public License, either version 3 of the License, or (at your option) any later version:
+https://www.gnu.org/licenses/gpl-3.0.en.html#license-text
-@import url('https://fonts.googleapis.com/css2?family=Fira+Mono:wght@400&family=Noto+Sans:wght@400;700&display=swap');
+@import url('https://fonts.googleapis.com/css?family=Cantarell');
+@import url('https://fonts.googleapis.com/css?family=Fira Mono');
html, body, p, ol, ul, li, dl, dt, dd,
blockquote, figure, fieldset, legend, textarea,
background: #151515;
color: #dedede;
font-family: "Noto Sans",sans-serif;
- font-size: 106%;
+ font-size: 106%;
line-height: 1.5;
word-wrap: break-word;
}
img {
display: block;
+ width: 80%;
margin-left: auto;
margin-right: auto;
border: 1px solid;
}
+img[src="/cat.png"] {
+ width: auto;
+}
+
+video {
+ width: 100%;
+ border: 1px solid;
+}
+
/* ------- '.one' classes used by 'one-ox' org backend ------- */
.one-hl {
- font-family: 'Fira Mono', monospace;
- font-size: 80%;
- border-radius: 6px;
+ font-family: "Fira Mono", monospace;
+ font-size: 90%;
}
.one-hl-inline {
- background: #31424a;
+ background: #31424a;
padding: 0.2em 0.4em;
margin: 0;
white-space: break-spaces;
}
.one-hl-block {
- background: #161f22;
+ background: #000000;
color: #c5c5c5;
display: block;
overflow: auto;
padding: 16px;
line-height: 1.45;
+ border: 1px solid;
}
.one-blockquote {
}
.one-blockquote > p:last-child {
+ text-align: right;
margin-bottom: 0;
}
background: #31424a;
}
-/* -------- specific to the default render functions -------- */
+/* -------- render functions -------- */
.header {
color: #ffffff;
+ font-family: "Cantarell", sans-serif;
font-size: 2em;
font-weight: bold;
padding: 0 16px 0 16px;
background: #151515;
width: 100%;
height: 3.5rem;
- position: fixed;
top: 0;
left: 0;
border-bottom: 1px solid #1d272b;
}
.content {
- margin: 3.5rem auto;
- padding-top: 1.8rem;
- max-width: 740px;
+ margin: 0 auto;
+ max-width: 852px;
padding: 0 16px;
}
padding: 1.8rem 0;
}
-.title-empty {
- padding-top: 1rem;
-}
-
-/* -------- one-default-home -------- */
-
-#home {
- margin: 5rem 0 1.5rem 0;
-}
-
-/* -------- one-default-home-list-pages -------- */
-
#home-list-pages {
- margin: 5rem 0 1.5rem 0;
+ margin: 0 0 1.5rem 0;
}
#pages ul {
color: #ffffff;
}
-/* -------- one-default, one-default-with-toc, one-default-with-sidebar, one-default-doc -------- */
-
-.nav {
- border-top: 1px solid #c5c5c5;
- margin-top: 3em;
- padding: 2em 0;
+.article-header {
display: flex;
- justify-content: center;
- gap: 0.5em;
- font-weight: bold;
+ justify-content: space-between;
}
-.nav a {
- display: block;
- background: #dedede;
- border-radius: 6px;
- padding: 0.2em 0.8em;
- color: #151515;
- width: 20%;
- text-align: center;
+.article-header #category {
}
-@media (max-width:600px) {
- .nav a {
- width: auto;
- }
+.article-header #date {
}
-/* -------- one-default-with-toc, one-default-doc -------- */
-
-.toc {
- display: flex;
- justify-content: center;
- margin-bottom: 1.8rem;
- color: #d1d1d1;
-}
-
-.toc > div {
- padding: 0 1em;
+.category {
}
-.toc a {
- color: #d1d1d1;
-}
-
-.toc > div > div:first-child {
- text-decoration: underline 1px;
+#footer {
+ margin: 3rem 0;
text-align: center;
- font-size: 1.2em;
- margin-bottom: 16px;
-}
-
-/* --------- one-default-with-sidebar, one-default-doc --------- */
-
-#sidebar-header {
- color: #ffffff;
- font-size: 2em;
- font-weight: bold;
- padding: 0 16px 0 16px;
- background: #151515;
- width: 100%;
- height: 3.5rem;
- position: fixed;
- top: 0;
- left: 0;
- border-bottom: 1px solid #1d272b;
- display: flex;
- justify-content: center;
- align-items: center;
-}
-
-#sidebar-header > a {
- color: inherit;
- cursor: pointer;
- text-decoration: none;
-}
-
-#sidebar-header > a:visited {
- color: inherit;
-}
-
-#hamburger {
- cursor: pointer;
- height: 1em;
- fill: #dedede;
- display: none;
- font-weight: normal;
- margin-right: 0.3em;
-}
-
-#sidebar-content {
- margin: 3.5rem auto;
- display: flex;
- margin-left: auto;
- margin-right: auto;
- max-width: 1140px;
- width: 100%;
- padding: 1em 16px;
-}
-
-#sidebar {
- border-right: 2px solid #31424a;
- top: 4.5rem;
- position: sticky;
- padding-top: 2.2em;
- padding-bottom: 6em;
- width: 250px;
- max-height: 100vh;
- overflow-y: auto;
-}
-
-#sidebar a {
- display: block;
- color: #dedede;
-}
-
-#sidebar a:hover {
- text-decoration: none;
-}
-
-#sidebar ul {
- list-style: none;
- padding:0;
-}
-
-#sidebar li {
- padding: 0.5em 0.6em;
-}
-
-#sidebar li:hover {
- background: #31424a;
-}
-
-article {
- padding: 0 1.5em;
- max-width: 640px;
- width: 100%;
-}
-
-#sidebar-left {
- width: 0;
- height: 100%;
- position: fixed;
- z-index: 3;
- top: 0;
- left: 0;
- transition: 0.25s;
- background: #2c444f;
- overflow: hidden; /* to make the children disappear when width is 0 */
- overflow-y: auto;
-}
-
-#sidebar-left > div:first-child {
- height: 3.5rem;
- font-size: 2em;
- font-weight: bold;
- border-bottom: 1px solid #b8b8b8;
- padding-left: 16px;
- margin-bottom: 16px;
- display: flex;
- align-items: center;
+ font-size: 70%;
}
-#sidebar-left > ul {
- padding: 0 16px 0 16px;
-}
-
-#sidebar-left > ul ul {
- padding-left: 0.8em;
- margin-left: 3px;
- border-left: 1px solid #b8b8b8;
-}
-
-#sidebar-left a {
- color: #dedede;
- text-decoration: none;
-}
-
-#sidebar-left li {
- padding: 0.5em 0;
- list-style-type: none;
-}
-
-#sidebar-main {
- display: none;
- top: 0;
- right: 0;
- width: 100%;
- height: 100%;
- position: fixed;
- background: #080808;
- opacity: 0.80;
- z-index: 2;
-}
-
-@media (max-width: 840px) {
- #hamburger {
- display: block;
- }
- #sidebar {
- display: none;
- }
- #sidebar-content {
- justify-content: center;
- }
- #sidebar-header {
- justify-content: left;
- }
- article {
- padding: 0;
- }
+#footer p {
+ margin-bottom: 0;
}
/* Local Variables: */
+++ /dev/null
-* shtarkov.net
-:PROPERTIES:
-:ONE: one-default-home-list-pages
-:CUSTOM_ID: /
-:END:
-
-[[./assets/cat.png][Cat]]
-
-* List all website's pages on the home page
-:PROPERTIES:
-:ONE: one-default-home-list-pages
-:CUSTOM_ID: /blog/default-home-list-pages/
-:END:
-
-This page is rendered with the default render function
-~one-default-home-list-pages~ specified in ~ONE~ org property which lists
-below all the pages on the website. You can use it instead of
-~one-default-home~ for your home page.
-
-* The default page
-:PROPERTIES:
-:ONE: one-default
-:CUSTOM_ID: /blog/default/
-:END:
-
-This page is rendered with the default render function ~one-default~
-specified in ~ONE~ org property.
-
-** Do you want a table of content?
-
-As you can see, ~one-default~ doesn't add a table of content (TOC). If
-you want a default render function that adds the TOC to the page you can
-use the render function ~one-default-with-toc~ presented in [[#/blog/one-default-with-toc/][The default
-page with a TOC]].
-
-** Headline foo
-*** Headline bar
-
-Some content.
-
-*** Headline baz
-
-#+BEGIN_SRC bash :results verbatim
-tree
-#+END_SRC
-
-#+RESULTS:
-#+begin_example
-.
-├── assets
-│ └── one.css
-├── one.org
-└── public
- ├── blog
- │ ├── default
- │ │ └── index.html
- │ ├── default-home-list-pages
- │ │ └── index.html
- │ ├── one-default-doc
- │ │ └── index.html
- │ ├── one-default-with-sidebar
- │ │ └── index.html
- │ └── one-default-with-toc
- │ └── index.html
- ├── index.html
- └── one.css
-
-8 directories, 9 files
-#+end_example
-
-* The default page with a TOC
-:PROPERTIES:
-:ONE: one-default-with-toc
-:CUSTOM_ID: /blog/one-default-with-toc/
-:END:
-
-This page is rendered with the render function ~one-default-with-toc~
-specified in the org property ~ONE~.
-
-** Do you want a sidebar?
-
-Perhaps you want a sidebar listing all the pages on your website, as
-many modern documentation sites do. If so, you can use the default
-render function ~one-default-with-sidebar~ presented in [[#/blog/one-default-with-sidebar/][The default page
-with a sidebar]].
-
-** Headline foo
-*** Headline bar
-
-Some content.
-
-*** Headline baz
-
-#+BEGIN_SRC bash :results verbatim
-tree
-#+END_SRC
-
-#+RESULTS:
-#+begin_example
-.
-├── assets
-│ └── one.css
-├── one.org
-└── public
- ├── blog
- │ ├── default
- │ │ └── index.html
- │ ├── default-home-list-pages
- │ │ └── index.html
- │ ├── one-default-doc
- │ │ └── index.html
- │ ├── one-default-with-sidebar
- │ │ └── index.html
- │ └── one-default-with-toc
- │ └── index.html
- ├── index.html
- └── one.css
-
-8 directories, 9 files
-#+end_example
-
-* The default page with a sidebar
-:PROPERTIES:
-:ONE: one-default-with-sidebar
-:CUSTOM_ID: /blog/one-default-with-sidebar/
-:END:
-
-This page is rendered with the render function ~one-default-with-sidebar~
-specified in the org property ~ONE~.
-
-** Do you want a sidebar and a TOC?
-
-Perhaps you want a sidebar listing all the pages on your website and a
-table of content, as many modern documentation sites do. If so, you
-can use the default render function ~one-default-doc~ presented in [[#/blog/one-default-doc/][The
-default page with TOC and sidebar]].
-
-** Headline foo
-*** Headline bar
-
-Some content.
-
-*** Headline baz
-
-#+BEGIN_SRC bash :results verbatim
-tree
-#+END_SRC
-
-#+RESULTS:
-#+begin_example
-.
-├── assets
-│ └── one.css
-├── one.org
-└── public
- ├── blog
- │ ├── default
- │ │ └── index.html
- │ ├── default-home-list-pages
- │ │ └── index.html
- │ ├── one-default-doc
- │ │ └── index.html
- │ ├── one-default-with-sidebar
- │ │ └── index.html
- │ └── one-default-with-toc
- │ └── index.html
- ├── index.html
- └── one.css
-
-8 directories, 9 files
-#+end_example
-
-* The default page with TOC and sidebar
-:PROPERTIES:
-:ONE: one-default-doc
-:CUSTOM_ID: /blog/one-default-doc/
-:END:
-
-This page is rendered with the function ~one-default-doc~ specified
-in the org property ~ONE~.
-
-** Do you want to know more about one.el?
-
-Check the documentation at https://one.tonyaldon.com.
-
-** Headline foo
-*** Headline bar
-
-Some content.
-
-*** Headline baz
-
-#+BEGIN_SRC bash :results verbatim
-tree
-#+END_SRC
-
-#+RESULTS:
-#+begin_example
-.
-├── assets
-│ └── one.css
-├── one.org
-└── public
- ├── blog
- │ ├── default
- │ │ └── index.html
- │ ├── default-home-list-pages
- │ │ └── index.html
- │ ├── one-default-doc
- │ │ └── index.html
- │ ├── one-default-with-sidebar
- │ │ └── index.html
- │ └── one-default-with-toc
- │ └── index.html
- ├── index.html
- └── one.css
-
-8 directories, 9 files
-#+end_example
--- /dev/null
+;; -*- lexical-binding: t; -*-
+(org-export-define-backend 'shtarkov.net-ox
+ '((headline . one-ox-headline)
+ (section . one-ox-section)
+ (paragraph . one-ox-paragraph)
+
+ (plain-text . one-ox-plain-text)
+
+ (bold . one-ox-bold)
+ (italic . one-ox-italic)
+ (strike-through . one-ox-strike-through)
+ (underline . one-ox-underline)
+ (code . one-ox-code)
+ (verbatim . one-ox-verbatim)
+
+ (subscript . one-ox-no-subscript)
+ (superscript . one-ox-no-superscript)
+
+ (plain-list . one-ox-plain-list)
+ (item . one-ox-item)
+
+ (src-block . one-ox-src-block)
+ (example-block . one-ox-example-block)
+ (fixed-width . one-ox-fixed-width)
+ (quote-block . one-ox-quote-block)
+
+ (link . shtarkov.net-link)))
+
+(defun shtarkov.net-head (website-name)
+ `(:head
+ (:meta (@ :charset "UTF-8"))
+ (:meta (@ :name "viewport" :content "width=device-width,initial-scale=1"))
+ (:link (@ :rel "stylesheet" :type "text/css" :href "/main.css"))
+ (:title ,website-name)))
+
+(setq shtarkov.net-footer
+ '(:div/footer
+ (:p
+ "© 2024 Christian Shtarkov. Except where otherwise noted, content on this site is licensed under the "
+ (:a (@ :href "https://creativecommons.org/licenses/by/4.0/") "Creative Commons Attribution 4.0 International license")
+ ".")
+ (:p
+ "Built with " (:a (@ :href "https://one.tonyaldon.com") "one.el")
+ " (" (:a (@ :href "/source/") "source") ")")))
+
+(defun shtarkov.net-category (custom-id)
+ (cadr (string-split custom-id "/")))
+
+(defun shtarkov.net-home (page-tree pages _global)
+ (let* ((website-name (one-default-website-name pages))
+ (content (org-export-data-with-backend
+ (org-element-contents page-tree)
+ 'shtarkov.net-ox nil))
+ (pages-list (one-default-pages pages "^/[^/]+/$")))
+ (jack-html
+ "<!DOCTYPE html>"
+ `(:html
+ (@ :lang "en")
+ ,(shtarkov.net-head website-name)
+ (:body
+ (:div.header (:a (@ :href "/") ,website-name))
+ (:div.content
+ (:div/home-list-pages ,content)
+ (:div/pages (:ul ,(reverse pages-list))))
+ ,shtarkov.net-footer)))))
+
+(defun shtarkov.net-articles (page-tree pages _global)
+ (let* ((website-name (one-default-website-name pages))
+ (title (org-element-property :raw-value page-tree))
+ (content (org-export-data-with-backend
+ (org-element-contents page-tree)
+ 'shtarkov.net-ox nil))
+ (pages-list
+ (one-default-pages
+ pages
+ (format "^%s[^$]" (org-element-property :CUSTOM_ID page-tree)))))
+ (jack-html
+ "<!DOCTYPE html>"
+ `(:html
+ (@ :lang "en")
+ ,(shtarkov.net-head website-name)
+ (:body
+ (:div.header (:a (@ :href "/") ,website-name))
+ (:div.content
+ (:div.title (:h1 ,title))
+ (:div/home-list-pages ,content)
+ (:div/pages (:ul ,(reverse pages-list)))))
+ ,shtarkov.net-footer))))
+
+(defun shtarkov.net-default (page-tree pages _global)
+ (let* ((website-name (one-default-website-name pages))
+ (title (org-element-property :raw-value page-tree))
+ (content (org-export-data-with-backend
+ (org-element-contents page-tree)
+ 'shtarkov.net-ox nil)))
+ (jack-html
+ "<!DOCTYPE html>"
+ `(:html
+ (@ :lang "en")
+ ,(shtarkov.net-head website-name)
+ (:body
+ (:div.header (:a (@ :href "/") ,website-name))
+ (:div.content
+ (:div.title (:h1 ,title))
+ ,content))
+ ,shtarkov.net-footer))))
+
+(defun shtarkov.net-article (page-tree pages _global)
+ (let* ((website-name (one-default-website-name pages))
+ (title (org-element-property :raw-value page-tree))
+ (category
+ (shtarkov.net-category (org-element-property :CUSTOM_ID page-tree)))
+ (date (org-element-property :DATE page-tree))
+ (content (org-export-data-with-backend
+ (org-element-contents page-tree)
+ 'shtarkov.net-ox nil)))
+ (jack-html
+ "<!DOCTYPE html>"
+ `(:html
+ (@ :lang "en")
+ ,(shtarkov.net-head website-name)
+ (:body
+ (:div.header (:a (@ :href "/") ,website-name))
+ (:div.content
+ (:div.title (:h1 ,title))
+ (:div.article-header
+ (:a/category (@ :href ,(format "/%s/" category)) "#" ,category)
+ (:p/date ,date))
+ ,content))
+ ,shtarkov.net-footer))))
+
+(defun shtarkov.net-link (link desc info)
+ "Transcode a LINK object from Org to HTML.
+DESC is the description part of the link, or the empty string.
+INFO is a plist holding contextual information."
+ (let* ((type (org-element-property :type link))
+ (path (org-element-property :path link))
+ (raw-link (org-element-property :raw-link link))
+ (custom-type-link
+ (let ((export-func (org-link-get-parameter type :export)))
+ (and (functionp export-func)
+ (funcall export-func path desc 'shtarkov.net-ox info))))
+ (href (cond
+ ((string= type "custom-id") path)
+ ((string= type "fuzzy")
+ (let ((beg (org-element-property :begin link)))
+ (signal 'one-link-broken
+ `(,raw-link
+ "fuzzy links not supported"
+ ,(format "goto-char: %s" beg)))))
+ ((string= type "file")
+ (or
+ ;; ./assets/images/image-1.png --> /images/image-1.png
+ ;; ./public/blog/page-1.md --> /blog/page-1.md
+ (and (string-match "\\`\\./\\(assets\\|public\\)" path)
+ (replace-match "" nil nil path))
+ (let ((beg (org-element-property :begin link)))
+ (signal 'one-link-broken
+ `(,raw-link ,(format "goto-char: %s" beg))))))
+ (t raw-link))))
+ (or custom-type-link
+ (and
+ (string-match one-ox-link-image-extensions path)
+ (format "<p><img src=\"%s\" alt=\"%s\" /></p>"
+ href (or (org-string-nw-p desc) href)) )
+ (and
+ (string-match "\\.webm$" path)
+ (format "<video controls><source src=\"%s\" alt=\"%s\" type=\"video/webm\"></video>"
+ href (or (org-string-nw-p desc) href)))
+ (format "<a href=\"%s\">%s</a>"
+ href (or (org-string-nw-p desc) href)))))
--- /dev/null
+* (shtarkov)
+:properties:
+:one: shtarkov.net-home
+:custom_id: /
+:end:
+
+[[file+emacs:./assets/cat.png][Cat]]
+
+* Emacs
+:properties:
+:one: shtarkov.net-articles
+:custom_id: /emacs/
+:end:
+
+* Cat
+:properties:
+:one: shtarkov.net-default
+:custom_id: /cat/
+:end:
+
+#+begin_quote
+A good movie is precisely the one in which the camera disappears, when
+we are no longer aware of watching a movie, while an avant-garde film
+tries to turn the camera to itself and show the hidden device. A
+jocular way of understanding this would be to compare the reaction of
+a human and that of a cat when we point to an object with the finger;
+while the human looks beyond the finger trying to discover the object
+to which the finger points, the cat stares at the finger; in this
+sense, the cat is avant-garde, it is more interested in the medium
+(the finger, the camera) than the object pointed.
+
+Julio Cabrera
+#+end_quote
+
+[[file+emacs:./assets/cat/cat1.jpg]]
+[[file+emacs:./assets/cat/cat2.jpg]]
+[[file+emacs:./assets/cat/cat3.jpg]]
+[[file+emacs:./assets/cat/cat4.jpg]]
+[[file+emacs:./assets/cat/cat5.jpg]]
+
+* Transpose Regions
+:properties:
+:one: shtarkov.net-article
+:custom_id: /emacs/transpose-regions/
+:date: <2024-10-20 Sun>
+:end:
+
+Often times I find myself indecisive about how to lay out function
+arguments.
+
+#+begin_src c++
+dump(repository, file);
+#+end_src
+
+Which one should go first? It's easy enough to reorder them with
+=C-M-t (transpose-sexps)= by placing point between the two.
+
+But, until now, I never had a good solution for the function
+declaration:
+
+#+begin_src c++
+void dump(repository::Repository &repository, const std::filesystem::path &file);
+#+end_src
+
+~transpose-sexps~ does not produce the desired result:
+
+#+begin_src c++
+void dump(repository::Repository &const, repository std::filesystem::path &file);
+#+end_src
+
+A Web search reveals that the usual approaches are defining Elisp
+commands in advance, installing packages or mucking about with the
+kill ring. Same goes for Vim!
+
+Well, there's also ~transpose-regions~, unbound by default. Let's see
+how that can work:
+
+[[./assets/emacs/transpose-regions.webm][Transpose Regions Demo]]
+
+Did you see it? The trick is to save the mark 3 times — at the
+beginning and end of the first region, and at the beginning of the
+second region. Then leave point at the end of the second region and do
+~transpose-regions~, which I've bound to =C-c t=. Isearch comes in
+handy here, because it saves the mark where the search starts. I've
+modified it to [[https://endlessparentheses.com/leave-the-cursor-at-start-of-match-after-isearch.html][leave point at the start of the match]], which makes it
+even more convenient. If you find Isearch too cumbersome you can use
+something like [[https://www.emacswiki.org/emacs/iy-go-to-char.el][iy-go-to-char.el]] or [[#/emacs/do-not-zap-up-to-char][do-not-zap-up-to-char]].