WIP: more styling updates and initial support for more output formats

This commit is contained in:
a 2024-10-15 03:09:14 -05:00
parent 29bc506741
commit 19dcdc48a2
59 changed files with 1293 additions and 620 deletions

View file

@ -2,21 +2,28 @@
@import "common/colors"; @import "common/colors";
@import "common/base"; @import "common/base";
@import "common/typography"; @import "common/typography";
@import "common/content"; @import "common/elements";
@import "common/links";
@import "features/links";
@import "features/syntax-highlighting";
@import "components/table-of-contents";
// default layout // default layout
@import "layouts/_default/list"; @import "layouts/_default/list";
@import "layouts/_default/single"; @import "layouts/_default/single";
@import "features/content";
@import "layouts/_default/_markup/render-codeblock";
@import "layouts/_default/_markup/render-heading"; @import "layouts/_default/_markup/render-heading";
@import "layouts/_default/_markup/render-link"; @import "layouts/_default/_markup/render-link";
@import "layouts/partials/mf2/h-card"; @import "layouts/partials/mf2/h-card";
@import "components/admonition"; @import "components/admonition";
@import "components/quote-block"; @import "components/quote-block";
@import "components/codeblock";
@import "features/footnotes";
// wiki layout // wiki layout
@ -28,5 +35,3 @@
@import "features/autonumbering"; @import "features/autonumbering";
@import "features/search"; @import "features/search";
@import "components/table-of-contents";

View file

@ -1,139 +0,0 @@
.hugo-content {
> * {
margin-block: 1em;
&:first-child {
margin-block-start: 0;
}
&:last-child {
margin-block-end: 0;
}
}
/* text */
line-height: 2;
/* semantics */
em {font-style: italic}
strong {font-weight: bold}
/* text formatting */
--script-size: 0.65em;
sup {
position: relative;
font-size: var(--script-size);
inset-block-start: -1em;
}
sub {
position: relative;
font-size: var(--script-size);
}
@supports #{'selector\(:has(kbd))'} {
/* style individual keys only (for the innermost <kbd> element) */
kbd kbd,
kbd:not(:has(kbd)) {
font-family: monospace;
padding: 0.25em;
background: var(--ui-overlay);
color: var(--ui-overlay-text);
}
}
@supports not #{'selector\(:has(kbd))'} {
/* style the entire key sequence */
kbd {
font-family: monospace;
padding: 0.25em;
background: var(--ui-overlay);
color: var(--ui-overlay-text);
}
/* and prevent double-styling for nested keys */
kbd kbd {
background: none;
}
}
mark {
background: var(--primary-accent-transparent);
color: var(--ui-text);
--pad-x-highlight: 0.125em;
padding-inline-start: var(--pad-x-highlight);
padding-inline-end: var(--pad-x-highlight);
}
abbr[title]:after {
content: '?';
font-size: var(--script-size);
color: var(--ui-text-muted);
}
>*:not(.highlight) code {
font-family: monospace;
background: var(--ui-overlay);
color: var(--ui-overlay-text);
padding: 0.25rem;
font-size: 1rem;
white-space: pre-wrap;
}
/* lists */
ul, ol {
padding-inline-start: 0;
margin-block: 1em;
position: relative;
}
ul {list-style: disc;}
ol {list-style: decimal;}
li {margin-block-end: 1em; line-height: 2; margin-inline-start: 1em;}
dl {margin-block: 1em; line-height: 1.4;}
dt {font-weight: 700;}
dd {margin-inline-start: 1em;}
pre:not(.chroma) {
font-family: monospace;
font-size: 1rem;
line-height: 1.4;
overflow: auto;
white-space: pre-wrap;
word-break: break-word;
display: grid;
tab-size: 3;
margin-block-end: 1em;
}
/* tables */
table {text-align: center;}
thead {
font-weight: 700;
background: var(--ui-overlay);
color: var(--ui-overlay-text);
}
th, td {
border: 1px solid var(--ui-text);
padding: 0.5em;
}
/* hugo-specific citation footnote */
cite sup {
position: inherit;
font-size: inherit;
a {
padding: 0.25em;
}
&:before {
content: ' [';
}
&:after {
content: ']';
}
}
/* the actual footnotes section */
.footnotes {
hr {
display: flex;
align-items: center;
border: 0;
&:before {
content: 'Footnotes';
color: var(--ui-text);
text-transform: uppercase;
font-weight: 900;
font-size: 0.8em;
}
&:after {
content: '';
width: 100%;
margin-inline-start: 1rem;
border-block-end: 1px solid var(--ui-text-muted);
}
}
}
}

View file

@ -0,0 +1,66 @@
/* render <kbd> with slight depth */
@supports #{'selector\(:has(kbd))'} {
/* style individual keys only (for the innermost <kbd> element) */
kbd kbd,
kbd:not(:has(kbd)) {
font-family: monospace;
padding: 0.25em;
background: var(--ui-overlay);
color: var(--ui-overlay-text);
border: 1px solid var(--primary-accent-transparent);
margin-inline: 0.25em;
}
}
@supports not #{'selector\(:has(kbd))'} {
/* style the entire key sequence */
kbd {
font-family: monospace;
padding: 0.25em;
background: var(--ui-overlay);
color: var(--ui-overlay-text);
}
/* and prevent double-styling for nested keys */
kbd kbd {
background: none;
}
}
/* render <mark> as highlighting */
mark {
background: var(--primary-accent-transparent);
color: var(--ui-text);
--pad-x-highlight: 0.125em;
padding-inline-start: var(--pad-x-highlight);
padding-inline-end: var(--pad-x-highlight);
}
/* lists */
ul {list-style: disc;}
ol {list-style: decimal;}
/* tables */
table {text-align: center;}
thead {
font-weight: bold;
background: var(--ui-overlay);
color: var(--ui-overlay-text);
}
th, td {
border: 1px solid var(--ui-text);
padding: 0.5em;
}
/* figures */
figure {
margin: 0;
}
figcaption {
padding-block: 1em;
padding-inline: 1em;
}
/* extra style and flair */
code {
background: var(--ui-overlay);
color: var(--ui-overlay-text);
padding: 0.25rem;
}
/* fix pre */
pre {
margin: 0;
}

View file

@ -10,3 +10,51 @@ h1, h2, h3, h4, h5, h6 {
margin-block-end: 1rem; margin-block-end: 1rem;
font-weight: bold; font-weight: bold;
} }
body {line-height: 2;}
pre {
line-height: 1.2;
font-family: monospace;
font-size: 1rem;
overflow: auto;
white-space: pre-wrap;
word-break: break-word;
tab-size: 3;
}
ul, ol {
padding-inline-start: 0;
}
li {
margin-inline-start: 1em;
margin-block: 1em;
}
dt {
font-weight: bold;
}
dd {
margin-inline-start: 1em;
}
code {
font-family: monospace;
font-size: 1rem;
white-space: pre-wrap;
word-break: break-word;
}
a {
word-break: break-word;
}
/* semantics */
em {font-style: italic}
strong {font-weight: bold}
/* script */
html {
--script-size: 0.8em;
}
sup {
position: relative;
font-size: var(--script-size);
inset-block-start: -1em;
}
sub {
position: relative;
font-size: var(--script-size);
}

View file

@ -1,33 +1,33 @@
.admonition { .admonition {
background: var(--ui-overlay-transparent); background: var(--ui-overlay-transparent);
border-inline-start: 0.4em solid var(--ui-overlay); border-inline-start: 0.4em solid var(--ui-overlay);
border-radius: 0.5em; border-radius: 1em;
padding-inline: 1.5em; padding-block-start: 1em;
padding-block: 1em; padding-block-end: 1em;
position: relative; position: relative;
&-header { &-header {
display: contents; display: contents;
} }
&__icon { &__icon {
position: absolute; position: absolute;
inset-inline-start: -0.4em;
inset-block-start: -0em;
padding: 0.25em;
box-sizing: border-box; box-sizing: border-box;
font-size: 3em;
border-radius: 100em;
background: var(--ui-overlay); background: var(--ui-overlay);
border-radius: 100em;
/* position the icon */
font-size: 3em;
padding: 0.25em;
inset-inline-start: -0.175em;
inset-block-start: -0em;
} }
&__title { &__title {
font-size: 1em; font-size: 1em;
font-weight: bold; font-weight: bold;
padding-inline: 1em; padding-inline: 1em;
margin-block-start: -0.5em; margin-block-start: -0.5em;
margin-inline-start: 0.25em; margin-inline-start: 2em;
margin-block-end: 0; margin-block-end: 0.5em;
} }
&__text { &__text {
margin-inline-start: 0.25em;
padding-inline: 1em; padding-inline: 1em;
> *:first-child { > *:first-child {
margin-block-start: 0; margin-block-start: 0;

View file

@ -0,0 +1,12 @@
.codeblock {
&__header {
background: var(--ui-overlay);
padding-block: 0.25em;
padding-inline: 1em;
}
&__title {
margin: 0;
font-weight: bold;
font-family: monospace;
}
}

View file

@ -1,9 +1,15 @@
.toc-title { .table-of-contents {
&__title {
margin-block-start: 1em;
margin-block-end: 0.5em; margin-block-end: 0.5em;
font-weight: 900; font-weight: 900;
text-transform: uppercase; text-transform: uppercase;
font-size: 1rem;
&:before {
content: '' !important; // if autonumbering is enabled, remove it from the heading
counter-reset: h2 // and don't increment the count for the rest of the content
}
} }
#TableOfContents {
ul, ol { ul, ol {
list-style: none; list-style: none;
margin: 0; margin: 0;
@ -12,18 +18,12 @@
li { li {
margin-block: 0.5em; margin-block: 0.5em;
margin-inline: 0; margin-inline: 0;
line-height: 1.5;
} }
a { a {
display: inline; display: inline;
line-height: 1.5;
} }
li > ul, li > ol { /* indent subheadings */ li > ul, li > ol { /* indent subheadings */
margin-inline-start: 1em; margin-inline-start: 1em;
} }
} }
.toc details {
max-width: 45ch;
background: rgba(0,0,0,0.1);
padding: 1em;
border-radius: 0.5em;
}

View file

@ -1,4 +1,4 @@
body {counter-reset: h2} body {counter-reset: h2 figure table}
h2 {counter-reset: h3} h2 {counter-reset: h3}
h3 {counter-reset: h4} h3 {counter-reset: h4}
h4 {counter-reset: h5} h4 {counter-reset: h5}
@ -35,14 +35,14 @@ article[autonumbering] {
font-size: 1rem; font-size: 1rem;
} }
#TableOfContents :is(ol, ul) { .table-of-contents :is(ol, ul) {
/* /*
each list gets a new counter each list gets a new counter
*/ */
counter-reset: item; counter-reset: item;
margin-inline-start: 0; margin-inline-start: 0;
} }
#TableOfContents li:before { .table-of-contents li:before {
/* /*
the counter is added as a pseudo-element, the counter is added as a pseudo-element,
and nested counters are joined by a dot and nested counters are joined by a dot
@ -54,12 +54,17 @@ article[autonumbering] {
font-weight: bold; font-weight: bold;
margin-inline-end: 1em; margin-inline-end: 1em;
} }
#TableOfContents > ul > li { /* tables and figures */
/* caption {
top-level items wrap after the number, counter-increment: table;
in order to add some visual separation &:before {
*/ content: "Table " counter(table) ": ";
display: flex; }
flex-flow: column; }
figcaption {
counter-increment: figure;
&:before {
content: "Figure " counter(figure) ": ";
}
} }
} }

View file

@ -0,0 +1,11 @@
.hugo-content {
> * {
margin-block: 1em;
&:first-child {
margin-block-start: 0;
}
&:last-child {
margin-block-end: 0;
}
}
}

View file

@ -0,0 +1,29 @@
.footnote-ref {
font-weight: bold;
&:before {
content: " [";
}
&:after {
content: "] "
}
}
.footnotes {
hr {
display: flex;
align-items: center;
border: 0;
&:before {
content: "Footnotes";
color: var(--ui-text);
text-transform: uppercase;
font-weight: 900;
font-size: 0.8em;
}
&:after {
content: '';
width: 100%;
margin-inline-start: 1rem;
border-block-end: 1px solid var(--ui-overlay);
}
}
}

View file

