@geekity/cms 0.14.0 → 0.15.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.
Files changed (120) hide show
  1. package/README.md +163 -52
  2. package/admin/pages/documents/editor.njk +69 -0
  3. package/admin/pages/media/library.njk +4 -3
  4. package/admin/pages/settings/general.njk +44 -0
  5. package/admin/static/admin.css +33 -0
  6. package/dist/admin/documents.d.ts +3 -0
  7. package/dist/admin/documents.d.ts.map +1 -1
  8. package/dist/admin/documents.js +38 -5
  9. package/dist/admin/documents.js.map +1 -1
  10. package/dist/admin/enclosure-field.d.ts +91 -0
  11. package/dist/admin/enclosure-field.d.ts.map +1 -0
  12. package/dist/admin/enclosure-field.js +282 -0
  13. package/dist/admin/enclosure-field.js.map +1 -0
  14. package/dist/admin/media.d.ts +7 -2
  15. package/dist/admin/media.d.ts.map +1 -1
  16. package/dist/admin/media.js +4 -2
  17. package/dist/admin/media.js.map +1 -1
  18. package/dist/admin/settings-general.d.ts.map +1 -1
  19. package/dist/admin/settings-general.js +36 -2
  20. package/dist/admin/settings-general.js.map +1 -1
  21. package/dist/admin/settings-page.d.ts.map +1 -1
  22. package/dist/admin/settings-page.js +1 -2
  23. package/dist/admin/settings-page.js.map +1 -1
  24. package/dist/admin/settings.d.ts +26 -0
  25. package/dist/admin/settings.d.ts.map +1 -1
  26. package/dist/admin/settings.js +88 -2
  27. package/dist/admin/settings.js.map +1 -1
  28. package/dist/admin/uploads.d.ts +7 -2
  29. package/dist/admin/uploads.d.ts.map +1 -1
  30. package/dist/admin/uploads.js +23 -5
  31. package/dist/admin/uploads.js.map +1 -1
  32. package/dist/config.d.ts +17 -3
  33. package/dist/config.d.ts.map +1 -1
  34. package/dist/config.js +11 -7
  35. package/dist/config.js.map +1 -1
  36. package/dist/content/enclosure.d.ts +69 -0
  37. package/dist/content/enclosure.d.ts.map +1 -0
  38. package/dist/content/enclosure.js +165 -0
  39. package/dist/content/enclosure.js.map +1 -0
  40. package/dist/content/index.d.ts +3 -1
  41. package/dist/content/index.d.ts.map +1 -1
  42. package/dist/content/index.js +1 -0
  43. package/dist/content/index.js.map +1 -1
  44. package/dist/content/media.d.ts +15 -2
  45. package/dist/content/media.d.ts.map +1 -1
  46. package/dist/content/media.js +92 -9
  47. package/dist/content/media.js.map +1 -1
  48. package/dist/federation/article.d.ts.map +1 -1
  49. package/dist/federation/article.js +35 -6
  50. package/dist/federation/article.js.map +1 -1
  51. package/dist/images/icons.d.ts +5 -0
  52. package/dist/images/icons.d.ts.map +1 -1
  53. package/dist/images/icons.js +13 -8
  54. package/dist/images/icons.js.map +1 -1
  55. package/dist/images/paths.d.ts +1 -1
  56. package/dist/images/paths.d.ts.map +1 -1
  57. package/dist/images/paths.js.map +1 -1
  58. package/dist/index.d.ts +5 -5
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +3 -3
  61. package/dist/index.js.map +1 -1
  62. package/dist/web/assets.d.ts +14 -0
  63. package/dist/web/assets.d.ts.map +1 -1
  64. package/dist/web/assets.js +56 -1
  65. package/dist/web/assets.js.map +1 -1
  66. package/dist/web/context.d.ts +26 -0
  67. package/dist/web/context.d.ts.map +1 -1
  68. package/dist/web/context.js +15 -0
  69. package/dist/web/context.js.map +1 -1
  70. package/dist/web/feed-atom.d.ts.map +1 -1
  71. package/dist/web/feed-atom.js +20 -0
  72. package/dist/web/feed-atom.js.map +1 -1
  73. package/dist/web/feed-item.d.ts +19 -0
  74. package/dist/web/feed-item.d.ts.map +1 -1
  75. package/dist/web/feed-item.js +22 -0
  76. package/dist/web/feed-item.js.map +1 -1
  77. package/dist/web/feed-json.d.ts +13 -0
  78. package/dist/web/feed-json.d.ts.map +1 -1
  79. package/dist/web/feed-json.js +15 -0
  80. package/dist/web/feed-json.js.map +1 -1
  81. package/dist/web/feed-rss.d.ts.map +1 -1
  82. package/dist/web/feed-rss.js +59 -2
  83. package/dist/web/feed-rss.js.map +1 -1
  84. package/dist/web/feed-xml.d.ts +7 -1
  85. package/dist/web/feed-xml.d.ts.map +1 -1
  86. package/dist/web/feed-xml.js +8 -2
  87. package/dist/web/feed-xml.js.map +1 -1
  88. package/dist/web/feeds.d.ts +1 -1
  89. package/dist/web/feeds.d.ts.map +1 -1
  90. package/dist/web/feeds.js +6 -0
  91. package/dist/web/feeds.js.map +1 -1
  92. package/dist/web/index.d.ts +4 -1
  93. package/dist/web/index.d.ts.map +1 -1
  94. package/dist/web/index.js +2 -0
  95. package/dist/web/index.js.map +1 -1
  96. package/dist/web/license.d.ts +94 -0
  97. package/dist/web/license.d.ts.map +1 -0
  98. package/dist/web/license.js +116 -0
  99. package/dist/web/license.js.map +1 -0
  100. package/dist/web/oembed.d.ts.map +1 -1
  101. package/dist/web/oembed.js +1 -2
  102. package/dist/web/oembed.js.map +1 -1
  103. package/dist/web/render.d.ts.map +1 -1
  104. package/dist/web/render.js +11 -2
  105. package/dist/web/render.js.map +1 -1
  106. package/dist/web/routes.d.ts.map +1 -1
  107. package/dist/web/routes.js +8 -4
  108. package/dist/web/routes.js.map +1 -1
  109. package/dist/web/share-image.d.ts +7 -7
  110. package/dist/web/share-image.d.ts.map +1 -1
  111. package/dist/web/share-image.js +8 -7
  112. package/dist/web/share-image.js.map +1 -1
  113. package/package.json +1 -1
  114. package/themes/default/README.md +81 -28
  115. package/themes/default/layouts/base.njk +3 -4
  116. package/themes/default/layouts/post.njk +4 -0
  117. package/themes/default/partials/jsonld.njk +4 -6
  118. package/themes/default/partials/recording.njk +41 -0
  119. package/themes/default/src/style.css +16 -0
  120. package/themes/default/static/style.css +25 -0
