@geekity/cms 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +344 -47
- package/admin/components/admin-bar.njk +26 -0
- package/admin/components/public-admin-bar.njk +41 -0
- package/admin/layouts/base.njk +1 -0
- package/admin/layouts/shell.njk +8 -19
- package/admin/pages/comments/all.njk +3 -0
- package/admin/pages/settings/discussion.njk +53 -0
- package/admin/pages/settings/email.njk +20 -0
- package/admin/pages/tools/personal-data.njk +67 -0
- package/admin/pages/users/edit.njk +1 -1
- package/admin/static/admin-bar.css +137 -0
- package/admin/static/admin.css +1 -102
- package/dist/admin/comments.d.ts +5 -0
- package/dist/admin/comments.d.ts.map +1 -1
- package/dist/admin/comments.js +15 -1
- package/dist/admin/comments.js.map +1 -1
- package/dist/admin/headers.d.ts +14 -5
- package/dist/admin/headers.d.ts.map +1 -1
- package/dist/admin/headers.js +18 -6
- package/dist/admin/headers.js.map +1 -1
- package/dist/admin/index.d.ts +2 -2
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/menu.d.ts.map +1 -1
- package/dist/admin/menu.js +5 -1
- package/dist/admin/menu.js.map +1 -1
- package/dist/admin/personal-data.d.ts +35 -0
- package/dist/admin/personal-data.d.ts.map +1 -0
- package/dist/admin/personal-data.js +94 -0
- package/dist/admin/personal-data.js.map +1 -0
- package/dist/admin/preview.d.ts.map +1 -1
- package/dist/admin/preview.js +1 -1
- package/dist/admin/preview.js.map +1 -1
- package/dist/admin/routes.d.ts.map +1 -1
- package/dist/admin/routes.js +20 -3
- package/dist/admin/routes.js.map +1 -1
- package/dist/admin/session.d.ts +42 -1
- package/dist/admin/session.d.ts.map +1 -1
- package/dist/admin/session.js +74 -4
- package/dist/admin/session.js.map +1 -1
- package/dist/admin/settings-discussion.d.ts.map +1 -1
- package/dist/admin/settings-discussion.js +9 -1
- package/dist/admin/settings-discussion.js.map +1 -1
- package/dist/admin/settings-email.d.ts.map +1 -1
- package/dist/admin/settings-email.js +10 -1
- package/dist/admin/settings-email.js.map +1 -1
- package/dist/admin/settings.d.ts +39 -0
- package/dist/admin/settings.d.ts.map +1 -1
- package/dist/admin/settings.js +144 -0
- package/dist/admin/settings.js.map +1 -1
- package/dist/admin/store.d.ts +65 -0
- package/dist/admin/store.d.ts.map +1 -1
- package/dist/admin/store.js +130 -5
- package/dist/admin/store.js.map +1 -1
- package/dist/admin/templates.d.ts +2 -0
- package/dist/admin/templates.d.ts.map +1 -1
- package/dist/admin/templates.js +2 -0
- package/dist/admin/templates.js.map +1 -1
- package/dist/admin/tools.d.ts +2 -0
- package/dist/admin/tools.d.ts.map +1 -1
- package/dist/admin/tools.js +3 -2
- package/dist/admin/tools.js.map +1 -1
- package/dist/avatars/avatars.d.ts +112 -0
- package/dist/avatars/avatars.d.ts.map +1 -0
- package/dist/avatars/avatars.js +259 -0
- package/dist/avatars/avatars.js.map +1 -0
- package/dist/avatars/index.d.ts +4 -0
- package/dist/avatars/index.d.ts.map +1 -0
- package/dist/avatars/index.js +3 -0
- package/dist/avatars/index.js.map +1 -0
- package/dist/avatars/routes.d.ts +11 -0
- package/dist/avatars/routes.d.ts.map +1 -0
- package/dist/avatars/routes.js +33 -0
- package/dist/avatars/routes.js.map +1 -0
- package/dist/comments/index.d.ts +2 -2
- package/dist/comments/index.d.ts.map +1 -1
- package/dist/comments/index.js +1 -1
- package/dist/comments/index.js.map +1 -1
- package/dist/comments/records.d.ts +58 -2
- package/dist/comments/records.d.ts.map +1 -1
- package/dist/comments/records.js +244 -47
- package/dist/comments/records.js.map +1 -1
- package/dist/comments/routes.d.ts.map +1 -1
- package/dist/comments/routes.js +8 -2
- package/dist/comments/routes.js.map +1 -1
- package/dist/comments/viewer.d.ts +13 -0
- package/dist/comments/viewer.d.ts.map +1 -1
- package/dist/comments/viewer.js +13 -1
- package/dist/comments/viewer.js.map +1 -1
- package/dist/config.d.ts +35 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +58 -0
- package/dist/config.js.map +1 -1
- package/dist/contact/index.d.ts +1 -1
- package/dist/contact/index.d.ts.map +1 -1
- package/dist/contact/index.js +1 -1
- package/dist/contact/index.js.map +1 -1
- package/dist/contact/records.d.ts +8 -2
- package/dist/contact/records.d.ts.map +1 -1
- package/dist/contact/records.js +13 -6
- package/dist/contact/records.js.map +1 -1
- package/dist/env.d.ts +25 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/federation/delivery.d.ts +3 -1
- package/dist/federation/delivery.d.ts.map +1 -1
- package/dist/federation/delivery.js +6 -1
- package/dist/federation/delivery.js.map +1 -1
- package/dist/federation/federation.d.ts +3 -0
- package/dist/federation/federation.d.ts.map +1 -1
- package/dist/federation/federation.js.map +1 -1
- package/dist/federation/inbox.d.ts +7 -5
- package/dist/federation/inbox.d.ts.map +1 -1
- package/dist/federation/inbox.js +15 -32
- package/dist/federation/inbox.js.map +1 -1
- package/dist/federation/index.d.ts +2 -0
- package/dist/federation/index.d.ts.map +1 -1
- package/dist/federation/index.js +1 -0
- package/dist/federation/index.js.map +1 -1
- package/dist/federation/mount.js +6 -1
- package/dist/federation/mount.js.map +1 -1
- package/dist/federation/profiles.d.ts +95 -0
- package/dist/federation/profiles.d.ts.map +1 -0
- package/dist/federation/profiles.js +171 -0
- package/dist/federation/profiles.js.map +1 -0
- package/dist/federation/quotes.d.ts +2 -0
- package/dist/federation/quotes.d.ts.map +1 -1
- package/dist/federation/quotes.js +4 -0
- package/dist/federation/quotes.js.map +1 -1
- package/dist/federation/relays.d.ts +3 -1
- package/dist/federation/relays.d.ts.map +1 -1
- package/dist/federation/relays.js +6 -1
- package/dist/federation/relays.js.map +1 -1
- package/dist/federation/replies.d.ts +36 -4
- package/dist/federation/replies.d.ts.map +1 -1
- package/dist/federation/replies.js +71 -15
- package/dist/federation/replies.js.map +1 -1
- package/dist/images/icons.d.ts.map +1 -1
- package/dist/images/icons.js +3 -2
- package/dist/images/icons.js.map +1 -1
- package/dist/images/index.d.ts +2 -2
- package/dist/images/index.d.ts.map +1 -1
- package/dist/images/markup.d.ts +15 -4
- package/dist/images/markup.d.ts.map +1 -1
- package/dist/images/markup.js +24 -15
- package/dist/images/markup.js.map +1 -1
- package/dist/images/paths.d.ts +11 -0
- package/dist/images/paths.d.ts.map +1 -1
- package/dist/images/paths.js +17 -1
- package/dist/images/paths.js.map +1 -1
- package/dist/images/variants.d.ts +13 -3
- package/dist/images/variants.d.ts.map +1 -1
- package/dist/images/variants.js +13 -11
- package/dist/images/variants.js.map +1 -1
- package/dist/index.d.ts +34 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +91 -18
- package/dist/index.js.map +1 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +1 -1
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/optouts.d.ts +6 -0
- package/dist/notifications/optouts.d.ts.map +1 -1
- package/dist/notifications/optouts.js +17 -0
- package/dist/notifications/optouts.js.map +1 -1
- package/dist/notifications/routes.js +1 -1
- package/dist/notifications/routes.js.map +1 -1
- package/dist/privacy/erase.d.ts +45 -0
- package/dist/privacy/erase.d.ts.map +1 -0
- package/dist/privacy/erase.js +76 -0
- package/dist/privacy/erase.js.map +1 -0
- package/dist/privacy/policy.d.ts +32 -0
- package/dist/privacy/policy.d.ts.map +1 -0
- package/dist/privacy/policy.js +37 -0
- package/dist/privacy/policy.js.map +1 -0
- package/dist/privacy/retention.d.ts +79 -0
- package/dist/privacy/retention.d.ts.map +1 -0
- package/dist/privacy/retention.js +133 -0
- package/dist/privacy/retention.js.map +1 -0
- package/dist/web/admin-bar.d.ts +21 -0
- package/dist/web/admin-bar.d.ts.map +1 -0
- package/dist/web/admin-bar.js +145 -0
- package/dist/web/admin-bar.js.map +1 -0
- package/dist/web/assets.d.ts +29 -1
- package/dist/web/assets.d.ts.map +1 -1
- package/dist/web/assets.js +48 -3
- package/dist/web/assets.js.map +1 -1
- package/dist/web/compression.d.ts +19 -0
- package/dist/web/compression.d.ts.map +1 -0
- package/dist/web/compression.js +160 -0
- package/dist/web/compression.js.map +1 -0
- package/dist/web/context.d.ts +5 -1
- package/dist/web/context.d.ts.map +1 -1
- package/dist/web/context.js +5 -2
- package/dist/web/context.js.map +1 -1
- package/dist/web/conversation.d.ts +9 -1
- package/dist/web/conversation.d.ts.map +1 -1
- package/dist/web/conversation.js +79 -17
- package/dist/web/conversation.js.map +1 -1
- package/dist/web/index.d.ts +1 -0
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +1 -0
- package/dist/web/index.js.map +1 -1
- package/dist/web/maintenance.d.ts.map +1 -1
- package/dist/web/maintenance.js +7 -2
- package/dist/web/maintenance.js.map +1 -1
- package/dist/web/render.d.ts +27 -0
- package/dist/web/render.d.ts.map +1 -1
- package/dist/web/render.js +77 -38
- package/dist/web/render.js.map +1 -1
- package/dist/web/routes.d.ts.map +1 -1
- package/dist/web/routes.js +67 -28
- package/dist/web/routes.js.map +1 -1
- package/dist/web/templates.d.ts.map +1 -1
- package/dist/web/templates.js +9 -2
- package/dist/web/templates.js.map +1 -1
- package/dist/web/well-known.d.ts +21 -0
- package/dist/web/well-known.d.ts.map +1 -0
- package/dist/web/well-known.js +66 -0
- package/dist/web/well-known.js.map +1 -0
- package/dist/webmention/fetch-public.d.ts +34 -0
- package/dist/webmention/fetch-public.d.ts.map +1 -0
- package/dist/webmention/fetch-public.js +101 -0
- package/dist/webmention/fetch-public.js.map +1 -0
- package/dist/webmention/reply-context.d.ts.map +1 -1
- package/dist/webmention/reply-context.js +12 -89
- package/dist/webmention/reply-context.js.map +1 -1
- package/dist/webmention/service.d.ts.map +1 -1
- package/dist/webmention/service.js +5 -1
- package/dist/webmention/service.js.map +1 -1
- package/package.json +1 -1
- package/templates/site/content/_data/site.json +7 -1
- package/templates/site/content/pages/privacy.md +127 -0
- package/themes/default/README.md +165 -22
- package/themes/default/layouts/base.njk +51 -10
package/themes/default/README.md
CHANGED
|
@@ -93,7 +93,11 @@ reason rather than quietly left out.
|
|
|
93
93
|
Resolution order for any template is then that theme first, this directory
|
|
94
94
|
second, one file at a time. The theme above replaces the post layout and keeps
|
|
95
95
|
receiving updates to every other template, and `static/style.css` replaces the
|
|
96
|
-
stylesheet the same way — assets under `/theme/` resolve in the same order.
|
|
96
|
+
stylesheet the same way — assets under `/theme/` resolve in the same order.
|
|
97
|
+
Link a theme file with the `asset` filter rather than a literal `/theme/`
|
|
98
|
+
path: `{{ "style.css" | asset }}` puts a hash of the file in the URL, so a
|
|
99
|
+
reader's browser keeps it for a year and still fetches the new one the moment
|
|
100
|
+
it changes. The plain `/theme/style.css` keeps working, cached for an hour. A
|
|
97
101
|
site that has chosen no theme reads this one and nothing else, and a theme
|
|
98
102
|
sitting in `themes/` that the setting does not name is never on the path at
|
|
99
103
|
all. A `theme` naming a folder that is not there, or is not a theme, falls back
|
|
@@ -117,8 +121,9 @@ packaged ones by name:
|
|
|
117
121
|
{% endblock %}
|
|
118
122
|
```
|
|
119
123
|
|
|
120
|
-
`layouts/base.njk` defines the blocks `title`, `
|
|
121
|
-
`
|
|
124
|
+
`layouts/base.njk` defines the blocks `title`, `speculationRules`,
|
|
125
|
+
`viewTransitions`, `head`, `alternates`, `header`, `content`, `footer` and
|
|
126
|
+
`scripts`, so most sites never have to copy it.
|
|
122
127
|
|
|
123
128
|
Two layouts are named for the pages the Reading settings pick:
|
|
124
129
|
|
|
@@ -188,6 +193,71 @@ block:
|
|
|
188
193
|
{% endblock %}
|
|
189
194
|
```
|
|
190
195
|
|
|
196
|
+
### The admin bar
|
|
197
|
+
|
|
198
|
+
When somebody signed in to the admin reads the site, the CMS puts the admin bar
|
|
199
|
+
across the top of every HTML page: the site title linking to the dashboard,
|
|
200
|
+
**+ New**, **Edit Post** or **Edit Page** on a post or a page, and the account
|
|
201
|
+
menu. A theme draws none of it and needs no template for it. The CMS inserts a
|
|
202
|
+
`<geekity-admin-bar>` element straight after the page's `<body>` tag, and an
|
|
203
|
+
anonymous reader's page is unchanged, byte for byte.
|
|
204
|
+
|
|
205
|
+
The bar keeps its markup and its stylesheet in a shadow root, so a theme's CSS
|
|
206
|
+
cannot restyle it and its CSS cannot restyle the theme. The element carries an
|
|
207
|
+
inline style that resets it and fixes it to the top of the window, full width,
|
|
208
|
+
above the page. Padding or margins on `<body>`, and rules such as `body > *` or
|
|
209
|
+
a grid on `<body>`, do not move or resize it. It stays in place while the page
|
|
210
|
+
scrolls.
|
|
211
|
+
|
|
212
|
+
The bar takes no room by itself, so the CMS makes room for it from outside. On
|
|
213
|
+
a page that carries the bar:
|
|
214
|
+
|
|
215
|
+
- `<html>` has the class `geekity-admin-bar`.
|
|
216
|
+
- A stylesheet in `<head>` declares `--geekity-admin-bar-height` on `:root`
|
|
217
|
+
and sets `margin-top: var(--geekity-admin-bar-height) !important` on
|
|
218
|
+
`<html>`, which pushes the whole page down below the bar. The height is
|
|
219
|
+
40px, or 80px on a screen 600px wide or less, where the bar can wrap onto a
|
|
220
|
+
second line. A script in the bar then measures it and sets the real height
|
|
221
|
+
on `<html>`, so the push matches the bar at any width, and is 0 when a theme
|
|
222
|
+
hides the bar.
|
|
223
|
+
- The same stylesheet sets `scroll-padding-top` on `<html>` to the bar's
|
|
224
|
+
height, so a link to an anchor on the page, such as a skip link or a
|
|
225
|
+
footnote, scrolls its target to below the bar. It is declared with no
|
|
226
|
+
specificity, so a theme that sets its own `scroll-padding-top` keeps it.
|
|
227
|
+
|
|
228
|
+
A theme with a sticky or fixed header of its own offsets it by the bar's
|
|
229
|
+
height. The fallback of `0` applies on every page the bar is not on:
|
|
230
|
+
|
|
231
|
+
```css
|
|
232
|
+
.site-header {
|
|
233
|
+
position: sticky;
|
|
234
|
+
top: var(--geekity-admin-bar-height, 0);
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
A theme that sets its own `scroll-padding-top` adds the bar to it the same
|
|
239
|
+
way, for example `calc(var(--geekity-admin-bar-height, 0px) + 4rem)`. Use the
|
|
240
|
+
`geekity-admin-bar` class for any other change, for example
|
|
241
|
+
`.geekity-admin-bar .site-header { ... }`.
|
|
242
|
+
|
|
243
|
+
A theme that must not show the bar can hide it:
|
|
244
|
+
|
|
245
|
+
```css
|
|
246
|
+
geekity-admin-bar {
|
|
247
|
+
display: none !important;
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
The `!important` is needed because it has to beat the element's inline style.
|
|
252
|
+
With script on, the bar then measures 0 and the page moves back up. For a
|
|
253
|
+
reader without script, the theme also removes the margin:
|
|
254
|
+
|
|
255
|
+
```css
|
|
256
|
+
html.geekity-admin-bar {
|
|
257
|
+
margin-top: 0 !important;
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
191
261
|
### An entry
|
|
192
262
|
|
|
193
263
|
`layouts/post.njk` and `layouts/page.njk` draw the same thing, the source
|
|
@@ -541,6 +611,59 @@ A site that wants a different graph — more types, an `Organization` publisher,
|
|
|
541
611
|
nothing at all — writes its own `partials/jsonld.njk` and that file replaces
|
|
542
612
|
this one, like any other partial.
|
|
543
613
|
|
|
614
|
+
### Prefetching and page transitions
|
|
615
|
+
|
|
616
|
+
Two tags near the top of `<head>` make moving between pages feel faster. Both
|
|
617
|
+
are progressive enhancement: a browser that does not support one ignores it.
|
|
618
|
+
|
|
619
|
+
**Speculation rules.** The `speculationRules` block prints one `<script
|
|
620
|
+
type="speculationrules">` that asks the browser to prefetch a page the reader
|
|
621
|
+
is about to open. It covers links to this site's own pages, at `moderate`
|
|
622
|
+
eagerness, which in Chrome is a hover of about 200 milliseconds or the start of
|
|
623
|
+
a tap. It never prefetches:
|
|
624
|
+
|
|
625
|
+
- `/admin` and anything under `/admin/`, which includes `/admin/logout`.
|
|
626
|
+
- Any path with `logout` in it.
|
|
627
|
+
- A feed: `/feed/`, `/feed/atom/`, `/feed/json/`, and any other path that has
|
|
628
|
+
a `/feed/` segment, such as a tag's feed or a post's comments feed.
|
|
629
|
+
- A file under `/uploads/`.
|
|
630
|
+
- A URL with a query, such as `/search/?q=words`.
|
|
631
|
+
|
|
632
|
+
Every pattern is written through the `url` filter, so a site served from a
|
|
633
|
+
subdirectory prefetches only its own pages.
|
|
634
|
+
|
|
635
|
+
**No rules reach a signed-in reader.** The CMS removes every `<script
|
|
636
|
+
type="speculationrules">` from a page it draws for somebody signed in, the same
|
|
637
|
+
page that carries [the admin bar](#the-admin-bar). That page is drawn for one
|
|
638
|
+
person and never stored, so a prefetch of it is wasted work. A site theme that
|
|
639
|
+
writes its own rules does not have to check who is reading.
|
|
640
|
+
|
|
641
|
+
**View transitions.** The `viewTransitions` block prints a `<style>` that opts
|
|
642
|
+
into cross-document view transitions. The browser cross-fades from one page of
|
|
643
|
+
the site to the next, with no JavaScript. The rule is inside `@media
|
|
644
|
+
(prefers-reduced-motion: no-preference)`, so a reader who asks for reduced
|
|
645
|
+
motion gets no transition:
|
|
646
|
+
|
|
647
|
+
```css
|
|
648
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
649
|
+
@view-transition {
|
|
650
|
+
navigation: auto;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
A site theme that wants a different animation adds `::view-transition-*` rules
|
|
656
|
+
to its own stylesheet.
|
|
657
|
+
|
|
658
|
+
**Turning either off.** Override its block with nothing in it:
|
|
659
|
+
|
|
660
|
+
```njk
|
|
661
|
+
{% extends "layouts/base.njk" %}
|
|
662
|
+
|
|
663
|
+
{% block speculationRules %}{% endblock %}
|
|
664
|
+
{% block viewTransitions %}{% endblock %}
|
|
665
|
+
```
|
|
666
|
+
|
|
544
667
|
### Colours
|
|
545
668
|
|
|
546
669
|
`static/style.css` is the source design: a serif body and sans headings at an
|
|
@@ -1051,8 +1174,9 @@ Category archives get their own three the same way.
|
|
|
1051
1174
|
## The conversation
|
|
1052
1175
|
|
|
1053
1176
|
`conversation` is what has been said about a post: the fediverse replies, likes
|
|
1054
|
-
and boosts its inbox was sent, the
|
|
1055
|
-
the
|
|
1177
|
+
and boosts its inbox was sent, the fediverse posts that quoted it with its
|
|
1178
|
+
author's approval, the comments people left on the page itself, and the
|
|
1179
|
+
webmentions other pages sent it.
|
|
1056
1180
|
It is on the context of a rendered post **only when there is something in it**,
|
|
1057
1181
|
so a post nobody has answered renders no empty section and a layout can simply
|
|
1058
1182
|
ask:
|
|
@@ -1092,29 +1216,29 @@ the thread, because it is not an answer.
|
|
|
1092
1216
|
| `replies` | The replies to the post, oldest first, each carrying its own `replies`. See below. |
|
|
1093
1217
|
| `likes` | The likes, oldest first, in the same shape. |
|
|
1094
1218
|
| `boosts` | The boosts, oldest first, in the same shape. A webmention `repost` is one of them. |
|
|
1095
|
-
| `mentions` | The pages
|
|
1219
|
+
| `mentions` | The pages and fediverse quotes about this post, oldest first, in the same shape. |
|
|
1096
1220
|
| `counts.replies` | How many replies, counted through the whole thread rather than the top of it. |
|
|
1097
1221
|
| `counts.likes`, `counts.boosts`, `counts.mentions` | How many of each. |
|
|
1098
1222
|
| `counts.total` | All four added up. Zero never reaches a template: there would be no `conversation` at all. |
|
|
1099
1223
|
|
|
1100
1224
|
Each entry — a reply, a like or a boost — is:
|
|
1101
1225
|
|
|
1102
|
-
| Key | What it holds
|
|
1103
|
-
| ---------------- |
|
|
1104
|
-
| `id` | What it is called: a reply's own note id, which is what an answer to it names.
|
|
1105
|
-
| `source` | `"activitypub"` for a fediverse reply, `"comment"` for one left on the page, `"webmention"` for another page linking here.
|
|
1106
|
-
| `kind` | `"reply"`, `"like"`, `"boost"`, `"repost"` or `"mention"`.
|
|
1107
|
-
| `author.name` | The best name available: their display name, else their handle, else their id. For a webmention, the source's `h-card` name, else its host.
|
|
1108
|
-
| `author.handle` | `@user@host`, or `null`. Taken from the follower profile the site holds, else guessed from the actor URL. `null` for a native comment.
|
|
1109
|
-
| `author.url` | Their profile page, the website a commenter typed, or a webmention author's `u-url`. May be `null`, so guard the link.
|
|
1110
|
-
| `author.avatar` | Their avatar, or `null`. The site knows one for
|
|
1111
|
-
| `author.actorId` | Their id, which is what identifies them however they are named. `null` for a native comment.
|
|
1112
|
-
| `url` | Where it can be read: the remote note's `url` for a fediverse reply, the source page for a webmention, and `{permalink}#comment-{id}` — this page's own anchor — for a native comment.
|
|
1113
|
-
| `content` | What it says, **already sanitised**, so print it with `\| safe`. Empty for a like or a boost.
|
|
1114
|
-
| `published` | A `Date`: when it was published, or when it arrived if it did not say. Use the `date` filter.
|
|
1115
|
-
| `inReplyTo` | What it answers — the post's ActivityPub id, or another reply's — and `null` for a reaction.
|
|
1116
|
-
| `status` | `"published"`. On the record for the sources that moderate.
|
|
1117
|
-
| `replies` | The replies to this one, oldest first, nested as deep as the site has seen.
|
|
1226
|
+
| Key | What it holds |
|
|
1227
|
+
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1228
|
+
| `id` | What it is called: a reply's own note id, which is what an answer to it names. |
|
|
1229
|
+
| `source` | `"activitypub"` for a fediverse reply, `"comment"` for one left on the page, `"webmention"` for another page linking here. |
|
|
1230
|
+
| `kind` | `"reply"`, `"like"`, `"boost"`, `"repost"` or `"mention"`. A `repost` comes from a webmention; a `mention` is a webmention or an approved fediverse quote. |
|
|
1231
|
+
| `author.name` | The best name available: their display name, else their handle, else their server, else their id. For a webmention, the source's `h-card` name, else its host. |
|
|
1232
|
+
| `author.handle` | `@user@host`, or `null`. Taken from the follower or actor profile the site holds, else guessed from the actor URL unless it ends in a number. `null` for a native comment. |
|
|
1233
|
+
| `author.url` | Their profile page, the website a commenter typed, or a webmention author's `u-url`. May be `null`, so guard the link. |
|
|
1234
|
+
| `author.avatar` | Their avatar, or `null`. The site knows one for a fediverse actor whose profile it holds and for a webmention whose `h-card` carried a `u-photo`. Always a same-origin `/_geekity/avatars/…` path, never the remote URL: see [Avatars](#avatars). |
|
|
1235
|
+
| `author.actorId` | Their id, which is what identifies them however they are named. `null` for a native comment. |
|
|
1236
|
+
| `url` | Where it can be read: the remote note's `url` for a fediverse reply or quote, the source page for a webmention, and `{permalink}#comment-{id}` — this page's own anchor — for a native comment. |
|
|
1237
|
+
| `content` | What it says, **already sanitised**, so print it with `\| safe`. Empty for a like or a boost. |
|
|
1238
|
+
| `published` | A `Date`: when it was published, or when it arrived if it did not say. Use the `date` filter. |
|
|
1239
|
+
| `inReplyTo` | What it answers — the post's ActivityPub id, or another reply's — and `null` for a reaction. |
|
|
1240
|
+
| `status` | `"published"`. On the record for the sources that moderate. |
|
|
1241
|
+
| `replies` | The replies to this one, oldest first, nested as deep as the site has seen. |
|
|
1118
1242
|
|
|
1119
1243
|
Three rules decide what is in the thread, and they are the CMS's rather than a
|
|
1120
1244
|
theme's: a reply whose author deleted it is gone, and its own answers move up to
|
|
@@ -1171,6 +1295,24 @@ and then loops over exactly the keys above. The slug is what names the post's
|
|
|
1171
1295
|
comment file under `content/_data/comments/`; the format is documented in
|
|
1172
1296
|
backlog doc-6.
|
|
1173
1297
|
|
|
1298
|
+
### Avatars
|
|
1299
|
+
|
|
1300
|
+
A reader's browser never fetches a face from the server it lives on. The
|
|
1301
|
+
`author.avatar` a theme is handed is a path on this site, under
|
|
1302
|
+
`/_geekity/avatars/`, and the CMS fetches the picture, crops it to an
|
|
1303
|
+
80-pixel square WebP (twice the 40 pixels this theme shows a commenter at)
|
|
1304
|
+
and keeps it under `data/avatars/`. Print it as it is; it needs no filter.
|
|
1305
|
+
|
|
1306
|
+
The route serves only the pictures the site shows: a follower who has
|
|
1307
|
+
liked, boosted, quoted or answered a post, and the author of an approved
|
|
1308
|
+
comment or webmention. Any other URL is a 404, and nothing is fetched for it.
|
|
1309
|
+
Pictures are fetched in the background when the site starts and every six
|
|
1310
|
+
hours after, refreshed once they are a week old, and forgotten once nothing
|
|
1311
|
+
shows them. A picture that cannot be fetched, is over 2 MB, or is not a JPEG,
|
|
1312
|
+
PNG, GIF, WebP or AVIF is served as a grey placeholder, and tried again an
|
|
1313
|
+
hour later. `data/avatars/` is a cache: delete it and every picture is fetched
|
|
1314
|
+
again.
|
|
1315
|
+
|
|
1174
1316
|
## The comment form
|
|
1175
1317
|
|
|
1176
1318
|
`commentForm` is on the context of a rendered post **only when that post is
|
|
@@ -1338,6 +1480,7 @@ the import above carries `with context`: a macro imported without it cannot see
|
|
|
1338
1480
|
| `date(format, zone)` | Formats a `Date` or a date string. `readable` (the default) gives `2 September 2026`, `html` gives `2026-09-02` for a `<time datetime>`, `year` gives `2026`, `month` gives `September 2026`, `iso` gives the full ISO 8601 instant. A date in a file is a UTC instant; every format but `iso` is rendered in the site's `timezone` setting, and `iso` stays the instant. Pass `zone` — an IANA name — to override the setting for one call. A value that is not a date renders as the empty string. |
|
|
1339
1481
|
| `url` | Prefixes a root-relative path with the base URL's path, so a site served from a subdirectory links correctly. Eleventy's filter of the same name. |
|
|
1340
1482
|
| `absoluteUrl` | The same path as a fully qualified URL against the site's `baseUrl`. |
|
|
1483
|
+
| `asset` | The URL of a file in the theme's `static/` directory, with the base path in front and a hash of the file's bytes on the end: `{{ "style.css" \| asset }}` gives `/theme/style.css?v=3f2a9c01b7d4`. The file is found the way `/theme/` finds it, the chosen theme first. A hashed URL is served with `Cache-Control: public, max-age=31536000, immutable`, and editing the file changes the hash on the next render. A file no theme has gets its plain `/theme/` URL. |
|
|
1341
1484
|
|
|
1342
1485
|
`date` reads one word rather than parsing it: `{{ "now" | date("year") }}` is
|
|
1343
1486
|
the year at the moment the page is rendered, in the site's own timezone. It is
|
|
@@ -13,14 +13,19 @@
|
|
|
13
13
|
off an account. The search form is on the search page and nowhere else.
|
|
14
14
|
|
|
15
15
|
Blocks a site can override without copying the whole file:
|
|
16
|
-
title
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
title the contents of <title>
|
|
17
|
+
speculationRules the <script type="speculationrules"> that prefetches the
|
|
18
|
+
site's own pages; empty it to prefetch nothing
|
|
19
|
+
viewTransitions the <style> that opts into cross-document view
|
|
20
|
+
transitions; empty it to turn them off
|
|
21
|
+
head the cards, the icons and the structured data, at the end
|
|
22
|
+
of <head>. Call `super()` from an override that only
|
|
23
|
+
adds to it.
|
|
24
|
+
alternates <link rel="alternate"> tags, where the feeds go
|
|
25
|
+
header the site header, with the site menu
|
|
26
|
+
content the page body
|
|
27
|
+
footer the site footer
|
|
28
|
+
scripts scripts at the end of <body>
|
|
24
29
|
#}{% import "partials/feeds.njk" as feeds %}
|
|
25
30
|
{%- import "partials/menu.njk" as nav %}
|
|
26
31
|
{#- Whether this is the front page, which is the one page the header and the
|
|
@@ -55,7 +60,43 @@
|
|
|
55
60
|
<meta name="description" content="{{ metaDescription }}">
|
|
56
61
|
{% endif %}
|
|
57
62
|
<link rel="canonical" href="{{ (page.url or "/") | absoluteUrl }}">
|
|
58
|
-
<link rel="stylesheet" href="{{ "
|
|
63
|
+
<link rel="stylesheet" href="{{ "style.css" | asset }}">
|
|
64
|
+
{% block speculationRules %}
|
|
65
|
+
{#
|
|
66
|
+
Prefetch the page behind a link the reader is about to follow: a hover
|
|
67
|
+
or a press on one of this site's own pages (TASK-140). Not the admin,
|
|
68
|
+
logout, a feed, an upload or a URL with a query, which are not pages to
|
|
69
|
+
read or not safe to fetch unasked. Every pattern goes through `url`, so
|
|
70
|
+
a site in a subdirectory prefetches only its own pages. The CMS drops
|
|
71
|
+
this script from a page drawn for somebody signed in.
|
|
72
|
+
#}
|
|
73
|
+
{% set speculationRules = { "prefetch": [{
|
|
74
|
+
"source": "document",
|
|
75
|
+
"eagerness": "moderate",
|
|
76
|
+
"where": { "and": [
|
|
77
|
+
{ "href_matches": "/*" | url },
|
|
78
|
+
{ "not": { "href_matches": "/admin" | url } },
|
|
79
|
+
{ "not": { "href_matches": "/admin/*" | url } },
|
|
80
|
+
{ "not": { "href_matches": "/*logout*" | url } },
|
|
81
|
+
{ "not": { "href_matches": "/feed/*" | url } },
|
|
82
|
+
{ "not": { "href_matches": "/*/feed/*" | url } },
|
|
83
|
+
{ "not": { "href_matches": "/uploads/*" | url } },
|
|
84
|
+
{ "not": { "href_matches": "/*\\?(.+)" | url } }
|
|
85
|
+
] }
|
|
86
|
+
}] } %}
|
|
87
|
+
<script type="speculationrules">{{ speculationRules | dump | safe }}</script>
|
|
88
|
+
{% endblock %}
|
|
89
|
+
{% block viewTransitions %}
|
|
90
|
+
{#
|
|
91
|
+
Cross-document view transitions: the default cross-fade between two of
|
|
92
|
+
this site's pages, and none for a reader who asks for reduced motion.
|
|
93
|
+
#}
|
|
94
|
+
<style>
|
|
95
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
96
|
+
@view-transition { navigation: auto; }
|
|
97
|
+
}
|
|
98
|
+
</style>
|
|
99
|
+
{% endblock %}
|
|
59
100
|
{% block alternates %}
|
|
60
101
|
{{ feeds.feedLinks("/", site.title) }}
|
|
61
102
|
{{ feeds.commentsFeedLink("/comments/feed/", site.title + " comments") }}
|
|
@@ -184,7 +225,7 @@
|
|
|
184
225
|
See the theme README.
|
|
185
226
|
#}
|
|
186
227
|
{% if content and "language-" in content %}
|
|
187
|
-
<script src="{{ "
|
|
228
|
+
<script src="{{ "highlight.js" | asset }}" defer></script>
|
|
188
229
|
{% endif %}
|
|
189
230
|
{% endblock %}
|
|
190
231
|
</body>
|