@ -24,13 +24,13 @@
--error: #cc0000; --error: #cc0000;
--keyword: hsl(210, 100%, 75%); --keyword: hsl(210, 100%, 75%);
--name: green; --name: lightgreen;
--literal: hsl(210, 100%, 60%); --literal: hsl(210, 100%, 60%);
--comment: hsl(210, 50%, 45%); --comment: hsl(210, 50%, 45%);
--generic: purple; --generic: cyan;
--whitespace: black; --whitespace: black;
--highlight: hsl(210, 100%, 25%); --highlight: hsl(210, 100%, 20%);
--linenos: var(--comment); --linenos: var(--comment);
--linenos-background: hsl(210, 100%, 5%); --linenos-background: hsl(210, 100%, 5%);
@ -39,44 +39,48 @@
color: black; color: black;
} }
} }
pre.chroma { .chroma {
overflow: auto; overflow-inline: auto;
display: grid;
span { span {
-webkit-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; // fix a safari bug
font-size: 1em; font-size: 1em;
} line-height: 1.5;
.ln {
font-size: 1em;
margin-inline-end: 1em;
background: var(--linenos-background);
padding-block: 0.25em;
display: flex;
width: 2em;
}
.hl .ln {
background: var(--highlight);
} }
.line { .line {
display: grid; display: grid;
grid-template-columns: max-content 1fr; grid-template-columns: auto 1fr;
.cl {
grid-column: 2;
} }
} .ln { // line number
.cl { grid-column: 1;
padding-inline: 1em; font-size: 1em;
background: var(--linenos-background);
padding-block: 0.25em; padding-block: 0.25em;
word-break: break-all; display: flex;
justify-content: end;
margin: 0;
} }
.line:first-child { .cl { // code line
.ln, .cl { grid-column: 2;
> * {
display: inline-flex;
height: 100%;
align-items: center;
}
padding-inline: 0.5em;
}
.line:first-child .cl {
padding-block-start: 1em; padding-block-start: 1em;
} }
} .line:last-child .cl {
.line:last-child {
.ln, .cl {
padding-block-end: 1em; padding-block-end: 1em;
} }
:is(.line:first-child, .line:last-child) .ln ~ .cl {
padding-block: 0em;
}
.hl > * { // highlight
background: var(--highlight);
} }
} }
} }
@ -88,10 +92,9 @@
/* CodeLine */ .chroma .cl { color: var(--text) } /* CodeLine */ .chroma .cl { color: var(--text) }
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } /* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; } /* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
/* LineHighlight */ .chroma .hl { background-color: var(--highlight) }
/* LineNumbersTable */ .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: var(--linenos) } /* LineNumbersTable */ .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: var(--linenos) }
/* LineNumbers */ .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: var(--linenos) } /* LineNumbers */ .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: var(--linenos) }
/* Line */ .chroma .line { display: flex; white-space: pre } /* Line */ .chroma .line { white-space: pre }
/* Keyword */ .chroma .k { color: var(--keyword); font-weight: bold } /* Keyword */ .chroma .k { color: var(--keyword); font-weight: bold }
/* KeywordConstant */ .chroma .kc { color: var(--keyword); font-weight: bold } /* KeywordConstant */ .chroma .kc { color: var(--keyword); font-weight: bold }
/* KeywordDeclaration */ .chroma .kd { color: var(--keyword); font-weight: bold } /* KeywordDeclaration */ .chroma .kd { color: var(--keyword); font-weight: bold }

View file

@ -1,26 +1,43 @@
.layout-_default-list { .layout-_default-list {
.list-header { .list-header {
padding: 1em;
hr {display: none;} hr {display: none;}
.container {
max-width: 80ch;
margin-inline: auto;
}
} }
.list-title { .list-title {
margin-block: 0; margin-block-start: 0;
font-size: 2.025rem;
line-height: 1.15;
letter-spacing: -0.022rem;
grid-area: title;
} }
.list-author {} .list-author {}
.list-summary {} .list-summary {
font-style: italic;
font-size: 1.266rem;
line-height: 1.15;
letter-spacing: -0.022rem;
margin-block-start: 0.5rem;
grid-area: summary;
}
.list-content {} .list-content {}
.list-permalink { .list-permalink {
.u-url {font-family: monospace;} .u-url {font-family: monospace;}
} }
.list-pages { .list-pages {
padding-inline: 1em; max-width: 80ch;
.list-of-pages-in-section { margin-inline: auto;
.pages-list {
list-style: none; list-style: none;
padding-inline-start: 0; padding-inline-start: 0;
&__item { &__item {
max-width: 80ch; margin-inline-start: 0;
.p-name { .p-name {
font-weight: bold; font-weight: bold;
font-size: 1em;
display: inline;
} }
.p-summary { .p-summary {
font-style: italic; font-style: italic;
@ -28,4 +45,9 @@
} }
} }
} }
@media (min-width: 400px) {
.container {
padding: 0 2em;
}
}
} }

View file

@ -48,6 +48,7 @@
.page-permalink { .page-permalink {
margin-block-end: 0; margin-block-end: 0;
align-self: end; align-self: end;
.u-url {font-family: monospace;}
} }
.page-series { .page-series {
// background: var(--ui-overlay); // background: var(--ui-overlay);
@ -79,52 +80,37 @@
} }
} }
} }
.page-toc {
display: none;
}
.page-content { .page-content {
width: 100%;
max-width: 80ch; max-width: 80ch;
margin-inline: auto; margin-inline: auto;
} }
.page[has-toc] { .page[has-toc] {
padding-block-end: 4em; padding-block-end: 4em;
} }
.page > .table-of-contents {
display: none;
}
#toc-toggle { #toc-toggle {
display: none; display: none;
&:checked ~ .page-toc { &:checked ~ .table-of-contents {
display: block; display: block;
position: fixed; position: fixed;
z-index: 10; z-index: 10;
bottom: 5em; bottom: 6em;
height: 40em; height: max-content;
width: 80%; width: 80%;
max-width: 20em; max-width: 20em;
max-height: 70vh; max-height: #{min}(40em, 70vh);
overflow-y: scroll; overflow-y: scroll;
right: 1em; right: 1em;
background: var(--ui-overlay); background: var(--ui-overlay);
border-radius: 1em; border-radius: 1em;
@include shadow-low; @include shadow-low;
padding: 0; padding-inline: 1em;
> .container { > .container {
padding: 0; padding: 0;
} }
details {
display: contents;
summary {
display: none;
}
#TableOfContents {
padding: 1em;
&:before {
content: 'Contents';
font-size: 1em;
font-weight: 900;
color: var(--ui-overlay-text);
text-transform: uppercase;
}
}
}
} }
} }
label[for="toc-toggle"] { label[for="toc-toggle"] {
@ -182,7 +168,7 @@
grid-column: 2 / span 2; grid-column: 2 / span 2;
} }
} }
.page-toc { .table-of-contents {
display: block; display: block;
grid-column: 3; grid-column: 3;
grid-row: 2; grid-row: 2;
@ -202,13 +188,6 @@
&::-webkit-scrollbar-track { &::-webkit-scrollbar-track {
background-color: var(--ui-overlay) background-color: var(--ui-overlay)
} }
details {
padding: 0;
background: none;
#TableOfContents {
padding-left: 0em;
}
}
} }
.page-content { .page-content {
grid-column: 2; grid-column: 2;

View file

@ -1,15 +1,20 @@
.author-card { .author-card {
.author-card__link {
text-decoration: none;
color: inherit;
font-weight: bold;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 0.5em; gap: 0.5em;
.author-card__link {
text-decoration: none;
color: inherit;
display: contents;
&:after {
display: none; // suppress "external links" icon
}
} }
&__avatar { &__avatar {
width: 3em; width: 3em;
border-radius: 100em; border-radius: 100em;
} }
&__name {
font-weight: bold;
}
} }

View file

@ -1 +1,2 @@
@import "common"; @import "common";

View file

@ -2,7 +2,7 @@
title = "AT&T is pulling some serious doublespeak right now." title = "AT&T is pulling some serious doublespeak right now."
summary = "Is AT&T a common carrier, according to AT&T? It depends on who's asking." summary = "Is AT&T a common carrier, according to AT&T? It depends on who's asking."
date = 2015-01-10T12:00:00-06:00 date = 2015-01-10T12:00:00-06:00
tags = [] tags = ["att", "ftc", "fcc"]
categories = [] categories = []
[[params.inReplyTo]] [[params.inReplyTo]]
name = "AT&T defends unlimited data throttling, says the FTC cant stop it" name = "AT&T defends unlimited data throttling, says the FTC cant stop it"

View file

@ -12,7 +12,6 @@ url = "https://socialhub.activitypub.rocks/t/socialwebfoundation-what-do-people-
> I think that, before we had an open standard for social networking, protocol experimentation was more important. Now that we have an open standard, with a hundred implementations, tens of thousands of nodes, and tens of millions of users, I think efforts should go into encouraging the use of that standard and enhancing it through backwards-compatible extensions, rather than starting the whole process from the ground up. > I think that, before we had an open standard for social networking, protocol experimentation was more important. Now that we have an open standard, with a hundred implementations, tens of thousands of nodes, and tens of millions of users, I think efforts should go into encouraging the use of that standard and enhancing it through backwards-compatible extensions, rather than starting the whole process from the ground up.
{cite="https://socialhub.activitypub.rocks/t/socialwebfoundation-what-do-people-think/4564/36" card="socialhub.activitypub.rocks/eprodrom"} {cite="https://socialhub.activitypub.rocks/t/socialwebfoundation-what-do-people-think/4564/36" card="socialhub.activitypub.rocks/eprodrom"}
I think this is a bit too conservative of a take regarding backwards-compatibility. I really dont think were at any sort of broad alignment, even within the ActivityPub space alone. Just about the only thing we universally do is POST to inbox. That payload is AS2 Activities. Things tighten up a bit when you consider e.g. the "Mastodon Protocol" as defined by https://docs.joinmastodon.org/spec/activitypub but even then, not everyone follows this protocol, and as a protocol, its got a lot of missing bits that are very important for a "social networking protocol" to have, not to mention it doesnt closely follow the requirements laid out in AP or AS2. The main criticism that several people have raised (@hrefna @stevebate and @jenniferplusplus in particular) is that any interop we have on the fediverse today is more the result of the blood, sweat, and tears of implementers than it is the “open standard” that pretty much no one implements to the letter. The far more common outcome is to reverse-engineer Mastodon, or reverse-engineer Lemmy, with the aid of their documentation, with lots of hours spent looking at the code, and with outreach and collaboration involving other implementers. I think this is a bit too conservative of a take regarding backwards-compatibility. I really dont think were at any sort of broad alignment, even within the ActivityPub space alone. Just about the only thing we universally do is POST to inbox. That payload is AS2 Activities. Things tighten up a bit when you consider e.g. the "Mastodon Protocol" as defined by https://docs.joinmastodon.org/spec/activitypub but even then, not everyone follows this protocol, and as a protocol, its got a lot of missing bits that are very important for a "social networking protocol" to have, not to mention it doesnt closely follow the requirements laid out in AP or AS2. The main criticism that several people have raised (@hrefna @stevebate and @jenniferplusplus in particular) is that any interop we have on the fediverse today is more the result of the blood, sweat, and tears of implementers than it is the “open standard” that pretty much no one implements to the letter. The far more common outcome is to reverse-engineer Mastodon, or reverse-engineer Lemmy, with the aid of their documentation, with lots of hours spent looking at the code, and with outreach and collaboration involving other implementers.
Honestly, looking at the ActivityPub spec over and over and over and over and over (as Ive done countless times), the well-defined parts that stick out are: Honestly, looking at the ActivityPub spec over and over and over and over and over (as Ive done countless times), the well-defined parts that stick out are:

View file

@ -1,2 +1,4 @@
+++ +++
title = "~a"
summary = "i have approximate knowledge of many things. perpetual student. (nb/ace/they)"
+++ +++

View file

@ -0,0 +1,7 @@
+++
title = "Sitemap"
type = "_default"
layout = "sitemap"
_build.list="never"
slug = "sitemap"
+++

View file

@ -0,0 +1,6 @@
+++
title = "Social Network versus Social Web"
date = 2024-10-12T13:03:23-06:00
toc = true
draft = true
+++

View file

@ -2,6 +2,7 @@
title = "Worse is worse" title = "Worse is worse"
date = 2024-10-12T13:03:23-06:00 date = 2024-10-12T13:03:23-06:00
toc = true toc = true
draft = true
+++ +++
sure, "worse is better", but also, worse is worse. sure, "worse is better", but also, worse is worse.

View file

@ -1,7 +1,7 @@
+++ +++
title = "monologues" title = "monologues"
name = "~a | monologues" name = "~a | monologues"
summary = "mostly a compilation of times i replied to myself on social media or in chat. what you see here is minimally curated and lightly formatted. it might disappear at any time or be upgraded to a proper or formal writing at some point." summary = "assorted musings. mostly a compilation of times i replied to myself on social media or in chat. what you see here is minimally curated and lightly formatted. it might disappear at any time or be upgraded to a proper or formal writing at some point."
[pagination] [pagination]
pagerSize = 100 pagerSize = 100
+++ +++

View file

@ -0,0 +1,12 @@
+++
title = "worse is worse"
summary = "sure, \"worse is better\", but also, worse is worse. the main thesis is that a) we keep dooming ourselves to \"worse\" futures than what could have been, b) we ought not to \"settle\" for the \"worse\" thing, c) things can be \"worse\" but do they really have to be *that much* \"worse\"?"
date = "2024-10-12T11:56:00-06:00"
source = "https://mastodon.social/@trwnh/113295504458006091"
+++
sure, "worse is better", but also, worse is worse.
might expand on this when i have the time, but the main thesis is that a) we keep dooming ourselves to "worse" futures than what could have been, b) we ought not to "settle" for the "worse" thing, c) things can be "worse" but they really don't have to be *that much* "worse"
conclusion: it may be a pattern, but consigning yourself to it as a mindset is a form of abdication

View file

@ -0,0 +1,4 @@
+++
title = "All posts"
summary = "Follow this stream to get all of my posts."
+++

View file

@ -0,0 +1,4 @@
+++
title = "theorycrafting"
summary = "explorations and thinkpieces about potential concepts. whether they are of use to anyone else remains to be seen."
+++

View file