package/README.md CHANGED
@@ -1671,53 +1671,53 @@ that ship inside the package, deliberately outside the theme search path: a
1671
1671
  site's theme may override any public template, and must not be able to
1672
1672
  shadow the login form.
1673
1673
 
1674
- | Route | What it does |
1675
- | ------------------------------------------------ | ----------------------------------------------------------------------------------- |
1676
- | `/admin` | The dashboard: counts, the five most recent posts, the follower count. |
1677
- | `/admin/posts`, `/admin/pages` | The listings and the editors. |
1678
- | `/admin/tags`, `/admin/categories` | Every term in use, with rename, merge and delete. |
1679
- | `/admin/tags/rename`, `/admin/categories/rename` | `POST` only. Renames a term, or merges it into one that exists. |
1680
- | `/admin/tags/delete`, `/admin/categories/delete` | `POST` only. Takes a term out of every file. |
1681
- | `/admin/navigation` | Navigation > Menus: every menu the site holds. `POST` saves one menu's items. |
1682
- | `/admin/navigation/add` | `POST` only. Adds an empty menu under the name the form gives. |
1683
- | `/admin/navigation/delete` | `POST` only. Deletes one menu the active theme renders nowhere. |
1684
- | `/admin/media` | Everything under `content/uploads`, with the URL, the Markdown and what uses it. |
1685
- | `/admin/media/upload` | `POST` only. Stores one file by the rules the editor's upload enforces. |
1686
- | `/admin/media/delete` | `POST` only. Deletes one upload, asking first when a document points at it. |
1687
- | `/admin/media/alt` | `POST` only. Sets a picture's alt text, or marks it decorative, in `media.json`. |
1688
- | `/admin/comments` | Pending, approved and spam, with approve, spam, delete and reply. |
1689
- | `/admin/comments/moderate` | `POST` only. Approves one comment, files it as spam, or deletes it. |
1690
- | `/admin/comments/reply` | `POST` only. Posts an approved reply under the comment it answers. |
1691
- | `/admin/messages` | The contact form's inbox, with a Spam list beside it. |
1692
- | `/admin/messages/read` | `POST` only. Marks one message read, or unread again. |
1693
- | `/admin/messages/delete` | `POST` only. Deletes one message, and its file with it. |
1694
- | `/admin/appearance/themes` | Appearance > Themes: the themes on disk. `POST` activates the one named. |
1695
- | `/admin/tools` | Tools > Content index: what the index holds, and the button that rebuilds it. |
1696
- | `/admin/tools/rebuild-index` | `POST` only. Offers the rebuild, then reads every file again on the live site. |
1697
- | `/admin/settings` | Settings > General: title, tagline, author, base URL, time zone, language, locale. |
1698
- | `/admin/settings/reading` | What the homepage displays, posts per page, the notify server. |
1699
- | `/admin/settings/permalinks` | The tag and category bases, and the archive redirects already recorded. |
1700
- | `/admin/settings/discussion` | Comments and the closing window, webmentions, and the Akismet key. |
1701
- | `/admin/settings/akismet` | `POST` only. Saves the Akismet key, or forgets it. |
1702
- | `/admin/settings/email` | The mail provider, the From line, the reply-to and the contact address. |
1703
- | `/admin/settings/mail` | `POST` only. Saves a mail credential, or forgets every one of them. |
1704
- | `/admin/settings/mail/test` | `POST` only. Sends the theme's test message through the whole chain. |
1705
- | `/admin/users` | Who may sign in: a row each, with Edit and Delete. Nothing on it edits anybody. |
1706
- | `/admin/users/new` | Users > Add new: the add form. `POST` adds one. |
1707
- | `/admin/users/<id>` | One user: the account, the profile, the notices, your password, and the delete. |
1708
- | `/admin/users/password` | `POST` only. Changes the signed-in admin's own password. |
1709
- | `/admin/users/email` | `POST` only. Sets or clears the email address on the user the form names. |
1710
- | `/admin/users/profile` | `POST` only. Saves the whole public profile of the user the form names. |
1711
- | `/admin/users/notifications` | `POST` only. Turns one notice on or off for the user the form names. |
1712
- | `/admin/users/notifications/mode` | `POST` only. Sets how often that notice arrives: as they arrive, hourly or daily. |
1713
- | `/admin/users/delete` | `POST` only. Deletes the user the form names. |
1714
- | `/admin/federation` | The actors, their followers, the inbox log, and per-post delivery. |
1715
- | `/admin/federation/settings` | Federation > Settings: the relays the site subscribes to, and the WordPress switch. |
1716
- | `/admin/federation/resend` | `POST` only. Sends one post to the followers again, as its file now reads. |
1717
- | `/admin/setup` | First run: creates the first admin. Closed once a user exists. |
1718
- | `/admin/login` | Username and password. |
1719
- | `/admin/logout` | `POST` only. Deletes the session row and sends `Clear-Site-Data`. |
1720
- | `/admin/_static/*` | The admin's own stylesheet and scripts, cached for an hour. |
1674
+ | Route | What it does |
1675
+ | ------------------------------------------------ | --------------------------------------------------------------------------------------------- |
1676
+ | `/admin` | The dashboard: counts, the five most recent posts, the follower count. |
1677
+ | `/admin/posts`, `/admin/pages` | The listings and the editors. |
1678
+ | `/admin/tags`, `/admin/categories` | Every term in use, with rename, merge and delete. |
1679
+ | `/admin/tags/rename`, `/admin/categories/rename` | `POST` only. Renames a term, or merges it into one that exists. |
1680
+ | `/admin/tags/delete`, `/admin/categories/delete` | `POST` only. Takes a term out of every file. |
1681
+ | `/admin/navigation` | Navigation > Menus: every menu the site holds. `POST` saves one menu's items. |
1682
+ | `/admin/navigation/add` | `POST` only. Adds an empty menu under the name the form gives. |
1683
+ | `/admin/navigation/delete` | `POST` only. Deletes one menu the active theme renders nowhere. |
1684
+ | `/admin/media` | Everything under `content/uploads`, with the URL, the Markdown and what uses it. |
1685
+ | `/admin/media/upload` | `POST` only. Stores one file by the rules the editor's upload enforces. |
1686
+ | `/admin/media/delete` | `POST` only. Deletes one upload, asking first when a document points at it. |
1687
+ | `/admin/media/alt` | `POST` only. Sets a picture's alt text, or marks it decorative, in `media.json`. |
1688
+ | `/admin/comments` | Pending, approved and spam, with approve, spam, delete and reply. |
1689
+ | `/admin/comments/moderate` | `POST` only. Approves one comment, files it as spam, or deletes it. |
1690
+ | `/admin/comments/reply` | `POST` only. Posts an approved reply under the comment it answers. |
1691
+ | `/admin/messages` | The contact form's inbox, with a Spam list beside it. |
1692
+ | `/admin/messages/read` | `POST` only. Marks one message read, or unread again. |
1693
+ | `/admin/messages/delete` | `POST` only. Deletes one message, and its file with it. |
1694
+ | `/admin/appearance/themes` | Appearance > Themes: the themes on disk. `POST` activates the one named. |
1695
+ | `/admin/tools` | Tools > Content index: what the index holds, and the button that rebuilds it. |
1696
+ | `/admin/tools/rebuild-index` | `POST` only. Offers the rebuild, then reads every file again on the live site. |
1697
+ | `/admin/settings` | Settings > General: title, tagline, author, base URL, time zone, language, locale, site icon. |
1698
+ | `/admin/settings/reading` | What the homepage displays, posts per page, the notify server. |
1699
+ | `/admin/settings/permalinks` | The tag and category bases, and the archive redirects already recorded. |
1700
+ | `/admin/settings/discussion` | Comments and the closing window, webmentions, and the Akismet key. |
1701
+ | `/admin/settings/akismet` | `POST` only. Saves the Akismet key, or forgets it. |
1702
+ | `/admin/settings/email` | The mail provider, the From line, the reply-to and the contact address. |
1703
+ | `/admin/settings/mail` | `POST` only. Saves a mail credential, or forgets every one of them. |
1704
+ | `/admin/settings/mail/test` | `POST` only. Sends the theme's test message through the whole chain. |
1705
+ | `/admin/users` | Who may sign in: a row each, with Edit and Delete. Nothing on it edits anybody. |
1706
+ | `/admin/users/new` | Users > Add new: the add form. `POST` adds one. |
1707
+ | `/admin/users/<id>` | One user: the account, the profile, the notices, your password, and the delete. |
1708
+ | `/admin/users/password` | `POST` only. Changes the signed-in admin's own password. |
1709
+ | `/admin/users/email` | `POST` only. Sets or clears the email address on the user the form names. |
1710
+ | `/admin/users/profile` | `POST` only. Saves the whole public profile of the user the form names. |
1711
+ | `/admin/users/notifications` | `POST` only. Turns one notice on or off for the user the form names. |
1712
+ | `/admin/users/notifications/mode` | `POST` only. Sets how often that notice arrives: as they arrive, hourly or daily. |
1713
+ | `/admin/users/delete` | `POST` only. Deletes the user the form names. |
1714
+ | `/admin/federation` | The actors, their followers, the inbox log, and per-post delivery. |
1715
+ | `/admin/federation/settings` | Federation > Settings: the relays the site subscribes to, and the WordPress switch. |
1716
+ | `/admin/federation/resend` | `POST` only. Sends one post to the followers again, as its file now reads. |
1717
+ | `/admin/setup` | First run: creates the first admin. Closed once a user exists. |
1718
+ | `/admin/login` | Username and password. |
1719
+ | `/admin/logout` | `POST` only. Deletes the session row and sends `Clear-Site-Data`. |
1720
+ | `/admin/_static/*` | The admin's own stylesheet and scripts, cached for an hour. |
1721
1721
 
1722
1722
  The screens behind the login share one layout: a bar across the top with the
1723
1723
  site name and a link to the public site, the menu down the left, and a place for
@@ -1846,7 +1846,8 @@ at once cannot each keep half of what the other kept.
1846
1846
 
1847
1847
  The file carries `title`, `tagline`, `url`, `author` (a username, when the
1848
1848
  site has one author), `postsPerPage`, `homepage`, `postsPage`,
1849
- `timezone`, `language`, `locale` (when it is set), `tagBase`,
1849
+ `timezone`, `language`, `locale` (when it is set), `icon` (when it is set),
1850
+ `license` and `licenseName` (when a license is chosen; see below), `tagBase`,
1850
1851
  `categoryBase`, `notifyServer`, `feedUpdatePeriod`, `feedUpdateFrequency`,
1851
1852
  `webmentionsSend`, `webmentionsReceive`,
1852
1853
  `mailProvider`, `mailFromName`, `mailFromAddress`, `mailReplyTo`,
@@ -1999,7 +2000,7 @@ would have done anyway.
1999
2000
  | `removeImageVariants` | Take a source's derived directory away. |
2000
2001
  | `responsiveImages(html, describe)` | Rewrite `<img src="/uploads/…">` as `<picture>`. Pure: hand it any lookup. |