@ -3,6 +3,8 @@ title = "Evolving OrderedCollection to be more useful"
date = 2024-10-04T21:50:07-06:00 date = 2024-10-04T21:50:07-06:00
toc = true toc = true
autonumbering = true autonumbering = true
streams = ["all"]
tags = ["activitypub", "orderedcollection", "as2"]
[[params.inReplyTo]] [[params.inReplyTo]]
name = "Reordering user-created collections" name = "Reordering user-created collections"
url = "https://github.com/w3c/activitypub/issues/439" url = "https://github.com/w3c/activitypub/issues/439"
@ -25,8 +27,8 @@ If you define a Collection in terms of `items`, then it is pretty clearly a Set,
If you define OrderedCollection in terms of `orderedItems`, then this is simply a term which defines `as:items` with a `@container: @list`, so declaring `orderedItems` instead of `items` will use the JSON-LD `@list` container instead of using the JSON-LD `@set` container. One notable thing about the `@list` container is that it is not the same as an OrderedSet -- it can actually contain duplicates. In JSON-LD, this is clearly described as an "ordered list". For example: If you define OrderedCollection in terms of `orderedItems`, then this is simply a term which defines `as:items` with a `@container: @list`, so declaring `orderedItems` instead of `items` will use the JSON-LD `@list` container instead of using the JSON-LD `@set` container. One notable thing about the `@list` container is that it is not the same as an OrderedSet -- it can actually contain duplicates. In JSON-LD, this is clearly described as an "ordered list". For example:
```json ```json{title="list.jsonld" caption="An ordered list containing the same object twice."}
{"@list": [{"@value": 10.0}, {"@value": 10.0}]} {"@list": [{"@id": "https://url.example"}, {"@id": "https://url.example"}]}
``` ```
### The argument for OrderedSet ### The argument for OrderedSet
@ -34,11 +36,10 @@ If you define OrderedCollection in terms of `orderedItems`, then this is simply
The problem with taking OrderedCollection to be defined in terms of `orderedItems` is that OrderedCollection in AS2-Vocab is actually defined as *inheriting from* Collection. The actual definitions given are the following: The problem with taking OrderedCollection to be defined in terms of `orderedItems` is that OrderedCollection in AS2-Vocab is actually defined as *inheriting from* Collection. The actual definitions given are the following:
> A `Collection` is a subtype of `Object` that represents ordered or unordered sets of `Object or Link` instances. > A `Collection` is a subtype of `Object` that represents ordered or unordered sets of `Object or Link` instances.
{cite="https://www.w3.org/TR/activitystreams-vocabulary/#dfn-collection" name="<cite>Activity Vocabulary</cite> § 2 \"Core Types\" # Collection"} {cite="https://www.w3.org/TR/activitystreams-vocabulary/#dfn-collection" title="Activity Vocabulary" ref="§ 2 \"Core Types\" # Collection"}
> A subtype of `Collection` in which members of the logical collection are assumed to always be strictly ordered. > A subtype of `Collection` in which members of the logical collection are assumed to always be strictly ordered.
{cite="https://www.w3.org/TR/activitystreams-vocabulary/#dfn-orderedcollection" name="<cite>Activity Vocabulary</cite> § 2 \"Core Types\" # OrderedCollection"} {cite="https://www.w3.org/TR/activitystreams-vocabulary/#dfn-orderedcollection" title="Activity Vocabulary" ref="§ 2 \"Core Types\" # OrderedCollection"}
So if Collection is a set (ordered or unordered), and OrderedCollection inherits from Collection, then OrderedCollection is logically implied to also be a set (ordered). So if Collection is a set (ordered or unordered), and OrderedCollection inherits from Collection, then OrderedCollection is logically implied to also be a set (ordered).
@ -51,6 +52,7 @@ Put another way: does Adding an item into an OrderedCollection simply append at
To give a concrete example, consider how ActivityPub mandates that OrderedCollection MUST be reverse chronological. (This has been the subject of an errata intending to relax this requirement to only apply to the properties defined as OrderedCollection *within ActivityPub*, but the general problem still applies.) ActivityPub gives this clarifying note: To give a concrete example, consider how ActivityPub mandates that OrderedCollection MUST be reverse chronological. (This has been the subject of an errata intending to relax this requirement to only apply to the properties defined as OrderedCollection *within ActivityPub*, but the general problem still applies.) ActivityPub gives this clarifying note:
> What property is used to determine the reverse chronological order is intentionally left as an implementation detail. For example, many SQL-style databases use an incrementing integer as an identifier, which can be reasonably used for handling insertion order in most cases. In other databases, an insertion time timestamp may be preferred. What is used isn't important, but the ordering of elements must remain intact, with newer items first. A property which changes regularly, such a "last updated" timestamp, should not be used. > What property is used to determine the reverse chronological order is intentionally left as an implementation detail. For example, many SQL-style databases use an incrementing integer as an identifier, which can be reasonably used for handling insertion order in most cases. In other databases, an insertion time timestamp may be preferred. What is used isn't important, but the ordering of elements must remain intact, with newer items first. A property which changes regularly, such a "last updated" timestamp, should not be used.
{cite="https://www.w3.org/TR/activitypub/#collections" title="ActivityPub" ref="§ 5 \"Collections\" # Note 3"}
So this clearly points toward using something like "insertion time" or "incrementing integer", which means that an Add targeting an OrderedCollection will by default insert the new item at the end of the `@list`. Or the beginning, once it's reversed. So this clearly points toward using something like "insertion time" or "incrementing integer", which means that an Add targeting an OrderedCollection will by default insert the new item at the end of the `@list`. Or the beginning, once it's reversed.
@ -72,9 +74,9 @@ The ordering happens based on each `https://schema.org/ListItem` having a `https
It doesn't immediately make sense to put `nextItem` and `previousItem` style links directly on the object, because an object may be part of multiple collections. I guess you could maybe inject those properties when rendering or presenting an OrderedCollection, but this wouldn't really make sense either. The ordering in an OrderedCollection is already handled by JSON-LD's `@list` container, which in plain JSON would be an ordered array. (With paging, you can follow `as:first`/`as:next`* or you can follow `as:last`/`as:prev`* to traverse the entire OrderedCollection, one OrderedCollectionPage at a time.) It doesn't immediately make sense to put `nextItem` and `previousItem` style links directly on the object, because an object may be part of multiple collections. I guess you could maybe inject those properties when rendering or presenting an OrderedCollection, but this wouldn't really make sense either. The ordering in an OrderedCollection is already handled by JSON-LD's `@list` container, which in plain JSON would be an ordered array. (With paging, you can follow `as:first`/`as:next`* or you can follow `as:last`/`as:prev`* to traverse the entire OrderedCollection, one OrderedCollectionPage at a time.)
So maybe we could define some property `orderType`, which gives a hint as to how the collection is ordered? The value of this property is left kind of open-ended, but it could be handled by vocabulary terms, if you just define the `orderType` term as `@type: @vocab`. This would allow you to explicitly define classes for things like `ReverseChronological`, `ForwardChronological`, or any other well-defined specific concept: So maybe we could define some property `orderType`, which gives a hint as to how the collection is ordered? The value of this property is left kind of open-ended, but it could be handled by vocabulary terms, if you just define the `orderType` term as `@type: @vocab`. This would allow you to explicitly define classes for things like `ReverseChronological`, `ForwardChronological`, or any other well-defined specific concept.
```json ```json{title="reversechronological-orderedcollection.jsonld" caption="An OrderedCollection that has been explicitly declared to be reverse chronological, instead of assuming it to be so by default." hl_lines=["5-8","9-10",20]}
{ {
"@context": [ "@context": [
"https://www.w3.org/ns/activitystreams", "https://www.w3.org/ns/activitystreams",
@ -104,7 +106,7 @@ This at least gives us one more option to be expressive; we are no longer assumi
It may be that Adding something into the collection *doesn't* simply append it at the end, but rather causes the entire collection to be re-sorted. For example, consider a collection representing a conversation which is ordered "forward chronologically", but in constructing the collection, we missed an item somewhere in the middle. Rather than start over entirely, or just living with having the missed item be appended out-of-order at the end, we might instead define a new type of collection to handle this use case. Whereas the `Collection` represents an UnorderedSet data type, and an `OrderedCollection` represents an OrderedSet data type, we can define `SortedCollection` to represent a SortedSet. It inherits from OrderedCollection, since a SortedSet is just an OrderedSet that reorders itself upon any new insertion. We can also define a property `sortType` which is an analogue of the `orderType` we defined above. It may be that Adding something into the collection *doesn't* simply append it at the end, but rather causes the entire collection to be re-sorted. For example, consider a collection representing a conversation which is ordered "forward chronologically", but in constructing the collection, we missed an item somewhere in the middle. Rather than start over entirely, or just living with having the missed item be appended out-of-order at the end, we might instead define a new type of collection to handle this use case. Whereas the `Collection` represents an UnorderedSet data type, and an `OrderedCollection` represents an OrderedSet data type, we can define `SortedCollection` to represent a SortedSet. It inherits from OrderedCollection, since a SortedSet is just an OrderedSet that reorders itself upon any new insertion. We can also define a property `sortType` which is an analogue of the `orderType` we defined above.
```json ```json{title="sortedcollection-idea-1.jsonld" caption="A collection that is sorted \"reverse chronologically\"... but what does that mean?" hl_lines=[11,"12-15",19,25] }
{ {
"@context": [ "@context": [
"https://www.w3.org/ns/activitystreams", "https://www.w3.org/ns/activitystreams",
@ -135,9 +137,9 @@ It may be that Adding something into the collection *doesn't* simply append it a
But now we run into a caveat: it might not be sufficient to define classes/types for `sortType`, because the sorting might happen based on some property instead. So it looks like we're gonna need another mechanism... But now we run into a caveat: it might not be sufficient to define classes/types for `sortType`, because the sorting might happen based on some property instead. So it looks like we're gonna need another mechanism...
Maybe something like `sortedBy` and `sortOrder`? These would both be `@type: @vocab`. The `sortedBy` would be a functional property that indicates which property is being used for sorting, for example, we could sort by `as:published` (although perhaps we shouldn't?); the `sortOrder` would take one of two type/class/vocab values: either `Ascending` or `Descending`. So our example object and context is starting to look like this: Maybe something like `sortedBy` and `sortOrder`? These would both be `@type: @vocab`. The `sortedBy` would be a functional property that indicates which property is being used for sorting, so for example, we could sort by `as:published` (although perhaps we shouldn't?); meanwhile, the `sortOrder` would take one of two type/class/vocab values: either `Ascending` or `Descending`. So our example object and context is starting to look like this:
```json ```json{title="sortedcollection-idea-2.jsonld" caption="A collection that is specifically sorted by the value of <code>as:published</code> in ascending order, i.e. with older dates first."}
{ {
"@context": [ "@context": [
"https://www.w3.org/ns/activitystreams", "https://www.w3.org/ns/activitystreams",
@ -169,13 +171,13 @@ Maybe something like `sortedBy` and `sortOrder`? These would both be `@type: @vo
"https://domain.example/objects/1" "https://domain.example/objects/1"
], ],
"sortedBy": "published", "sortedBy": "published",
"sortOrder": "Descending" "sortOrder": "Ascending"
} }
``` ```
Now finally, we can send an Add and know for sure that it will be inserted in the right index/position of the underlying `@list` container: Now finally, we can send an Add and know for sure that it will be inserted in the right index/position of the underlying `@list` container:
```json ```json{title="add-to-sortedcollection.jsonld" caption="Adding an item to a SortedCollection whose sorting key is in the middle of existing items, causing the collection to reorder itself automatically."}
{ {
"@context": "https://www.w3.org/ns/activitystreams", "@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/some-activity", "id": "https://domain.example/some-activity",
@ -188,7 +190,7 @@ Now finally, we can send an Add and know for sure that it will be inserted in th
We now expect that the result will be `[4, 3, 2, 1]` instead of `[3,4,2,1]`. We now expect that the result will be `[4, 3, 2, 1]` instead of `[3,4,2,1]`.
We might also need a comparison function, but this is currently left unconsidered. We might also need a comparison function, but this is currently left unconsidered. Maybe we can generally assume that strings will be sorted lexically, and anything that can be coerced into a number (i.e. either it is a number in JSON-LD, or it is a `@value` whose `@type` is something like `http://www.w3.org/2001/XMLSchema#integer`) will be coerced into a number and compared by its magnitude.
## Q3: Are there other ways to approach insertion into an OrderedCollection? ## Q3: Are there other ways to approach insertion into an OrderedCollection?
@ -200,7 +202,7 @@ In the most basic case, this might be doable with a property like `insertAfter`?
Say we have `orderedItems: [4, 2, 1]`. We might formulate the following Add activity: Say we have `orderedItems: [4, 2, 1]`. We might formulate the following Add activity:
```json ```json{title="Add-with-insertAfter.jsonld" caption="An <code>Add</code> activity with an <code>insertAfter</code> property."}
{ {
"@context": [ "@context": [
"https://www.w3.org/ns/activitystreams", "https://www.w3.org/ns/activitystreams",
@ -223,7 +225,7 @@ Did you spot the flaw in this? We have a problem: the receiving server might not
This can be fixed by changing the `Add` to an `Insert`. Now, the server has to specifically understand the Insert activity and its side effects, or else the activity will not be processed. This can be fixed by changing the `Add` to an `Insert`. Now, the server has to specifically understand the Insert activity and its side effects, or else the activity will not be processed.
```json ```json{title="insert.jsonld" caption="An <code>Insert</code> activity, using the <code>insertAfter</code> property."}
{ {
"@context": [ "@context": [
"https://www.w3.org/ns/activitystreams", "https://www.w3.org/ns/activitystreams",
@ -253,13 +255,13 @@ Maybe we could do `"object": {"@list": [...]}` but this might not be friendly to
### Moving an item to a new position in the list? (WIP) ### Moving an item to a new position in the list? (WIP)
Just like we defined an Insert activity earlier, we might want to use something like a Move activity to reorder our OrderedCollection. Say we have a list in the form `orderedItems: [3, 4, 2, 1]`, perhaps by some permutation of aware or unaware servers leading to the error case from before. Well, how do we fix this? Just like we defined an `Insert` activity earlier, we might want to use something like a `Move` activity to reorder our OrderedCollection. Say we have a list in the form `orderedItems: [3, 4, 2, 1]`, perhaps by some permutation of aware or unaware servers leading to the error case from before. Well, how do we fix this?
What we want in this case is to move `3` to be after `4`. (Alternatively, we can achieve the same result by moving `4` to come before `3`, but we already established that "insert before" is harder than "insert after". If we need to reconsider this, then it can be reconsidered.) What we want in this case is to move `3` to be after `4`. (Alternatively, we can achieve the same result by moving `4` to come before `3`, but we already established that "insert before" is harder than "insert after". If we need to reconsider this, then it can be reconsidered.)
We might formulate "move `3` to be after `4`" with the following Move activity: We might formulate "move `3` to be after `4`" with the following `Move` activity:
```json ```json{title="Move-within-collection.jsonld" caption="A <code>Move</code> activity which attempts to change an item's position within that collection."}
{ {
"@context": [ "@context": [
"https://www.w3.org/ns/activitystreams", "https://www.w3.org/ns/activitystreams",
@ -289,11 +291,11 @@ For now, the safest thing is probably to fall back to doing this in 2 activities
### Completely reordering the items ### Completely reordering the items
Given a complex enough OrderedCollection, you might find it easier to just create a new collection and Add or Insert items as appropriate... but this is not an option if you want to preserve the collection's id. (Well, it could be an option, but you'd have to define a mechanism to reassign identifiers first. Perhaps a `Migrate` activity could be defined in some other FEP? Alternatively, `Move` might work, but I'd be wary of overloading the semantics of "moving" an object. Of course, Mastodon's "account migration" feature already uses `Move` in exactly this overloading way, so...) Given a complex enough `OrderedCollection`, you might find it easier to just create a new collection and `Add` or `Insert` items as appropriate... but this is not an option if you want to preserve the collection's id. (Well, it could be an option, but you'd have to define a mechanism to reassign identifiers first. Perhaps a `Migrate` activity could be defined in some other FEP? Alternatively, `Move` might work, but I'd be wary of overloading the semantics of "moving" an object. Of course, Mastodon's "account migration" feature already uses `Move` in exactly this overloading way, so...)
So given the same OrderedCollection, we might consider an `Update` that changes the `orderedItems` around: So given the same `OrderedCollection`, we might consider an `Update` that changes the `orderedItems` around:
```json ```json{title="Update-orderedItems.jsonld" caption="An <code>Update</code> activity that updates the <code>orderedItems</code> to be in the correct order."}
{ {
"@context": "https://www.w3.org/ns/activitystreams", "@context": "https://www.w3.org/ns/activitystreams",
"id": "https://domain.example/some-activity", "id": "https://domain.example/some-activity",
@ -311,15 +313,15 @@ So given the same OrderedCollection, we might consider an `Update` that changes
} }
``` ```
In the case where an OrderedCollection is *unpaged* but just very very large, then the answer is to simply construct a similarly large Update activity. But the larger the collection grows, the more likely it is to be paged. So if the OrderedCollection is paged and the pages are reified, you might only be able to Update one page at a time. You might even find yourself needing to Move items between one reified page and another! So maybe you can construct a new OrderedCollectionPage and carefully update the `next` and `prev` links of what is effectively a linked list. The exact algorithm for doing this is left as an exercise for the reader, because this is already getting to be a very long post, and adding more examples for this increasingly niche and situation-dependent use case is going to make it even longer... eh, perhaps in a follow-up. In the case where an `OrderedCollection` is *unpaged* but just very very large, then the answer is to simply construct a similarly large `Update` activity. But the larger the collection grows, the more likely it is to be paged. So if the `OrderedCollection` is paged and the pages are reified, you might only be able to `Update` one page at a time. You might even find yourself needing to `Move` items between one reified page and another! So maybe you can construct a new `OrderedCollectionPage` and carefully update the `next` and `prev` links of what is effectively a linked list. The exact algorithm for doing this is left as an exercise for the reader, because this is already getting to be a very long post, and adding more examples for this increasingly niche and situation-dependent use case is going to make it even longer... eh, perhaps in a follow-up.
## Current summary and conclusions ## Current summary and conclusions
- An OrderedCollection's items are expressed in JSON-LD as a `@list` but the data type is more correctly an ordered set, not an ordered list. This is because of the AS2-Vocab definitions. This means that items can only be included in a Collection at most once. - An `OrderedCollection`'s items are expressed in JSON-LD as a `@list` but the data type is more correctly an ordered set, not an ordered list. This is because of the AS2-Vocab definitions. This means that items can only be included in a `Collection` at most once.
- Ordering is determined by time-of-insertion and can be signalled by something like a property `orderType` and some classes like `ForwardChronological` and `ReverseChronological` to indicate if the latest appended item will be the first item (LIFO/FILO) or if the latest appended item will be the last item (LILO/FIFO). - Ordering is determined by time-of-insertion and can be signalled by something like a property `orderType` and some classes like `ForwardChronological` and `ReverseChronological` to indicate if the latest appended item will be the first item (LIFO/FILO) or if the latest appended item will be the last item (LILO/FIFO).
- We could define a new type `SortedCollection` specifically for collections that are not only ordered, but also sorted according to some criteria. Time-of-insertion doesn't matter here, as any newly inserted item will take its place within the sorting. We might define the `sortType` somehow using vocab terms again, but it is more useful to use `sortedBy` pointing to a property name, and `sortOrder` can be one of two classes, either `Ascending` (smallest first) or `Descending` (largest first). We might also need a comparison function, but this is currently left unconsidered. - We could define a new type `SortedCollection` specifically for collections that are not only ordered, but also sorted according to some criteria. Time-of-insertion doesn't matter here, as any newly inserted item will take its place within the sorting. We might define the `sortType` somehow using vocab terms again, but it is more useful to use `sortedBy` pointing to a property name, and `sortOrder` can be one of two classes, either `Ascending` (smallest first) or `Descending` (largest first). We might also need a comparison function, but this is currently left unconsidered -- it can be generally expected that strings can be sorted lexigraphically and numbers can be sorted by magnitude (with some consideration toward coercing any string literal `@value` if its `@type` is something like `http://www.w3.org/2001/XMLSchema#integer`).
- For cases where the collection is ordered but not sorted, we could define an `Insert` activity and an `insertAfter` property to point to the object after which the Insert's object will be inserted. Currently left unconsidered is the possibility of inserting at a specific index numerically, as well as the ability to `insertBefore`. - For cases where the collection is ordered but not sorted, we could define an `Insert` activity and an `insertAfter` property to point to the object after which the `Insert.object` will be inserted. Currently left unconsidered is the possibility of inserting at a specific index numerically (which would likely cause problems if processed out-of-order), as well as the ability to `insertBefore`.
- Inserting multiple objects into an OrderedCollection also needs to be thought about. - Inserting multiple objects into an `OrderedCollection` also needs to be thought about.
- Moving items typically is equivalent to Remove then Add. We might need an activity that is equivalent to Remove then Insert, but I have no idea what to call it. For now, the safest thing is probably to fall back to doing this in 2 activities: first, send a `Remove`, then send an `Insert`. - `Move`ing items typically is equivalent to `Remove` then `Add`. We might need an activity that is equivalent to `Remove` then `Insert`, but I have no idea what to call it. For now, the safest thing is probably to fall back to doing this in 2 activities: first, send a `Remove`, then send an `Insert`.
- Items might be completely reordered with an Update targeting the orderedItems property but this can be kinda complicated when dealing with paged collections, especially if pages are reified. You will possibly have to construct new pages and manipulate the `next`/`prev` links between them. - Items might be completely reordered with an `Update` targeting the `orderedItems` property but this can be kinda complicated when dealing with paged collections, especially if pages are reified. You will possibly have to construct new pages and manipulate the `next`/`prev` links between them.
- For now, we neglect to consider or define non-set types, such as List which is explicitly allowed to contain the same item multiple times. - For now, we neglect to consider or define non-set types, such as a potential `List` which is explicitly allowed to contain the same item multiple times. For this, use the JSON-LD `@list` or the `rdf:List` class with `rdf:first`, `rdf:rest`, and `rdf:nil`.

View file

@ -0,0 +1,163 @@
+++
title = "Marking up quotes"
summary = "In which I explore the semantics and properties of a quote, and how it differs from a citation or reference"
date = "2024-10-12T18:07:26-06:00"
streams = ["all"]
toc = true
autonumbering = false
+++
## preamble
i was looking at Hugo's recent-ish feature for render hooks, specifically new support for render-blockquote.html which allows doing some cool things with markdown blockquotes, like rendering them as alerts or admonitions:
```md{title="alert.md"}
> [!tip]
> lorem ipsum
```
you can also set `markup.goldmark.parser.attribute.block = true` in your hugo.toml config file, which enables support for the "block attributes" syntax:
```md{title="block-attributes.md"}
> lorem ipsum
{cite="http://example.com/something"}
```
so i implemented support for these new features using the following logic:
- if it has an `{{.AlertType}}` then it's not a quote, it's an alert/admonition/tip/note/etc. i choose to render these as `<aside>` with the alert type and an svg icon inside a child `<header>`.
- otherwise, it's a regular blockquote. (how to render it will come later.)
but that got me thinking: what exactly do i want to do semantically with blockquotes? well:
- a blockquote is a quote block
- a quote is... what is a quote?
## semantic building blocks of a "quote"
- content. without this, there would be no quote.
- author/attribution. attribution of a quote to its author provides more context on who said the quote.
- cite/citation. if the quote came from somewhere, then it's best to cite that source.
- caption. in the case that there is no citation, or in the case that there is more context to the quote than the work being cited, a caption can help contextualize the quote.
## how to markup a quote
in html, the most basic way is to just use a `<blockquote>`:
```html
<blockquote>
<p>Some quoted text.<p>
</blockquote>
```
if you want to cite the work, then you can do two things:
- add the `cite=` attribute to the `<blockquote>` if the work being cited has a URL
- use the `<cite>` element outside the `<blockquote>` to wrap the title of the work being cited
> [!notice] `<cite>` is for works cited, not people attributed
> Per <a class="u-cite-of" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite">MDN</a>, WHATWG, and W3C specifications and guidance, using the `<cite>` element or the `cite=` attribute on something that isn't the work being cited is incorrect usage of "citation".
> [!info] Criticism regarding the above point
> > There were too many debates about this like 13-15 years ago about "proper" use of the `<cite>` tag in HTML also
> {cite="https://chat.indieweb.org/dev/2024-10-12#t1728765670088500" card="chat.indieweb.org/[tantek]"}
```html{caption="Figure 1: A blockquote followed by a citation.", title="blockquote-and-cite.html"}
<blockquote cite="http://pub.example/the-work">
<p>Some quoted text.<p>
</blockquote>
<p><cite>Title of the Work</cite></p>
```
TODO: talk about associating the cited title with the blockquote. talk about figures and captions.
## prior art: h-cite
in looking at prior art i found the indieweb/microformats2 "h-cite" markup, which at first seemed like a good enough fit as any. but once you start to think about it more deeply, you start to find incongruities.
an h-cite is described as only having 8 elements. from http://microformats.org/wiki/h-cite we get the following list:
> - <b>p-name</b> - name of the work
> - <b>dt-published</b> - date (and optionally time) of publication
> - <b>p-author</b> - author of publication, with optional nested h-card
> - <b>u-url</b> - a URL to access the cited work
> - <b>u-uid</b> - a URL/URI that uniquely/canonically identifies the cited work, canonical permalink.
> - <b>p-publication</b> - for citing articles in publications with more than one author, or perhaps when the author has a specific publication vehicle for the cited work. Also works when the publication is known, but the authorship information is either unknown, ambiguous, unclear, or collaboratively complex enough to be unable to list explicit author(s), e.g. like with many wiki pages.
> - <b>dt-accessed</b> - date the cited work was accessed for whatever reason it is being cited. Useful in case online work changes and it's possible to access the dt-accessed datetimestamped version in particular, e.g. via the Internet Archive.
> - <b>p-content</b> for when the citation includes the content itself, like when citing short text notes (e.g. tweets).
{cite="http://microformats.org/wiki/h-cite" name="h-cite"}
these properties certainly make sense for citations like you'd find in the references section of a wikipedia article or something. but it doesn't really feel like it fits a "quote" very well.
a quotation differs from a citation in that it generally doesn't have a date of publication, nor does publication really make sense for a quote. you can access a citation but generally you don't access quotations. the content of a citation generally is unnecessary and only useful when the work itself is very short; by contrast, a quotation's content is the main thing about it.
## points of contention
### Citation vs Attribution
> "attribution but not citation" doesn't make sense, the attribution (in any form) is presumably a source of some kind, even if it is only the name of the person who said it
{cite="https://chat.indieweb.org/dev/2024-10-12#t1728772529358600" card="chat.indieweb.org/[tantek]"}
### Entities vs Relations (or Properties)
one common way to model the world is to think of everything in terms of entities. [entities have properties (in the form of attribute-values)](https://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model#History), and they also [have relations (to other entities)](https://en.wikipedia.org/wiki/Entityrelationship_model#Components).
in this case, looking at quotes and quotations vs cites and citations, there is one (possibly two) entity/ies representing the thing being quoted/cited. there is also a relation between the current entity (the h-entry) and the object of that link relation. the question is whether the "quote entity" and the "citation entity" are the same thing. and i don't think that they are. the former is a contentful thing and the latter is a referent thing.
it almost makes more sense to, instead of using h-cite and u-quotation-of, if we had h-quote and u-citation-of -- i mean, the "quote" is the main "entity" here, and it's possible to use a quote as a "citation". in general, i see h-* as representing entities, and u-* as representing link relations between those entities. so i still think h-cite deserves to exist separately to mark up citations when they are entities.
### what's the point? aren't you overthinking this?
```
<article class="quote-block{{if $cite}} h-cite{{end}}{{ if $author}} h-x-quote{{end}}">
{{ with $author }}
<header style="display: contents">
<p style="display: contents">
<span class="p-author">
{{ partial "mf2/h-card.html" . }}
</span>
<span style="display: none"> wrote:</span>
</p>
</header>
{{ else }}
<header style="display: contents">
<svg class="quote-block__icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 290 290" width="1em" height="1em" fill="currentColor"><title>quote</title><path d="M22.12 145v97.65h97.65V145H70.95c0-26.92 21.9-48.82 48.82-48.82V47.35c-53.93 0-97.65 43.72-97.65 97.65zm245.76-48.82V47.35c-53.93 0-97.65 43.72-97.65 97.65v97.65h97.65V145h-48.82c-.01-26.92 21.89-48.82 48.82-48.82z"></path></svg>
</header>
{{ end }}
{{ with $content }}
<section style="display: contents">
<blockquote class="quote-block__content e-content"{{ with $cite }} cite="{{.}}"{{end}}>
{{.}}
</blockquote>
</section>
{{ end }}
{{ if (or $cite $caption) }}
<footer style="display: contents">
<p style="display: contents">
{{ with $cite }}
<a href="{{.}}" class="u-url">
{{ with $name }}
<span>{{ . | markdownify }}</span>
{{ else }}
<span>(View quote in its original context)</span>
{{ end }}
</a>
{{ else }}
{{ with $caption }}
<span>{{ . | markdownify }}</span>
{{ end }}
{{ end }}
</p>
</footer>
{{ end }}
</article>
```

View file

@ -0,0 +1,5 @@
{
"@context": "https://www.w3.org/ns/activitystreams",
"name": "[tantek]",
"icon": "http://tantek.com/logo.jpg"
}

View file

@ -0,0 +1,6 @@
{
"@context": "https://www.w3.org/ns/activitystreams",
"name": "tantek",
"url": "http://tantek.com",
"icon": "http://tantek.com/photo.jpg"
}

View file

@ -1,4 +1,8 @@
#!/bin/bash #!/bin/bash
rm -r public/ rm -r public/
hugo hugo
#shopt -s globstar
#tidy -mq public/**/*.html
#npx prettier@3.3.3 "public/**/*.html" --parser html --write
#npx prettier@3.3.3 "public/**/*.json" --parser json --write
rsync -avz --delete public/ a@trwnh.com:/srv/http/trwnh.com rsync -avz --delete public/ a@trwnh.com:/srv/http/trwnh.com

View file

@ -1,8 +1,10 @@
baseURL = "https://trwnh.com/" baseURL = "https://trwnh.com/"
languageCode = "en-us" languageCode = "en-us"
defaultContentLanguage = "en"
title = "~a" title = "~a"
capitalizeListTitles = false capitalizeListTitles = false
summaryLength = 200
markup.goldmark.renderer.unsafe = true markup.goldmark.renderer.unsafe = true
@ -14,24 +16,69 @@ attribute.title = true
[markup.highlight] [markup.highlight]
codeFences = true codeFences = true
noClasses = false noClasses = false
lineNos = true
lineNumbersInTable = false lineNumbersInTable = false
tabWidth = 3 tabWidth = 3
guessSyntax = true
[markup.tableOfContents] [markup.tableOfContents]
startLevel = 2 startLevel = 2
endLevel = 6 endLevel = 6
ordered = true ordered = true
[mediaTypes."application/atom+xml"]
suffixes = ["atom"]
[mediaTypes."application/feed+json"]
suffixes = ["json"]
[mediaTypes."application/jf2feed+json"]
suffixes = ["json"]
[mediaTypes."application/ld+json"]
suffixes = ["jsonld"]
[outputs] [outputs]
home = ["html", "lunr"] home = ["html", "lunr", "as2"]
section = ["html", "lunr"] section = ["html", "lunr", "rss", "atom", "jsonfeed", "as2"]
taxonomy = ["html", "rss", "atom", "jsonfeed", "as2"]
term = ["html", "lunr", "rss", "atom", "jsonfeed", "as2"]
page = ["html", "as2"]
rss = ["rss"]
[outputFormats.lunr] [outputFormats.lunr]
mediaType = "application/json"
baseName = "index.lunr" baseName = "index.lunr"
isPlainText = true isPlainText = true
mediaType = "application/json"
[outputFormats.rss]
baseName = "index.rss"
[outputFormats.atom]
mediaType = "application/atom+xml"
baseName = "index"
isPlainText = true
[outputFormats.jsonfeed]
mediaType = "application/feed+json"
baseName = "index.jsonfeed"
isPlainText = true
[outputFormats.jf2]
mediaType = "application/jf2feed+json"
baseName = "index.jf2"
isPlainText = true
[outputFormats.as2]
mediaType = "application/ld+json"
baseName = "index.as2"
isPlainText = true
[taxonomies] [taxonomies]
category = "categories"
tag = "tags" tag = "tags"
series = "series" series = "series"
stream = "streams"
[params.author]
name = "a"
# url = ""

View file

@ -1,35 +1,31 @@
{{ if .AlertType }} {{- if .AlertType }}
{{/* this is an alert/admonition, not a quote */}} {{- /* this is an alert/admonition, not a quote */}}
<aside class="admonition{{ with .AlertType }} {{.}}{{end}}"> {{- $type := .AlertType }}
<header class="admonition-header"> {{- $title := or .AlertTitle (strings.FirstUpper .AlertType) }}
{{ if in (slice "info" "note" "tip" "hint") .AlertType }} {{- $text := .Text }}
<svg class="admonition__icon info-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><title>info</title><path fill="currentColor" d="M11 17h2v-6h-2zm1-8q.425 0 .713-.288T13 8t-.288-.712T12 7t-.712.288T11 8t.288.713T12 9m0 13q-2.075 0-3.9-.788t-3.175-2.137T2.788 15.9T2 12t.788-3.9t2.137-3.175T8.1 2.788T12 2t3.9.788t3.175 2.137T21.213 8.1T22 12t-.788 3.9t-2.137 3.175t-3.175 2.138T12 22"/></svg> {{- $opts := dict "ctx" . "type" $type "title" $title "text" $text }}
{{ end -}} {{- partial "components/admonition.html" $opts }}
{{- else }}
{{- if in (slice "warning" "caution" "alert" "notice" "warn") .AlertType }} {{- /* this is a quote */}}
<svg class="admonition__icon warning-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><title>warning</title><path fill="currentColor" d="M1 21L12 2l11 19zm11-3q.425 0 .713-.288T13 17t-.288-.712T12 16t-.712.288T11 17t.288.713T12 18m-1-3h2v-5h-2z"/></svg> {{- $datapath := (split (.Attributes.card) "/") }}
{{ end }} {{- $author := .Page.Site.Data.people }}
{{- range $datapath }}
{{ if in (slice "danger" "important") .AlertType}} {{- $author = index $author . }}
<svg class="admonition__icon danger-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 12 12"><title>danger</title><path fill="currentColor" d="M4.283 2.98a1.735 1.735 0 1 1 3.434 0l-.576 4.03a1.153 1.153 0 0 1-2.282 0zM7 10a1 1 0 1 1-2 0a1 1 0 0 1 2 0"/></svg> {{- end }}
{{ end -}} {{- $content := .Text }}
<p class="admonition__title" aria-hidden="true">{{or .AlertTitle (strings.FirstUpper .AlertType) }}</p> {{- $cite := .Attributes.cite }}
</header> {{- $title := .Attributes.title }}
<div class="admonition__text"> {{- $ref := .Attributes.ref }}
{{ .Text }} {{- $caption := .Attributes.caption }}
</div> {{- $opts := dict
</aside> "ctx" .
{{ else }} "author" $author
{{/* this is a quote, which we can render with an h-cite partial */}} "content" $content
{{ $datapath := (split (.Attributes.card) "/") }} "cite" $cite
{{ $author := .Page.Site.Data.people }} "title" $title
{{ range $datapath }} "ref" $ref
{{ $author = index $author . }} "caption" $caption }}
{{ end }} {{- printf "<!-- begin quote %s -->" (string (add .Ordinal 1)) | safeHTML }}
{{ $content := .Text }} {{- partial "components/quote-block.html" $opts }}
{{ $cite := .Attributes.cite }} {{ printf "<!-- end quote %s -->" (string (add .Ordinal 1)) | safeHTML }}
{{ $name := .Attributes.name }}
{{ $caption := .Attributes.caption }}
{{ $opts := dict "ctx" . "author" $author "content" $content "cite" $cite "name" $name "caption" $caption }}
{{ partial "mf2/h-cite.html" $opts }}
{{ end }} {{ end }}

View file

@ -1,5 +1,29 @@
{{ $opts := merge .Options (dict "hl_inline" true) }} {{- $result := transform.HighlightCodeBlock . }}
{{ $result := transform.HighlightCodeBlock . }} {{- $caption := .Attributes.caption }}
{{- $title := .Attributes.title }}
{{- if $caption }}
{{- printf "<!-- begin figure with codeblock %s -->" (string (add .Ordinal 1)) | safeHTML }}
<figure class="figure-with-codeblock"{{if $title}} title="{{$title}}"{{end}}>
{{ with $title }}<header class="codeblock__header"><p class="codeblock__title">{{.}}</p></header>{{ end }}
<div class="codeblock highlight"> <div class="codeblock highlight">
<pre class="chroma">{{$result.Inner}}</pre> <pre class="chroma">
{{$result.Inner}}
</pre>
</div> </div>
<figcaption>{{$caption | markdownify}}</figcaption>
</figure>
{{ printf "<!-- end figure with codeblock %s -->" (string (add .Ordinal 1)) | safeHTML }}
{{ else }}
{{- printf "<!-- begin codeblock %s -->" (string (add .Ordinal 1)) | safeHTML }}
<div class="codeblock highlight">
{{- with $title }}
<header class="codeblock__header">
<p class="codeblock__title">{{.}}</p>
</header>
{{- end }}
<pre class="chroma">
{{$result.Inner}}
</pre>
</div>
{{- printf "<!-- end codeblock %s -->" (string (add .Ordinal 1)) | safeHTML }}
{{ end }}

View file

@ -1,6 +1,6 @@
<h{{.Level}} class="rendered-heading" id="{{.Anchor | safeURL}}"> <h{{.Level}} class="rendered-heading" id="{{.Anchor | safeURL}}">{{- /* chomp whitespace */ -}}
<span class="rendered-heading__text">{{.Text | safeHTML}}</span> <span class="rendered-heading__text">{{.Text | safeHTML}}</span>{{- /* chomp whitespace */ -}}
<a class="rendered-heading__anchor-link" href="#{{.Anchor | safeURL}}"> <a class="rendered-heading__anchor-link" href="#{{.Anchor | safeURL}}" aria-hidden="true">{{- /* chomp whitespace */ -}}
<span class="rendered-heading__anchor-link__text">[link]</span> <span class="rendered-heading__anchor-link__text">[link]</span>{{- /* chomp whitespace */ -}}
</a> </a>{{- /* chomp whitespace */ -}}
</h{{.Level}}> </h{{.Level}}>

View file

@ -1,20 +0,0 @@
{{ $link := .Destination }}
{{ $isRemote := strings.HasPrefix $link "http" }}
{{- if not $isRemote -}}
{{ $url := urls.Parse .Destination }}
{{- if $url.Path -}}
{{ $fragment := "" }}
{{- with $url.Fragment }}{{ $fragment = printf "#%s" . }}{{ end -}}
{{- with .Page.GetPage $url.Path }}{{ $link = printf "%s%s" .RelPermalink $fragment }}{{ end }}{{ end -}}
{{- end -}}
<a class="rendered-link" href="{{ $link | safeURL }}"
{{- with .Title}} title="{{ . }}"
{{- end -}}
{{ if $isRemote }} target="_blank"{{ end -}}
>
<span class="rendered-link__text">{{- .Text | safeHTML -}}</span>
{{ if $isRemote -}}
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 1000 1000" width="1em" aria-hidden="true" class="rendered-link__external-link-icon"><title>external link</title><path fill="currentColor" d="M609.34 13.31c-23.11 8.5-28.16 20.46-28.42 65.35 0 29.49.8 34.8 6.11 45.16 10.89 21.52 13.81 22.31 96.96 23.91l72.25 1.33L568.7 336.32C465.63 439.39 379.57 526.79 377.44 530.77c-4.78 9.3-4.78 24.17 0 33.47 2.13 3.98 15.67 18.86 30.28 33.47 24.17 23.91 27.36 26.3 38.52 27.36 6.64.8 15.41.53 19.66-.26 5.05-1.33 65.88-60.3 196.04-190.46C765.8 330.74 851.6 246.01 852.66 246.01c1.06 0 1.86 32.67 1.86 72.78 0 70.93.26 72.78 6.11 81.29 11.16 16.47 19.39 18.86 63.49 18.06 42.5-.8 46.49-2.12 58.97-19.13 5.58-7.17 5.58-10.36 6.37-180.37l.53-173.2-5.84-11.95c-3.72-6.91-9.83-14.08-15.41-17.27-9.03-5.05-14.08-5.31-179.31-5.84-137.59-.53-171.85.01-180.09 2.93z"/><path fill="currentColor" d="M100.12 121.42c-29.75 5.05-57.91 24.44-73.58 50.47-17.8 29.49-17 10.63-16.2 390.49l.8 343.73 8.5 17.8c9.56 20.19 27.89 41.44 44.36 51 26.83 15.67 8.5 14.87 381.46 14.87 306.28 0 343.2-.53 356.22-4.25 38.25-11.42 68.01-43.57 76.51-82.88 2.13-9.83 2.92-81.82 2.92-235.62v-221.8H745.63v409.08H145.27V256.63h409.08V118.5l-219.68.27c-121.13.26-226.59 1.32-234.55 2.65z"/></svg>
{{- end -}}
</a>

View file

@ -1,33 +1,41 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{.Site.Language.Lang }}" xml:lang="{{.Site.Language.Lang }}"> <html lang="{{.Site.Language.Lang }}" xml:lang="{{.Site.Language.Lang }}">
<head> {{- /* TODO: more precise type detection */}}
<meta charset="UTF-8"> {{- $itemtype := "https://schema.org/WebPage"}}
<meta name="viewport" content="width=device-width, initial-scale=1.0"> {{- if isset .Params "date" }}
{{- $itemtype = "https://schema.org/Article"}}
{{- end }}
<head itemscope itemtype="{{$itemtype}}">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
{{ $screen := resources.Get "styles/screen.scss" | toCSS | minify | fingerprint }} <meta name="color-scheme" content="dark light" />
{{ "<!-- styles and scripts -->" | safeHTML }}
{{- $screen := resources.Get "styles/screen.scss" | toCSS | minify | fingerprint }}
<link rel="stylesheet" <link rel="stylesheet"
href="{{ $screen.Permalink }}" href="{{ $screen.Permalink }}"
integrity="{{ $screen.Data.Integrity }}" integrity="{{ $screen.Data.Integrity }}"
media="screen" /> media="screen" />
{{ $print := resources.Get "styles/print.scss" | toCSS | minify | fingerprint }} {{- $print := resources.Get "styles/print.scss" | toCSS | minify | fingerprint }}
<link rel="stylesheet" <link rel="stylesheet"
href="{{ $print.Permalink }}" href="{{ $print.Permalink }}"
integrity="{{ $print.Data.Integrity }}" integrity="{{ $print.Data.Integrity }}"
media="print" /> media="print" />
{{ $script := resources.Get "scripts/main.js" | js.Build "script.js" | minify | fingerprint }} {{- $script := resources.Get "scripts/main.js" | js.Build "script.js" | minify | fingerprint }}
<script type="text/javascript" <script type="text/javascript"
src="{{ $script.Permalink }}" src="{{ $script.Permalink }}"
integrity="{{ $script.Data.Integrity }}"> integrity="{{ $script.Data.Integrity }}">
</script> </script>
{{ partial "seo.html" . }} {{- partial "seo.html" . }}
{{- "<!-- add external icon to external links -->" | safeHTML }}
{{ partial "styles/external-links.html" . }} {{ partial "styles/external-links.html" . }}
{{ block "head" . }} {{- block "head" . }}
{{ end }} {{- end }}
</head> </head>
{{ block "body" . }} {{- block "body" . }}
<body> <body>
</body> </body>
{{ end }} {{- end -}}
</html> </html>

View file

@ -0,0 +1,58 @@
{{- $as2 := .OutputFormats.Get "as2" -}}
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "{{$as2.Permalink}}",
"type": "Person",{{/* TODO: make this variable? */}}
{{- with (or .Title .Site.Params.author.name) }}
"name": {{. | jsonify}},
{{- end }}
{{- with (or .Summary .Site.Params.author.summary) }}
"summary": {{. | jsonify}},
{{- end }}
"url": {
"href": "{{ .Permalink }}"
{{- with .Language }},
"hreflang": "{{.}}"
{{- end }},
"rel": ["canonical", "alternate"],
"mediaType": "text/html"
},
"icon": { {{/* TODO: make this variable */}}
"type": "Image",
"name": "a's icon",
"summary": "is this going to work?",
"url": {
"href": "https://trwnh.com/.assets/icon.png",
"name": "icon.png",
"width": 400,
"height": 400,
"mediaType": "image/png"
}
},
{{- with .Site.GetPage "/streams" }}
{{- $streams_as2 := .OutputFormats.Get "as2"}}
"streams": [
{{- range .Pages }}
{{- $stream_as2 := .OutputFormats.Get "as2"}}
{
"id": "{{ $stream_as2.Permalink }}",
"type": "OrderedCollection", {{/* TODO: expose orderedItems? */}}
{{- with .Title }}
"name": {{. | jsonify}},
{{- end }}
{{- with .Summary }}
"summary": {{. | jsonify}},
{{- end }}
"url": {
"href": "{{.Permalink}}"
{{- with .Language }},
"hreflang": "{{.}}"
{{- end }},
"rel": ["canonical", "alternate"],
"mediaType": "text/html"
}
}
{{- end }}
]
{{- end }}
}

View file

@ -0,0 +1,79 @@
{{ $id := "" }}
{{- with .OutputFormats.Get "as2" -}}
{{- $id = .Permalink }}
{{- end }}
{{- $type := slice "OrderedCollection" -}}
{
"@context": "https://www.w3.org/ns/activitystreams"
{{- with $id }},
"id": "{{ . }}"
{{- end }}{{/* with id */}}
{{- with $type }}
{{- if eq (. | len) 1 }},{{/* single type */}}
"type": "{{index . 0}}"
{{- else }},{{/* multiple types */}}
"type": {{ . | jsonify }}
{{- end }}{{/* end if single type */}}
{{- end }}{{/* end with $type */}}
{{- with .Title | jsonify }},
"name": {{.}}
{{- end }}{{/* end with Title */}}
{{- if .Params.summary }}{{/* only consider explicitly-set summaries */}}
{{- with .Summary | jsonify }},
"summary": {{.}}
{{- end }}{{/* end with Summary */}}
{{- end }}{{/* end if Params.summary */}},
"attributedTo": { {{/* TODO: make this variable instead of hardcoded */}}
{{- $ownerId := "" }}
{{- with .Site.Home.OutputFormats.Get "as" }}
{{- $ownerId = .Permalink }}
{{- end }}
{{- with $ownerId }}
"id": "{{ $ownerId }}",
{{ end }}
"type": "Person",
"url": {
"href": "{{.Site.Home.Permalink}}"
{{- with .Language }},
"hreflang": "{{.}}"
{{- end }},
"rel": "canonical",
"mediaType": "text/html"
}
}
{{- if .Permalink }},
"url": {
"href": "{{.Permalink}}"
{{- with .Language }},
"hreflang": "{{.}}"
{{- end }},
"rel": "canonical",
"mediaType": "text/html"
}
{{- end }}{{/* end if Permalink */}}
{{- with .Pages }},
"orderedItems": [
{{- range $index, $item := . }}
{{- $itemId := "" }}
{{- with .OutputFormats.Get "as2" }}
{{- $itemId = .Permalink }}
{{- end }}
{
{{- with $itemId }}
"id": "{{ . }}"
{{- end }}
{{- if .Permalink }},
"url": {
"href": "{{.Permalink}}"
{{- with .Language }},
"hreflang": "{{.}}"
{{- end }},
"rel": "canonical",
"mediaType": "text/html"
}
{{- end }}{{/* end if Permalink */}}
}{{ if ne $index (sub (len $.Pages) 1)}},{{end}}
{{- end }}{{/* end range Pages */}}
]
{{- end }}{{/* end with Pages */}}
}

View file

@ -1,60 +1,63 @@
{{ define "body" }} {{ define "body" }}
<body class="layout-_default-list"> <body class="layout-_default-list">
<main class="h-feed hfeed" id="main"> <main class="h-feed hfeed" id="main">
{{ "<!-- the main header contains list metadata -->" | safeHTML }}
<header class="list-header section"> <header class="list-header section">
<div class="container"> <div class="container">
<h1 class="list-title p-name">{{.Title}}</h1> <h1 class="list-title p-name">{{.Title}}</h1>
{{ with (index .Site.Data.people "trwnh.com").a }} {{ with .Summary }}<p class="list-summary p-summary">{{ . }}</p>{{ end }}
<p class="list-author p-author">{{ partial "mf2/h-card.html" . }}</p> {{- with (index .Site.Data.people "trwnh.com").a }}{{/* TODO: make author a variable instead of assuming it's always me */}}
{{ end }} <p class="list-author p-author">
<p class="list-summary p-summary">{{ .Summary }}</p> {{- partial "mf2/h-card.html" . }}
</p>
{{ end -}}{{/* end with author h-card */ -}}
<div class="list-content e-content hugo-content"> <div class="list-content e-content hugo-content">
{{ .Content }} {{- .Content }}
</div> </div>
<p class="list-permalink">h-feed URL: <a href="{{.Permalink}}" rel="self" class="u-url">{{.Permalink}}</a></p> <p class="list-permalink">h-feed URL: <a href="{{.Permalink}}" rel="self" class="u-url">{{.Permalink}}</a></p>
</div> </div>
<hr> <hr>
</header> </header>
{{ "<!-- list the pages in this section/taxonomy/term/etc -->" | safeHTML }}
<section class="list-pages section"> <section class="list-pages section">
<div class="container"> <div class="container">
<ol class="list-of-pages-in-section"> <ol class="pages-list">
{{ range .RegularPages }} {{- range .Pages }}
{{ if .Title }} {{- if .Title }}{{/* indieweb post kind is probably article */}}
{{/* indieweb post kind is article */}} <li class="pages-list__item">
<li class="list-of-pages-in-section__item">
<article class="h-entry hentry"> <article class="h-entry hentry">
<p> <header style="display: contents">
<span><time class="dt-published published" datetime="{{.Date.Format "2006-01-02T15:04:05Z"}}">{{.Date.Format "2006-01-02"}}</time>: <a class="p-name entry-title" href="{{.Permalink}}">{{.Title}}</a></span><br> <span><time class="dt-published published" datetime="{{.Date.Format "2006-01-02T15:04:05Z"}}">{{.Date.Format "2006-01-02"}}</time>: <a class="u-url url" href="{{.Permalink}}"><h2 class="p-name entry-title">{{.Title}}</h2></a></span>
{{ if isset .Params "summary" }} {{- if isset .Params "summary" }}{{/* only use explicitly-set summaries, not auto-generated ones */ -}}
<br>
<span class="p-summary entry-summary">{{.Summary}}</span> <span class="p-summary entry-summary">{{.Summary}}</span>
{{ end }} {{- end }}{{/* end if isset .Params "summary" */ -}}
</p> </header>
</article> </article>
</li> </li>
{{ else }} {{- else }}{{/* indieweb post kind is probably note */}}
{{/* indieweb post kind is probably note */}} <li class="pages-list__item">
<li class="list-of-pages-in-section__item">
<article class="h-entry hentry"> <article class="h-entry hentry">
<header> <header>
<p><span><time class="dt-published published" datetime="{{.Date.Format "2006-01-02T15:04:05Z"}}">{{.Date.Format "2006-01-02"}}</time>: <a href="{{.Permalink}}">[link]</a></span></p> <p><span><time class="dt-published published" datetime="{{.Date.Format "2006-01-02T15:04:05Z"}}">{{.Date.Format "2006-01-02"}}</time>: <a href="{{.Permalink}}">untitled post</a></span></p>
</header> </header>
<section class="p-content entry-content"> <section class="p-content entry-content">
<p> <p>
{{ .Content | strings.Truncate 1000 }} {{- .Summary }}
</p> </p>
</section> </section>
{{ if not (eq .Content (.Content | strings.Truncate 1000)) }}
<footer> <footer>
<a href="{{.Permalink}}">Read more</a> <a href="{{.Permalink}}">Read more</a>{{/* then we need a read-more link. */ -}}
</footer> </footer>
{{ end }}
</article> </article>
</li> </li>
{{ end }} {{- end }}{{/* end if-title post type check */ -}}
{{ end }} {{- end }}{{/* end range over pages */ -}}
</ol> </ol>
</div> </div>
</section> </section>
</main> </main>
</body> </body>
{{ end }} {{ end }}{{/* end define-body */ -}}

View file

@ -0,0 +1,36 @@
{{ with .OutputFormats.Get "as2" }}
{{ $type := slice "Page" }}
{
"@context": "https://www.w3.org/ns/activitystreams"
{{- with .Permalink }},
"id": "{{ . }}"
{{- end }}
{{- with $type }}
{{- if eq (. | len) 1 }},
"type": "{{index . 0}}"
{{- else }},
"type": {{ . | jsonify }}
{{- end }}
{{- end }}
{{- if $.Params.title }}
{{- with $.Title | jsonify }},
"name": {{.}}
{{- end }}
{{- end }}
{{- if $.Params.summary }}
{{- with $.Summary | jsonify }},
"summary": {{.}}
{{- end }}
{{- end }}
{{- with $.Permalink }},
"url": {
"href": "{{.}}"
{{- with $.Language }},
"hreflang": "{{.}}"
{{- end }},
"rel": "canonical",
"mediaType": "text/html"
}
{{- end }}
}
{{ end }}

View file

@ -1,41 +1,37 @@
{{ define "head" }} {{- define "head" }}
{{ end }} {{- end }}
{{ define "body" }} {{- define "body" }}
<body class="layout-_default-single"> <body class="layout-_default-single">
<main id="main"> <main id="main">
<article class="page h-entry hentry" {{- if .Param "autonumbering" }} autonumbering {{- end }}{{- if .Param "toc" }} has-toc {{- end }}> <article class="page h-entry hentry" {{- if .Param "autonumbering" }} autonumbering {{- end }}{{- if .Param "toc" }} has-toc {{- end }}>
<header class="page-header section"> <header class="page-header section">
<div class="container"> <div class="container">
{{ with .Title | safeHTML }} {{ with .Title | safeHTML }}<h1 class="page-title p-name entry-title">{{.}}</h1>{{ end }}
<h1 class="page-title p-name entry-title">{{.}}</h1> {{- if isset .Params "summary" }}{{/* only use explicitly-set summaries, not auto-generated ones */}}
{{ end }} {{ with .Summary | safeHTML }}<p class="page-summary p-summary entry-summary">{{.}}</p>{{ end }}
{{ if isset .Params "summary" }} {{- end }}
{{ with .Summary | safeHTML }} {{- with (index .Site.Data.people "trwnh.com").a }}{{/* TODO: make author a variable instead of assuming it's always me */}}
<p class="page-summary p-summary entry-summary">{{.}}</p>
{{ end }}
{{ end }}
{{ with (index .Site.Data.people "trwnh.com").a }}
<p class="page-author p-author"> <p class="page-author p-author">
{{ partial "mf2/h-card.html" . }} {{- partial "mf2/h-card.html" . }}
</p> </p>
{{ end }} {{- end }}
{{ with .Date }} {{- with .Date }}
<p class="page-date"> <p class="page-date">
<time class="dt-published published" datetime='{{.UTC.Format "2006-01-02T15:04:05Z0700"}}'>{{ .Format "3:04 AM" }}<br>{{ .Format "Mon Jan 2, 2006"}}</time> <time class="dt-published published" datetime="{{.UTC.Format "2006-01-02T03:04:05Z"}}"><span class="time">{{ .Format "3:04 PM" }}</span> on <span class="date">{{ .Format "Mon Jan 2, 2006"}}</span></time>
</p> </p>
{{ end }} {{- end }}
{{ with .Params.inReplyTo }} {{- with .Params.inReplyTo }}
{{ range . }} {{- range . }}
<p class="page-inreplyto"> <p class="page-inreplyto">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" class="reply-icon" aria-hidden="true"><path fill="currentColor" d="M19 19v-4q0-1.25-.875-2.125T16 12H6.825l3.6 3.6L9 17l-6-6l6-6l1.425 1.4l-3.6 3.6H16q2.075 0 3.538 1.463T21 15v4z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" class="reply-icon" aria-hidden="true"><path fill="currentColor" d="M19 19v-4q0-1.25-.875-2.125T16 12H6.825l3.6 3.6L9 17l-6-6l6-6l1.425 1.4l-3.6 3.6H16q2.075 0 3.538 1.463T21 15v4z"/></svg>
<span>In reply to: <a class="u-in-reply-to" href="{{with .url}}{{.}}{{end}}">{{with .name}}<span>{{.}}</span>{{else}}<span>[link]</span>{{end}}</a></span> <span>In response to: <a class="u-in-reply-to" href="{{with .url}}{{.}}{{end}}">{{with .name}}<span>{{.}}</span>{{else}}<span>[link]</span>{{end}}</a></span>
</p> </p>
{{ end }} {{- end }}
{{ end }} {{- end }}
{{ with .Permalink }} {{- with .Permalink }}
<p class="page-permalink"><a class="u-url url" href="{{.}}">[permalink]</a></p> <p class="page-permalink">h-entry URL: <a class="u-url url" href="{{.}}">{{.}}</a></p>
{{ end }} {{- end -}}
{{/* {{ with .Params.source }} {{/* {{ with .Params.source }}
{{ $type := (printf "%T" .) }} {{ $type := (printf "%T" .) }}
{{ if eq $type "string"}} {{ if eq $type "string"}}
@ -49,7 +45,8 @@
</div> </div>
<hr> <hr>
</header> </header>
{{ if .Params.series }} {{- if .Params.series }}
{{ "<!-- article aside: this article is part of a series -->" | safeHTML }}
<aside class="page-series section"> <aside class="page-series section">
<div class="container"> <div class="container">
{{ with .Site.Taxonomies.series }} {{ with .Site.Taxonomies.series }}
@ -76,23 +73,18 @@
{{ end }} {{ end }}
</div> </div>
</aside> </aside>
{{ end }} {{- end }}
{{ if .Params.toc }} {{- if .Params.toc }}
{{ "<!-- article nav/toc -->" | safeHTML }}
<input type="checkbox" id="toc-toggle"> <input type="checkbox" id="toc-toggle">
<label for="toc-toggle" id="toc-toggle__label-show" role="button" tabindex=0>Show TOC</label> <label for="toc-toggle" id="toc-toggle__label-show" role="button" tabindex=0>Show TOC</label>
<label for="toc-toggle" id="toc-toggle__label-hide" role="button" tabindex=0>Hide TOC</label> <label for="toc-toggle" id="toc-toggle__label-hide" role="button" tabindex=0>Hide TOC</label>
<aside class="page-toc toc section"> {{ partial "components/table-of-contents.html" . }}
<div class="container"> {{- end }}
<details open> {{ "<!-- article content -->" | safeHTML }}
<summary class="toc-title">Contents</summary>
{{ .TableOfContents }}
</details>
</div>
</aside>
{{ end }}
<section class="page-content section"> <section class="page-content section">
<div class="container e-content entry-content hugo-content"> <div class="container e-content entry-content hugo-content">
{{ .Content }} {{ .Content -}}
</div> </div>
</section> </section>
</article> </article>

View file

@ -0,0 +1,45 @@
{{ define "body" }}
<body class="layout-_default-sitemap">
<main>
<header>
<div class="container">
<h1>{{.Title}}</h1>
<p>alternate formats: <a href="/sitemap.xml">[XML]</a></p>
</div>
</header>
<hr>
<section>
<div class="container">
<header>
<h2>most recently updated</h2>
</header>
<ul class="page-list">
{{ range where .Pages "Sitemap.Disable" "ne" true }}
{{- if .Permalink -}}
<li>
<a href="{{.Permalink}}">
{{.RelPermalink}}
</a>
<br>
<span>{{ .Lastmod.UTC.Format "2006-01-02" }}</span>
</li>
{{ end }}
{{ end }}
</ul>
</div>
</section>
</main>
<style>
.page-list {
display: flex;
flex-flow: column;
gap: 1rem;
margin-top: 1rem;
}
hr {
margin-top: 1rem;
margin-bottom: 1rem;
}
</style>
</body>
{{ end }}

View file

@ -0,0 +1,25 @@
{{ printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<test>test</test>
{{ range where .Pages "Sitemap.Disable" "ne" true }}
{{- if .Permalink -}}
<url>
<loc>{{ .Permalink }}</loc>{{ if not .Lastmod.IsZero }}
<lastmod>{{ safeHTML ( .Lastmod.UTC.Format "2006-01-02T15:04:05Z" ) }}</lastmod>{{ end }}{{ with .Sitemap.ChangeFreq }}
<changefreq>{{ . }}</changefreq>{{ end }}{{ if ge .Sitemap.Priority 0.0 }}
<priority>{{ .Sitemap.Priority }}</priority>{{ end }}{{ if .IsTranslated }}{{ range .Translations }}
<xhtml:link
rel="alternate"
hreflang="{{ .Language.LanguageCode }}"
href="{{ .Permalink }}"
/>{{ end }}
<xhtml:link
rel="alternate"
hreflang="{{ .Language.LanguageCode }}"
href="{{ .Permalink }}"
/>{{ end }}
</url>
{{- end -}}
{{ end }}
</urlset>

View file

@ -0,0 +1,8 @@
{{- $.Scratch.Add "pagesIndex" slice -}}
{{- range $index, $page := .Pages -}}
{{- if gt (len $page.Content) 0 -}}
{{- $pageData := (dict "title" (or $page.Title $page.File.LogicalName) "href" $page.Permalink "content" $page.Plain) -}}
{{- $.Scratch.Add "pagesIndex" $pageData -}}
{{- end -}}
{{- end -}}
{{- $.Scratch.Get "pagesIndex" | jsonify -}}

View file

@ -0,0 +1,28 @@
{{- $type := or .type "note" }}
{{- $title := .title }}
{{- $text := .text }}
{{ "<!-- alert block -->" | safeHTML }}
<aside class="admonition"{{ with $type }} data-type="{{.}}"{{end}}>
<header class="admonition-header">
{{- if in (slice "info" "note" "tip" "hint") $type }}
<svg class="admonition__icon info-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><title>info</title><path fill="currentColor" d="M11 17h2v-6h-2zm1-8q.425 0 .713-.288T13 8t-.288-.712T12 7t-.712.288T11 8t.288.713T12 9m0 13q-2.075 0-3.9-.788t-3.175-2.137T2.788 15.9T2 12t.788-3.9t2.137-3.175T8.1 2.788T12 2t3.9.788t3.175 2.137T21.213 8.1T22 12t-.788 3.9t-2.137 3.175t-3.175 2.138T12 22"/></svg>
{{- end -}}
{{- if in (slice "warning" "caution" "alert" "notice" "warn") $type }}
<svg class="admonition__icon warning-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><title>warning</title><path fill="currentColor" d="M1 21L12 2l11 19zm11-3q.425 0 .713-.288T13 17t-.288-.712T12 16t-.712.288T11 17t.288.713T12 18m-1-3h2v-5h-2z"/></svg>
{{- end }}
{{- if in (slice "danger" "important") $type}}
<svg class="admonition__icon danger-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 12 12"><title>danger</title><path fill="currentColor" d="M4.283 2.98a1.735 1.735 0 1 1 3.434 0l-.576 4.03a1.153 1.153 0 0 1-2.282 0zM7 10a1 1 0 1 1-2 0a1 1 0 0 1 2 0"/></svg>
{{- end -}}
{{- with $title }}
<p class="admonition__title" aria-hidden="true">{{.}}</p>
{{- end }}
</header>
{{- with $text }}
<div class="admonition__text">
{{ . }}
</div>
{{- end }}
</aside>
{{ "<!-- end alert block -->" | safeHTML }}

View file

@ -0,0 +1,52 @@
{{- $author := .author }}
{{- $content := .content }}
{{- $cite := .cite }}
{{- $title := .title }}
{{- $ref := .ref }}
{{- $caption := .caption }}
<article class="quote-block{{if $cite}} h-cite{{end}}{{ if $author}} h-x-quote{{end}}">
{{- with $author }}
<header style="display: contents">
<svg class="quote-block__icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 290 290" width="1em" height="1em" fill="currentColor"><title>quote</title><path d="M22.12 145v97.65h97.65V145H70.95c0-26.92 21.9-48.82 48.82-48.82V47.35c-53.93 0-97.65 43.72-97.65 97.65zm245.76-48.82V47.35c-53.93 0-97.65 43.72-97.65 97.65v97.65h97.65V145h-48.82c-.01-26.92 21.89-48.82 48.82-48.82z"></path></svg>
<p style="display: contents">
<span class="p-author">
{{- partial "mf2/h-card.html" . }}
</span>
<span style="display: none"> wrote:</span>
</p>
</header>
{{- else }}
<header style="display: contents">
<svg class="quote-block__icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 290 290" width="1em" height="1em" fill="currentColor"><title>quote</title><path d="M22.12 145v97.65h97.65V145H70.95c0-26.92 21.9-48.82 48.82-48.82V47.35c-53.93 0-97.65 43.72-97.65 97.65zm245.76-48.82V47.35c-53.93 0-97.65 43.72-97.65 97.65v97.65h97.65V145h-48.82c-.01-26.92 21.89-48.82 48.82-48.82z"></path></svg>
</header>
{{- end }}
{{- with $content }}
<section style="display: contents">
<blockquote class="quote-block__content e-content"{{ with $cite }} cite="{{.}}"{{end}}>
{{. -}}
</blockquote>
</section>
{{- end }}
{{- if (or $caption $cite) }}
<footer style="display: contents">
<p style="display: contents">
{{- with $caption }} {{/* caption is manually set and overrides cite+title+ref/etc */}}
<span class="quote-block__caption">{{ . | markdownify }}</span>
{{- else }} {{/* no caption, so make one from the cite+title and ref/etc */}}
{{- if $title }}
<span class="quote-block__caption">
<cite><a href="{{$cite}}" class="u-url">{{$title}}</a></cite>
{{- with $ref}} {{/* TODO: make this more modular? break ref into page numbers, edition, etc */}}
{{.}}
{{- end }}
</span>
{{- else }} {{/* no title, so add a basic link */}}
<span class="quote-block__caption">
<a href="{{$cite}}" class="u-url">(View source)</a> {{/* also stop here bc a ref with no title makes no sense? */}}
</span>
{{- end }}
{{- end }}
</p>
</footer>
{{- end }}
</article>

View file

@ -0,0 +1,27 @@
<nav class="table-of-contents" id="TableOfContents">
<h2 class="table-of-contents__title">Table of Contents</h2>
{{- template "headings" (index .Fragments.Headings 0) }}
</nav>
{{- define "headings" }}
{{ if .Title -}}
{{ print (strings.Repeat (sub .Level 1) "\t") }}<li data-depth="{{sub .Level 1}}">
{{ print (strings.Repeat (sub .Level 1) "\t") }}<a href="#{{.ID}}">{{.Title | htmlUnescape}}</a>
{{- if .Headings }}
{{ print (strings.Repeat (sub .Level 1) "\t") }}<ol data-depth="{{sub .Level 1}}">
{{- range $k, $v := .Headings }}
{{- template "headings" $v }}
{{- end }}
{{ print (strings.Repeat (sub .Level 1) "\t") }}</ol>
{{- end }}
{{ print (strings.Repeat (sub .Level 1) "\t") }}</li>
{{- else }}
{{- if .Headings -}}
{{ print (strings.Repeat .Level "\t") }}<ol data-depth="{{.Level}}">
{{- range $k, $v := .Headings }}
{{- template "headings" $v }}
{{- end }}
{{ print (strings.Repeat .Level "\t") }}</ol>
{{- end }}
{{- end }}
{{- end }}

View file

@ -1,10 +1,30 @@
{{ $name := .name }}
{{- $url := .url }}
{{- $icon := .icon }}
<span class="author-card h-card vcard"> <span class="author-card h-card vcard">
<a class="author-card__link u-url url" href="{{.url}}"> {{- if $url }}
<a class="author-card__link u-url url" href="{{$url}}">
{{- with $icon }}
<img class="author-card__avatar u-photo u-logo photo logo" <img class="author-card__avatar u-photo u-logo photo logo"
src="{{.icon}}" src="{{.}}"
alt="" alt=""
aria-hidden="true" aria-hidden="true"
/> />
<span class="author-card__name p-name fn">{{.name}}</span> {{- end }}
{{- with $name }}
<span class="author-card__name p-name fn">{{.}}</span>
{{- end }}
</a> </a>
{{- else }}
{{- with $icon }}
<img class="author-card__avatar u-photo u-logo photo logo"
src="{{.}}"
alt=""
aria-hidden="true"
/>
{{- end }}
{{- with $name }}
<span class="author-card__name p-name fn">{{.}}</span>
{{- end }}
{{- end }}
</span> </span>

View file

@ -1,5 +1,6 @@
{{- $firstH1 := partial "firstH1.html" . }} {{- $firstH1 := partial "firstH1.html" . }}
{{- $title := or .Title $firstH1 $.File.ContentBaseName }} {{- $title := or .Title $firstH1 $.File.ContentBaseName }}
{{- $fullTitle := print .Site.Title " | " $title}}
{{- $cover := ($.Resources.ByType "image").GetMatch "{*opengraph*}" -}} {{- $cover := ($.Resources.ByType "image").GetMatch "{*opengraph*}" -}}
{{ $icon := resources.GetMatch (default "" .Site.Params.icon) -}} {{ $icon := resources.GetMatch (default "" .Site.Params.icon) -}}
@ -15,7 +16,7 @@
<meta property="og:title" name="twitter:title" itemprop="name" content="{{ . }}" /> <meta property="og:title" name="twitter:title" itemprop="name" content="{{ . }}" />
{{- end }} {{- end }}
{{- else }} {{- else }}
<title>{{ or .Params.name (print .Site.Title " | " $title) }}</title> <title>{{ or .Params.name $fullTitle }}</title>
<meta property="og:title" name="twitter:title" itemprop="name" content="{{ or .Params.name $title }}" /> <meta property="og:title" name="twitter:title" itemprop="name" content="{{ or .Params.name $title }}" />
{{- end -}} {{- end -}}
@ -57,12 +58,12 @@
{{- with .Params.locale }}<meta property="og:locale" content="{{ . }}" />{{ end }} {{- with .Params.locale }}<meta property="og:locale" content="{{ . }}" />{{ end }}
{{- with .Params.videos }}{{- range . }} {{- with .Params.videos }}{{- range . }}
<meta property="og:video" content="{{ . | absURL }}" /> <meta property="og:video" content="{{ . | absURL }}" />
{{ end }}{{ end }} {{ end }}{{ end -}}
{{/*=== article ===*/}} {{/*=== article ===*/}}
{{ with or .Params.author .Site.Params.author -}} {{- with or .Params.author .Site.Params.author }}
{{ "<!-- author -->" | safeHTML }} {{ "<!-- author -->" | safeHTML }}
<meta name="author" property="article:author" content="{{ . }}" /> <meta name="author" property="article:author" content="{{ .name }}" />
{{- end }} {{- end }}
<meta property="article:publisher" content="{{ .Site.BaseURL }}" /> <meta property="article:publisher" content="{{ .Site.BaseURL }}" />
{{ "<!-- time -->" | safeHTML }} {{ "<!-- time -->" | safeHTML }}
@ -74,16 +75,17 @@
{{ end -}} {{ end -}}
{{/*=== section and keywords ===*/}} {{/*=== section and keywords ===*/}}
{{- "<!-- keywords -->" | safeHTML }} {{- with.Params.category -}}
{{ with.Params.category -}} {{- "<!-- keywords -->" | safeHTML -}}
<meta property="article:section" content="{{ . }}" /> <meta property="article:section" content="{{ . }}" />
{{- end -}} {{- end -}}
{{- with .Params.tags }} {{- with .Params.tags -}}
<meta property="article:tag" itemprop="keywords" name="keywords" content='{{ delimit . " "}}' /> <meta property="article:tag" itemprop="keywords" name="keywords" content='{{ delimit . ", "}}' />
{{- end -}} {{- end -}}
{{/* if it has a date, it's probably an article */}}
{{- if isset .Params "date" }} {{- if isset .Params "date" }}
{{ "<!-- article metadata -->" | safeHTML }} {{- "<!-- article metadata -->" | safeHTML }}
<meta property="og:type" content="article" /> <meta property="og:type" content="article" />
<meta itemprop="wordCount" content="{{ .WordCount }}" /> <meta itemprop="wordCount" content="{{ .WordCount }}" />
<script defer type="application/ld+json"> <script defer type="application/ld+json">
@ -93,18 +95,19 @@
"headline": {{ $title }}, "headline": {{ $title }},
"author": { "author": {
"@type": "Person", "@type": "Person",
"name": {{ or .Params.author .Site.Params.author }}, "name": {{ or .Params.author.name .Site.Params.author.name }},
"url": {{ .Site.BaseURL }} "url": {{ or .Params.author.url .Site.Params.author.url .Site.BaseURL }}
}, },
"datePublished": {{ .Date.UTC.Format "2006-01-02T03:04:05Z" }}, "datePublished": {{ .Date.UTC.Format "2006-01-02T03:04:05Z" }},
"description": {{ (or .Description .Summary) | plainify }}, "description": {{ (or .Description .Summary) | plainify }},
"wordCount": {{ .WordCount }}, "wordCount": {{ .WordCount }},
"mainEntityOfPage": {{.Permalink}}, "mainEntityOfPage": {{.Permalink}},
"dateModified": "{{ .Lastmod.UTC.Format "2006-01-02T03:04:05Z" }}", "dateModified": "{{ .Lastmod.UTC.Format "2006-01-02T03:04:05Z" }}",
{{ with or $cover $icon }}
"image": { "image": {
"@type": "ImageObject", "@type": "ImageObject",
"url": {{ with or $cover $icon }}{{ .Permalink | absURL }}{{ end }} "url": {{ .Permalink | absURL }}
}, },{{ end }}
"publisher": { "publisher": {
"@type": "WebSite", "@type": "WebSite",
"name": {{ .Site.Title }}, "name": {{ .Site.Title }},
@ -117,7 +120,8 @@
} }
</script> </script>
{{- else }} {{- else }}
{{ "<!-- webpage metadata -->" | safeHTML }} {{- /* otherwise if it doesn't have a date it's probably just a webpage */}}
{{- "<!-- webpage metadata -->" | safeHTML }}
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<script defer type="application/ld+json"> <script defer type="application/ld+json">
{ {
@ -141,14 +145,19 @@
{{- else -}} {{- else -}}
<link rel="shortcut icon" href='{{ $staticIcon }}' sizes="512x512" /> <link rel="shortcut icon" href='{{ $staticIcon }}' sizes="512x512" />
{{- end }} {{- end }}
<meta name="theme-color" content="#ffffff" /> <meta name="theme-color" content="#001a33" />{{/* TODO: make this a variable in site config */}}
<meta name="msapplication-TileColor" content="#ffffff" /> <meta name="msapplication-TileColor" content="#001a33" />{{/* TODO: make this a variable in site config */}}
<link rel="sitemap" type="application/xml" title="Sitemap" href="{{ .Site.BaseURL }}sitemap.xml" />
{{ with .OutputFormats.Get "RSS" -}} {{ with .OutputFormats.Get "RSS" -}}
<link href="{{ .Permalink }}" rel="feed alternate" type="application/rss+xml" title="{{ $.Site.Title }}" /> <link rel="feed alternate" type="application/rss+xml" title="{{ $fullTitle }}" href="{{ .Permalink }}" />
{{- end }}
{{ with .OutputFormats.Get "Atom" -}}
<link rel="feed alternate" type="application/atom+xml" title="{{ $fullTitle }}" href="{{ .Permalink }}" />
{{- end }}
{{ with .OutputFormats.Get "Sitemap" -}}
<link rel="sitemap" type="application/xml" title="Sitemap" href="{{ .Permalink }}" />
{{- end -}} {{- end -}}
{{/* robots */}} {{/* robots */ -}}
{{ "<!-- robots -->" | safeHTML }} {{ "<!-- robots -->" | safeHTML }}
<meta name="robots" content="index,follow" /> <meta name="robots" content="index,follow" />
<meta name="googlebot" content="index,follow" /> <meta name="googlebot" content="index,follow" />

View file

@ -1,12 +1,19 @@
<style> <style>
a[href]:after {
a[href^="{{.Site.BaseURL}}"] svg content: '';
{ display: inline-grid;
display: none !important; width: 1rem;
height: 0.875rem;
font-size: 1rem;
background-color: currentColor;
mask: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxnIGZpbGw9IndoaXRlIj48cGF0aCBkPSJNMTUuNjQgNy4wMjVoLTMuNjIydi0yaDd2N2gtMnYtMy41NWwtNC45MTQgNC45MTRsLTEuNDE0LTEuNDE0eiIvPjxwYXRoIGQ9Ik0xMC45ODIgNi45NzVoLTZ2MTJoMTJ2LTZoLTJ2NGgtOHYtOGg0eiIvPjwvZz48L3N2Zz4=');
mask-repeat: no-repeat;
} }
a[href^="http://localhost"] svg a[href^="{{.Site.BaseURL}}"]:after,
a[href^="#"]:after
{ {
display: none !important; background-image: none !important;
display: none;
} }
</style> </style>

View file

@ -0,0 +1,5 @@
{{ $type := or (.Get "type") "note" }}
{{ $title := or (.Get "title") (strings.FirstUpper $type) }}
{{ $text := .Inner }}
{{ $opts := dict "ctx" . "type" $type "title" $title "text" $text }}
{{ partial "components/admonition.html" $opts }}

View file

@ -5,7 +5,15 @@
{{ end }} {{ end }}
{{ $content := .Page.RenderString (dict "display" "block") .Inner }} {{ $content := .Page.RenderString (dict "display" "block") .Inner }}
{{ $cite := .Get "cite" }} {{ $cite := .Get "cite" }}
{{ $name := .Get "name" }} {{ $title := .Get "title" }}
{{ $ref := .Get "ref" }}
{{ $caption := .Get "caption" }} {{ $caption := .Get "caption" }}
{{ $opts := dict "ctx" . "author" $author "content" $content "cite" $cite "name" $name "caption" $caption }} {{ $opts := dict
"ctx" .
"author" $author
"content" $content
"cite" $cite
"title" $title
"ref" $ref
"caption" $caption }}
{{ partial "mf2/h-cite.html" $opts }} {{ partial "mf2/h-cite.html" $opts }}

View file

@ -1,6 +1 @@
<aside class="toc section"> {{ partial "components/table-of-contents.html" .Page }}
<details open>
<summary class="toc-title">Contents</summary>
{{ .Page.TableOfContents }}
</details>
</aside>

View file

@ -1,41 +0,0 @@
{{ define "main" }}
<main>
<header>
<div class="container">
<h1>{{.Title}}</h1>
<p>alternate formats: <a href="/sitemap.xml">[XML]</a></p>
</div>
</header>
<hr>
<section>
<div class="container">
<header>
<h2>most recently updated</h2>
</header>
<ul class="page-list">
{{ range sort .Site.RegularPages ".Lastmod.UTC" "desc" }}
<li>
<a href="{{.Permalink}}">
{{.File.Path}}
</a>
<br>
<span>{{ .Lastmod.UTC.Format "2006-01-02" }}</span>
</li>
{{ end }}
</ul>
</div>
</section>
</main>
<style>
.page-list {
display: flex;
flex-flow: column;
gap: 1rem;
margin-top: 1rem;
}
hr {
margin-top: 1rem;
margin-bottom: 1rem;
}
</style>
{{ end }}