2001
2002
  | `siteImageMarkup(config, html)` | The same, over one site's own records, deriving in the background for a record miss. |
2002
- | `siteIcons(config, avatar)` | The head's icon links for a site's avatar: `{ rel, sizes, href }`, or none at all. |
2003
+ | `siteIcons(config, setting)` | The head's icon links for a site's icon setting: `{ rel, sizes, href }`, or none. |
2003
2004
 
2004
2005
  `describeImage` is synchronous and cached because it is called once per image
2005
2006
  while a page renders, and decision-10 forbids probing an image file at render
@@ -2009,10 +2010,11 @@ markup that asks for them back, and the first request for each one rebuilds it.
2009
2010
 
2010
2011
  The site's icons are derived copies of the same kind, and `siteIcons` is how a
2011
2012
  head gets at them: three square PNGs, `icon` at 32 and 16 pixels and
2012
- `apple-touch-icon` at 180, cropped from the middle of the site's avatar and
2013
+ `apple-touch-icon` at 180, cropped from the middle of the site's icon (the
2014
+ `icon` that Settings > General writes, else a hand-set `avatar`) and
2013
2015
  encoded the first time a browser asks for one. It answers an empty list — and
2014
- the packaged theme then links no icon at all — when the site has no avatar, when
2015
- its avatar is a file no icon can be made of, or when `imageOptimization` is off,
2016
+ the packaged theme then links no icon at all — when the site has no icon, when
2017
+ its icon is a file no icon can be made of, or when `imageOptimization` is off,
2016
2018
  so a head never advertises a URL this server would answer 404 for.
2017
2019
 
2018
2020
  `documentContext(document, images)` is where the rewrite is applied. Passing the
@@ -2869,8 +2871,22 @@ to read them on, `wfw:commentRss` (the [Well-Formed Web][wfw-ns] comment API) is
2869
2871
  the feed to poll, and `source:comments` is that same feed with a `count`
2870
2872
  attribute, so a reader can say "3 comments" without fetching anything.
2871
2873
 
2874
+ When the site has a license (Settings > General, or `license` in `site.json`),
2875
+ the channel carries a `creativeCommons:license` holding its URL, and every item
2876
+ carries one for its own license: the post's `license` front matter, else the
2877
+ site's. A post whose front matter says `license: none` carries none. The
2878
+ element is the [Creative Commons RSS module][cc-rss], declared as
2879
+ `xmlns:creativeCommons` only when some license is printed. It names a license
2880
+ by URL, Creative Commons or not, which a reader can act on; `dc:rights` would be
2881
+ free text. Nothing about a license is printed on a site and posts without one.
2882
+ RSS has no element that says "no license", so a post saying `license: none`
2883
+ on a licensed site carries no item license while the channel still names the
2884
+ site's; a reader that applies the channel's license to every item reads that
2885
+ post as under it.
2886
+
2872
2887
  [source-ns]: https://source.scripting.com/
2873
2888
  [wfw-ns]: http://wellformedweb.org/CommentAPI/
2889
+ [cc-rss]: https://cyber.harvard.edu/rss/creativeCommonsRssModule.html
2874
2890
 
2875
2891
  ### Comments
2876
2892
 
@@ -2925,6 +2941,14 @@ post as `content type="html"`. The feed itself carries `id`, `title`,
2925
2941
  `xml:lang` from the `language` setting, and — when the site names a notify
2926
2942
  server — a `source:cloud` and a `link rel="hub"`.
2927
2943
 
2944
+ With a license, the feed carries an [RFC 4946][rfc4946] `link rel="license"`
2945
+ to the site's license, its name as the `title`, and every entry carries one for
2946
+ its own license, the post's `license` front matter else the site's. An entry
2947
+ states its own because RFC 4946 does not let an entry inherit the feed's. JSON
2948
+ Feed has no field for a license and says nothing about one.
2949
+
2950
+ [rfc4946]: https://www.rfc-editor.org/rfc/rfc4946
2951
+
2928
2952
  A JSON Feed item carries `id` (the object id), `url` (the permalink), `title`,
2929
2953
  `content_html`, `summary`, `date_published`, `date_modified`, `tags` (the
2930
2954
  terms) and `authors`; the feed carries `version`, `title`, `home_page_url`,
@@ -2935,6 +2959,93 @@ The XML is written by this package rather than by a library. Text is escaped;
2935
2959
  `content:encoded` and `source:markdown` are CDATA sections, with any `]]>` in
2936
2960
  the text split across two sections so it cannot end one early.
2937
2961
 
2962
+ ### A recording on a post
2963
+
2964
+ A post can carry one recording: a spoken version of an article, or an episode
2965
+ now and then. The site is a blog that sometimes has audio, not a podcast host,
2966
+ so the feeds carry what a podcast app needs to play, describe and caption an
2967
+ item, and leave the podcast directory tags out. The front matter spells it the
2968
+ way RSS does:
2969
+
2970
+ ```yaml
2971
+ enclosure:
2972
+ url: /uploads/2026/10/episode-12.mp3 # an upload; written by the editor
2973
+ type: audio/mpeg # from the upload table; written by the editor
2974
+ length: 23456789 # bytes, from the file on disk; written by the editor
2975
+ duration: 1834 # seconds, optional
2976
+ transcript: # optional
2977
+ url: /uploads/2026/10/episode-12.vtt # an upload or an http(s) URL
2978
+ type: text/vtt # text/vtt, application/x-subrip, text/html or text/plain
2979
+ alternates: # optional, any number
2980
+ - url: /uploads/2026/10/episode-12.mp4
2981
+ type: video/mp4
2982
+ length: 98765432
2983
+ title: Video # optional, at most 32 characters
2984
+ height: 720 # optional, for video
2985
+ lang: en # optional
2986
+ - url: https://cdn.example.com/episode-12-low.mp3
2987
+ type: audio/mpeg # required for a link
2988
+ title: Low bandwidth # a link's length is optional
2989
+ ```
2990
+
2991
+ **The main file is always an upload** under `/uploads/`, because RSS requires
2992
+ its `url`, `length` and `type`, and an upload is the only file whose length the
2993
+ site knows. A main file without all three is no recording at all. A bad
2994
+ alternate version or transcript is dropped on its own, so one wrong line in a
2995
+ hand-edited file costs that line and not the episode. `enclosureOf(extra)` is
2996
+ the one reading of the key, and the feeds, the theme and the federation read
2997
+ only what it returns.
2998
+
2999
+ **The post editor** has a Recording section on posts. The main file is a select
3000
+ of the audio and video in the media library, and None removes the whole key,
3001
+ transcript and alternate versions included. The duration takes seconds, `30:34`
3002
+ or `1:02:03`. The transcript is an upload (`.vtt`, `.srt` or `.txt`) or an
3003
+ `https` address with its type. Alternate versions are numbered rows with one
3004
+ blank row at the end, so a plain form can add, edit and remove them; emptying a
3005
+ row's address removes it. On save, the type and length of every upload are read
3006
+ from the upload table and from the file on disk, never from the form. A main
3007
+ file that is not an audio or video upload, a linked version without an `http`
3008
+ or `https` address or a media type, or a title over 32 characters is refused
3009
+ with a message, and nothing is written.
3010
+
3011
+ **What the feeds print, and when.** A post with no recording prints none of
3012
+ this, and a feed with no recording in it declares neither namespace, so its
3013
+ bytes and its ETag are what they were before recordings existed.
3014
+
3015
+ | Element | Format | Printed when |
3016
+ | ----------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
3017
+ | `<enclosure url length type/>` | RSS item | The post has a main file. `url` is absolute. |
3018
+ | `<podcast:alternateEnclosure>` with `<podcast:source uri/>` | RSS item | Once per alternate version, with `type`, and `length`, `title`, `height` and `lang` when known. |
3019
+ | `<podcast:transcript url type/>` | RSS item | The post has a transcript, with `rel="captions"` for WebVTT or SubRip. |
3020
+ | `<itunes:duration>` | RSS item | The duration is known, in whole seconds. |
3021
+ | `<podcast:medium>blog</podcast:medium>` | RSS channel | Any item in the feed has a recording. Without it an app assumes `podcast`; `blog` is the spec's word for articles that sometimes have audio. |
3022
+ | `<link rel="enclosure" type length href/>` | Atom entry | The post has a main file. Atom has no way to say the others are versions of it. |
3023
+ | `attachments` | JSON Feed | The main file and then each alternate version, with `url`, `mime_type`, `size_in_bytes` and `title` when known, and `duration_in_seconds` on audio or video when the duration is known. |
3024
+
3025
+ `xmlns:podcast="https://podcastindex.org/namespace/1.0"` is declared only on a
3026
+ feed that prints a podcast element, and
3027
+ `xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"` only on one that
3028
+ prints `itunes:duration`.
3029
+
3030
+ **Left out on purpose:** the Apple directory tags (`itunes:image`,
3031
+ `itunes:category`, `itunes:explicit`, `itunes:owner`), and `podcast:guid`,
3032
+ `podcast:locked`, `podcast:person`, `podcast:season`, `podcast:episode`,
3033
+ `podcast:value` and `podcast:chapters`. Each one matters only to a podcast
3034
+ directory or a dedicated show. Adding them is a follow-up for a site that wants
3035
+ to be listed in Apple Podcasts, and so is `podcast:license`, which could reuse
3036
+ the [content license](#rss-20).
3037
+
3038
+ **The fediverse** gets the main file as an `Audio` or `Video` attachment with
3039
+ its absolute `url`, its `mediaType`, and the post's title, or a note's first
3040
+ words, as its `name`. It goes before the post's `Image` attachments, because
3041
+ Mastodon shows a player or a gallery by the type of the first attachment and
3042
+ keeps only four. Alternate versions and linked files stay out: a remote server
3043
+ fetches and re-encodes what it is given, and a link to another host is a type
3044
+ this site cannot check. `playsAsVideo(type)` decides `Video` against `Audio`,
3045
+ and the default theme's `<video>` against `<audio>`, so the two never disagree.
3046
+
3047
+ The default theme plays the main file on the post page; see its README.
3048
+
2938
3049
  ### Real-time notification
2939
3050
 
2940
3051
  A feed reader that polls hears about a post when it next polls. An [rssCloud][]
@@ -62,6 +62,75 @@
62
62
  formnovalidate
63
63
  >Preview</button>
64
64
  </div>
65
+
66
+ {% if kind.type == "post" %}
67
+ <fieldset class="admin-editor-enclosure">
68
+ <legend>Recording</legend>
69
+
70
+ <label for="editor-enclosure-url">Audio or video file</label>
71
+ <select id="editor-enclosure-url" name="{{ enclosureFields.url }}" aria-describedby="editor-enclosure-url-hint">
72
+ <option value=""{% if form.enclosure.url == "" %} selected{% endif %}>None</option>
73
+ {% for choice in enclosureChoices.media %}
74
+ <option value="{{ choice.url }}"{% if choice.url == form.enclosure.url %} selected{% endif %}>{{ choice.label }}</option>
75
+ {% endfor %}
76
+ </select>
77
+ <p class="admin-hint" id="editor-enclosure-url-hint">
78
+ Upload the file to the media library first. Feeds carry it as the post’s enclosure and the
79
+ post page plays it. None removes the recording, its transcript and its other versions.
80
+ </p>
81
+
82
+ <label for="editor-enclosure-duration">Duration</label>
83
+ <input id="editor-enclosure-duration" name="{{ enclosureFields.duration }}" type="text" inputmode="numeric" value="{{ form.enclosure.duration }}" autocomplete="off" aria-describedby="editor-enclosure-duration-hint" />
84
+ <p class="admin-hint" id="editor-enclosure-duration-hint">Seconds, or 30:34, or 1:02:03. Leave it empty if you do not know.</p>
85
+
86
+ <label for="editor-enclosure-transcript">Transcript or captions</label>
87
+ <input id="editor-enclosure-transcript" name="{{ enclosureFields.transcriptUrl }}" type="text" value="{{ form.enclosure.transcriptUrl }}" list="editor-transcript-uploads" autocomplete="off" aria-describedby="editor-enclosure-transcript-hint" />
88
+ <datalist id="editor-transcript-uploads">
89
+ {% for url in enclosureChoices.transcripts %}
90
+ <option value="{{ url }}"></option>
91
+ {% endfor %}
92
+ </datalist>
93
+ <label for="editor-enclosure-transcript-type">Transcript type</label>
94
+ <select id="editor-enclosure-transcript-type" name="{{ enclosureFields.transcriptType }}" aria-describedby="editor-enclosure-transcript-hint">
95
+ <option value=""{% if form.enclosure.transcriptType == "" %} selected{% endif %}>From the file</option>
96
+ {% for type in transcriptTypes %}
97
+ <option value="{{ type }}"{% if type == form.enclosure.transcriptType %} selected{% endif %}>{{ type }}</option>
98
+ {% endfor %}
99
+ </select>
100
+ <p class="admin-hint" id="editor-enclosure-transcript-hint">
101
+ A .vtt, .srt or .txt file in the media library, or an address starting https://. A linked
102
+ transcript needs its type.
103
+ </p>
104
+
105
+ <h2 class="admin-editor-enclosure-heading">Other versions</h2>
106
+ <p class="admin-hint">
107
+ A video of an audio episode, a smaller file, another language. Each is a file in the media
108
+ library or an address starting https://, which needs its type. Empty an address to remove
109
+ that version.
110
+ </p>
111
+ {% for row in alternateRows %}
112
+ {% set n = loop.index0 %}
113
+ <fieldset class="admin-editor-alternate">
114
+ <legend>{% if row.url %}Version {{ loop.index }}{% else %}Add a version{% endif %}</legend>
115
+ <label for="editor-alternate-url-{{ n }}">Address</label>
116
+ <input id="editor-alternate-url-{{ n }}" name="{{ enclosureFields.alternate.url }}{{ n }}" type="text" value="{{ row.url }}" list="editor-media-uploads" autocomplete="off" />
117
+ <label for="editor-alternate-type-{{ n }}">Type</label>
118
+ <input id="editor-alternate-type-{{ n }}" name="{{ enclosureFields.alternate.type }}{{ n }}" type="text" value="{{ row.type }}" placeholder="video/mp4" autocomplete="off" spellcheck="false" />
119
+ <label for="editor-alternate-title-{{ n }}">Title</label>
120
+ <input id="editor-alternate-title-{{ n }}" name="{{ enclosureFields.alternate.title }}{{ n }}" type="text" value="{{ row.title }}" maxlength="32" />
121
+ <label for="editor-alternate-height-{{ n }}">Height</label>
122
+ <input id="editor-alternate-height-{{ n }}" name="{{ enclosureFields.alternate.height }}{{ n }}" type="text" inputmode="numeric" value="{{ row.height }}" autocomplete="off" />
123
+ <label for="editor-alternate-lang-{{ n }}">Language</label>
124
+ <input id="editor-alternate-lang-{{ n }}" name="{{ enclosureFields.alternate.lang }}{{ n }}" type="text" value="{{ row.lang }}" autocomplete="off" spellcheck="false" />
125
+ </fieldset>
126
+ {% endfor %}
127
+ <datalist id="editor-media-uploads">
128
+ {% for choice in enclosureChoices.media %}
129
+ <option value="{{ choice.url }}"></option>
130
+ {% endfor %}
131
+ </datalist>
132
+ </fieldset>
133
+ {% endif %}
65
134
  </div>
66
135
 
67
136
  <div class="admin-editor-side">
@@ -48,7 +48,8 @@
48
48
  <label for="media-file">Add a file</label>
49
49
  <input id="media-file" name="{{ fields.file }}" type="file" accept="{{ accepts }}" required />
50
50
  <p class="admin-hint">
51
- This site accepts {{ accepts | replace(',', ', ') }}, up to {{ maxBytes }} bytes. The same
51
+ This site accepts {{ accepts | replace(',', ', ') }}, up to {{ maxBytes }} bytes, or
52
+ {{ mediaMaxBytes }} bytes for audio and video. The same
52
53
  rules the editor’s upload control enforces.
53
54
  </p>
54
55
  <p class="admin-settings-buttons">
@@ -76,7 +77,7 @@
76
77
  {% for file in files %}
77
78
  <tr>
78
79
  <td class="admin-media-file">
79
- {% if file.image %}
80
+ {% if file.kind == 'image' %}
80
81
  <a href="{{ file.url }}">
81
82
  <img class="admin-media-thumbnail" src="{{ file.url }}" alt="" width="60" height="60" />
82
83
  </a>
@@ -86,7 +87,7 @@
86
87
  <a href="{{ file.url }}">{{ file.path }}</a>
87
88
  </td>
88
89
  <td class="admin-media-alt">
89
- {% if file.image %}
90
+ {% if file.kind == 'image' %}
90
91
  <form method="post" action="{{ altUrl }}">
91
92
  <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
92
93
  <input type="hidden" name="{{ fields.path }}" value="{{ file.path }}" />
@@ -8,6 +8,10 @@
8
8
  {{ field.problem('settings-timezone', problems.timezone) }}
9
9
  {{ field.problem('settings-language', problems.language) }}
10
10
  {{ field.problem('settings-locale', problems.locale) }}
11
+ {{ field.problem('settings-icon', problems.icon) }}
12
+ {{ field.problem('settings-license', problems.license) }}
13
+ {{ field.problem('settings-license-url', problems.licenseUrl) }}
14
+ {{ field.problem('settings-license-name', problems.licenseName) }}
11
15
  {% endblock %}
12
16
 
13
17
  {% block settingsFields %}
@@ -41,6 +45,46 @@
41
45
  {% set localeHint %}How the public site writes dates and counts, such as <code>en-US</code> for <code>September 2, 2026</code>. Leave it empty to follow the language.{% endset %}
42
46
  {{ field.text('settings-locale', fields.locale, 'Locale', form.locale,
43
47
  error=problems.locale, hint=localeHint) }}
48
+
49
+ {% set iconHint %}A square image at least 512 pixels wide, uploaded on <a href="{{ adminUrl }}/media">Media</a>: a path such as <code>/uploads/2026/10/icon.png</code>. It is the favicon, the home screen icon, <code>/favicon.ico</code> and the web app manifest's icon. Anything not square is cropped to its middle.{% endset %}
50
+ {{ field.text('settings-icon', fields.icon, 'Site icon', form.icon,
51
+ error=problems.icon, hint=iconHint) }}
52
+
53
+ <div class="admin-avatar-current">
54
+ {% if iconPreview %}
55
+ <img class="admin-icon-preview" src="{{ iconPreview }}" alt="The current site icon" width="96" height="96" />
56
+ {% else %}
57
+ <span class="admin-icon-preview admin-avatar-blank"></span>
58
+ {% endif %}
59
+ <p class="admin-hint">
60
+ {% if iconUnderived and not imageOptimization %}
61
+ Image optimization is off, so no icons are derived and the site links none.
62
+ {% elif iconUnderived %}
63
+ The site cannot derive icons from <code>{{ iconInEffect }}</code>, so it links none.
64
+ {% elif iconPreview and iconFromAvatar %}
65
+ The current icon, from the <code>avatar</code> in <code>site.json</code>. Saving this page writes it as the icon; clearing the field leaves the avatar in use.
66
+ {% elif iconPreview %}
67
+ The current icon, as a browser tab and a home screen show it.
68
+ {% else %}
69
+ The site has no icon yet, so browsers show their own.
70
+ {% endif %}
71
+ </p>
72
+ </div>
73
+
74
+ {% set licenseHint %}What readers may do with your posts. The footer links it, and the structured data and the feeds declare it. A post can name its own with <code>license</code> in its front matter.{% endset %}
75
+ {% call field.select('settings-license', fields.license, 'License', error=problems.license, hint=licenseHint) %}
76
+ <option value=""{% if not form.license %} selected{% endif %}>No license (all rights reserved)</option>
77
+ {% for choice in licenseChoices %}
78
+ <option value="{{ choice.key }}"{% if choice.key == form.license %} selected{% endif %}>{{ choice.name }}</option>
79
+ {% endfor %}
80
+ <option value="custom"{% if form.license == 'custom' %} selected{% endif %}>Custom license</option>
81
+ {% endcall %}
82
+
83
+ {{ field.text('settings-license-url', fields.licenseUrl, 'Custom license URL', form.licenseUrl,
84
+ error=problems.licenseUrl, inputmode='url', hint='Where its terms are. Only read for a custom license.') }}
85
+
86
+ {{ field.text('settings-license-name', fields.licenseName, 'Custom license name', form.licenseName,
87
+ error=problems.licenseName, hint='What the footer calls it. Only read for a custom license.') }}
44
88
  {% endblock %}
45
89
 
46
90
  {% block settingsPanels %}{% endblock %}
@@ -490,6 +490,31 @@ button[type='submit']:hover,
490
490
  min-height: 28rem;
491
491
  }
492
492
 
493
+ .admin-editor-enclosure {
494
+ margin: 1.25rem 0 0;
495
+ padding: 0.75rem 1rem 1rem;
496
+ border: 1px solid var(--admin-border);
497
+ }
498
+
499
+ .admin-editor-enclosure-heading {
500
+ margin: 1.25rem 0 0;
501
+ font-size: 1em;
502
+ }
503
+
504
+ .admin-editor-alternate {
505
+ display: grid;
506
+ grid-template-columns: max-content minmax(0, 1fr);
507
+ gap: 0.4rem 0.75rem;
508
+ align-items: center;
509
+ margin: 0.75rem 0 0;
510
+ padding: 0.5rem 0.75rem 0.75rem;
511
+ border: 1px solid var(--admin-border);
512
+ }
513
+
514
+ .admin-editor-alternate label {
515
+ margin: 0;
516
+ }
517
+
493
518
  .admin-hint {
494
519
  margin: 0.25rem 0 0;
495
520
  color: var(--admin-muted);
@@ -792,6 +817,14 @@ button[type='submit']:hover,
792
817
  border: 1px solid var(--admin-border);
793
818
  }
794
819
 
820
+ .admin-icon-preview {
821
+ flex: none;
822
+ width: 6rem;
823
+ height: 6rem;
824
+ border: 1px solid var(--admin-border);
825
+ border-radius: 0.75rem;
826
+ }
827
+
795
828
  .admin-avatar-current {
796
829
  display: flex;
797
830
  gap: 1rem;
@@ -2,6 +2,7 @@ import type { Context, Hono } from 'hono';
2
2
  import type { Document, DocumentType } from '../content/document.ts';
3
3
  import type { ContentStore, ListAllOptions } from '../content/store.ts';
4
4
  import type { GeekityEnv } from '../env.ts';
5
+ import type { EnclosureForm } from './enclosure-field.ts';
5
6
  /**
6
7
  * Everything that differs between the posts screens and the pages screens.
7
8
  *
@@ -179,6 +180,8 @@ export interface EditorForm {
179
180
  * lose is the default every document starts at.
180
181
  */
181
182
  comments: string;
183
+ /** The post's recording (TASK-213). Posts only; blank on a page. */
184
+ enclosure: EnclosureForm;
182
185
  body: string;
183
186
  /** The hash of the file the form was filled in from; empty for a new one. */
184
187
  hash: string;
@@ -1 +1 @@
1
- {"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/admin/documents.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/admin/documents.ts"],"names":[],"mappings":"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"}