@geekity/cms 0.28.0 → 0.29.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 (187) hide show
  1. package/README.md +303 -53
  2. package/admin/pages/comments/all.njk +3 -1
  3. package/admin/pages/federation/followers.njk +8 -2
  4. package/dist/admin/comments.d.ts +2 -0
  5. package/dist/admin/comments.d.ts.map +1 -1
  6. package/dist/admin/comments.js +19 -49
  7. package/dist/admin/comments.js.map +1 -1
  8. package/dist/admin/documents.d.ts +5 -0
  9. package/dist/admin/documents.d.ts.map +1 -1
  10. package/dist/admin/documents.js +45 -17
  11. package/dist/admin/documents.js.map +1 -1
  12. package/dist/admin/federation.d.ts +21 -1
  13. package/dist/admin/federation.d.ts.map +1 -1
  14. package/dist/admin/federation.js +17 -7
  15. package/dist/admin/federation.js.map +1 -1
  16. package/dist/admin/store.d.ts +64 -10
  17. package/dist/admin/store.d.ts.map +1 -1
  18. package/dist/admin/store.js +166 -17
  19. package/dist/admin/store.js.map +1 -1
  20. package/dist/cli.js +2 -2
  21. package/dist/cli.js.map +1 -1
  22. package/dist/comments/form.d.ts +3 -1
  23. package/dist/comments/form.d.ts.map +1 -1
  24. package/dist/comments/form.js +2 -1
  25. package/dist/comments/form.js.map +1 -1
  26. package/dist/comments/index.d.ts +2 -2
  27. package/dist/comments/index.d.ts.map +1 -1
  28. package/dist/comments/index.js +1 -1
  29. package/dist/comments/index.js.map +1 -1
  30. package/dist/comments/records.d.ts +24 -20
  31. package/dist/comments/records.d.ts.map +1 -1
  32. package/dist/comments/records.js +74 -23
  33. package/dist/comments/records.js.map +1 -1
  34. package/dist/comments/reply-notices.d.ts +42 -0
  35. package/dist/comments/reply-notices.d.ts.map +1 -0
  36. package/dist/comments/reply-notices.js +76 -0
  37. package/dist/comments/reply-notices.js.map +1 -0
  38. package/dist/comments/reply-post.d.ts +49 -0
  39. package/dist/comments/reply-post.d.ts.map +1 -0
  40. package/dist/comments/reply-post.js +66 -0
  41. package/dist/comments/reply-post.js.map +1 -0
  42. package/dist/comments/routes.d.ts +6 -1
  43. package/dist/comments/routes.d.ts.map +1 -1
  44. package/dist/comments/routes.js +49 -21
  45. package/dist/comments/routes.js.map +1 -1
  46. package/dist/comments/submission.d.ts +8 -25
  47. package/dist/comments/submission.d.ts.map +1 -1
  48. package/dist/comments/submission.js +19 -38
  49. package/dist/comments/submission.js.map +1 -1
  50. package/dist/comments/viewer.d.ts.map +1 -1
  51. package/dist/comments/viewer.js +1 -0
  52. package/dist/comments/viewer.js.map +1 -1
  53. package/dist/content/citation.d.ts +1 -0
  54. package/dist/content/citation.d.ts.map +1 -1
  55. package/dist/content/citation.js +2 -0
  56. package/dist/content/citation.js.map +1 -1
  57. package/dist/content/original.d.ts +6 -1
  58. package/dist/content/original.d.ts.map +1 -1
  59. package/dist/content/original.js +7 -4
  60. package/dist/content/original.js.map +1 -1
  61. package/dist/content/store.d.ts +43 -11
  62. package/dist/content/store.d.ts.map +1 -1
  63. package/dist/content/store.js +170 -10
  64. package/dist/content/store.js.map +1 -1
  65. package/dist/content/sync.d.ts +8 -0
  66. package/dist/content/sync.d.ts.map +1 -1
  67. package/dist/content/sync.js +164 -71
  68. package/dist/content/sync.js.map +1 -1
  69. package/dist/federation/article.d.ts.map +1 -1
  70. package/dist/federation/article.js +2 -1
  71. package/dist/federation/article.js.map +1 -1
  72. package/dist/federation/backfill.d.ts +114 -0
  73. package/dist/federation/backfill.d.ts.map +1 -0
  74. package/dist/federation/backfill.js +312 -0
  75. package/dist/federation/backfill.js.map +1 -0
  76. package/dist/federation/inbox.js +1 -1
  77. package/dist/federation/inbox.js.map +1 -1
  78. package/dist/federation/records.d.ts +13 -0
  79. package/dist/federation/records.d.ts.map +1 -1
  80. package/dist/federation/records.js +49 -10
  81. package/dist/federation/records.js.map +1 -1
  82. package/dist/federation/replies.d.ts +6 -0
  83. package/dist/federation/replies.d.ts.map +1 -1
  84. package/dist/federation/replies.js +18 -1
  85. package/dist/federation/replies.js.map +1 -1
  86. package/dist/index.d.ts +11 -3
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +79 -10
  89. package/dist/index.js.map +1 -1
  90. package/dist/init.d.ts +1 -1
  91. package/dist/init.d.ts.map +1 -1
  92. package/dist/init.js +1 -7
  93. package/dist/init.js.map +1 -1
  94. package/dist/web/context.d.ts +18 -3
  95. package/dist/web/context.d.ts.map +1 -1
  96. package/dist/web/context.js +17 -4
  97. package/dist/web/context.js.map +1 -1
  98. package/dist/web/conversation.d.ts +155 -16
  99. package/dist/web/conversation.d.ts.map +1 -1
  100. package/dist/web/conversation.js +602 -135
  101. package/dist/web/conversation.js.map +1 -1
  102. package/dist/web/documents.d.ts +0 -22
  103. package/dist/web/documents.d.ts.map +1 -1
  104. package/dist/web/documents.js +1 -34
  105. package/dist/web/documents.js.map +1 -1
  106. package/dist/web/feed-item.d.ts +12 -8
  107. package/dist/web/feed-item.d.ts.map +1 -1
  108. package/dist/web/feed-item.js +10 -10
  109. package/dist/web/feed-item.js.map +1 -1
  110. package/dist/web/feed-rss.d.ts.map +1 -1
  111. package/dist/web/feed-rss.js +34 -10
  112. package/dist/web/feed-rss.js.map +1 -1
  113. package/dist/web/feed-source.d.ts +30 -1
  114. package/dist/web/feed-source.d.ts.map +1 -1
  115. package/dist/web/feed-source.js +25 -0
  116. package/dist/web/feed-source.js.map +1 -1
  117. package/dist/web/feeds.d.ts +1 -1
  118. package/dist/web/feeds.d.ts.map +1 -1
  119. package/dist/web/feeds.js +7 -2
  120. package/dist/web/feeds.js.map +1 -1
  121. package/dist/web/guids.d.ts +59 -0
  122. package/dist/web/guids.d.ts.map +1 -0
  123. package/dist/web/guids.js +79 -0
  124. package/dist/web/guids.js.map +1 -0
  125. package/dist/web/index.d.ts +5 -6
  126. package/dist/web/index.d.ts.map +1 -1
  127. package/dist/web/index.js +3 -3
  128. package/dist/web/index.js.map +1 -1
  129. package/dist/web/redirects.d.ts +12 -0
  130. package/dist/web/redirects.d.ts.map +1 -1
  131. package/dist/web/redirects.js +16 -4
  132. package/dist/web/redirects.js.map +1 -1
  133. package/dist/web/render.d.ts +19 -9
  134. package/dist/web/render.d.ts.map +1 -1
  135. package/dist/web/render.js +44 -42
  136. package/dist/web/render.js.map +1 -1
  137. package/dist/web/routes.d.ts +2 -2
  138. package/dist/web/routes.d.ts.map +1 -1
  139. package/dist/web/routes.js +94 -31
  140. package/dist/web/routes.js.map +1 -1
  141. package/dist/webmention/index.d.ts +1 -1
  142. package/dist/webmention/index.d.ts.map +1 -1
  143. package/dist/webmention/index.js +1 -1
  144. package/dist/webmention/index.js.map +1 -1
  145. package/dist/webmention/links.d.ts +55 -0
  146. package/dist/webmention/links.d.ts.map +1 -1
  147. package/dist/webmention/links.js +162 -0
  148. package/dist/webmention/links.js.map +1 -1
  149. package/dist/webmention/microformats.d.ts +15 -0
  150. package/dist/webmention/microformats.d.ts.map +1 -1
  151. package/dist/webmention/microformats.js +63 -1
  152. package/dist/webmention/microformats.js.map +1 -1
  153. package/dist/webmention/pingback.js +2 -1
  154. package/dist/webmention/pingback.js.map +1 -1
  155. package/dist/webmention/receive.d.ts +5 -0
  156. package/dist/webmention/receive.d.ts.map +1 -1
  157. package/dist/webmention/receive.js +99 -3
  158. package/dist/webmention/receive.js.map +1 -1
  159. package/dist/webmention/reply-context.d.ts +15 -0
  160. package/dist/webmention/reply-context.d.ts.map +1 -1
  161. package/dist/webmention/reply-context.js +29 -0
  162. package/dist/webmention/reply-context.js.map +1 -1
  163. package/dist/webmention/reply-contexts.d.ts +6 -0
  164. package/dist/webmention/reply-contexts.d.ts.map +1 -1
  165. package/dist/webmention/reply-contexts.js +16 -10
  166. package/dist/webmention/reply-contexts.js.map +1 -1
  167. package/dist/webmention/routes.d.ts.map +1 -1
  168. package/dist/webmention/routes.js +2 -1
  169. package/dist/webmention/routes.js.map +1 -1
  170. package/dist/webmention/service.d.ts +30 -1
  171. package/dist/webmention/service.d.ts.map +1 -1
  172. package/dist/webmention/service.js +172 -32
  173. package/dist/webmention/service.js.map +1 -1
  174. package/package.json +1 -2
  175. package/themes/default/README.md +242 -78
  176. package/themes/default/layouts/base.njk +8 -0
  177. package/themes/default/layouts/comment.njk +90 -0
  178. package/themes/default/layouts/front-page.njk +9 -47
  179. package/themes/default/layouts/page.njk +2 -0
  180. package/themes/default/layouts/post.njk +14 -2
  181. package/themes/default/partials/backlinks.njk +19 -0
  182. package/themes/default/partials/cited-page.njk +3 -1
  183. package/themes/default/partials/comment-form.njk +10 -0
  184. package/themes/default/partials/comment.njk +79 -0
  185. package/themes/default/partials/conversation.njk +14 -59
  186. package/themes/default/src/style.css +42 -0
  187. package/themes/default/static/style.css +67 -0
package/README.md CHANGED
@@ -683,6 +683,50 @@ database.
683
683
 
684
684
  [FEP-044f]: https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md
685
685
 
686
+ ### Replies to fediverse replies
687
+
688
+ Somebody who answers a fediverse reply on one of the site's posts addresses
689
+ the person they answer, not the site, so their reply never reaches the inbox.
690
+ Most servers list a note's replies in its `replies` collection, and the site
691
+ reads those collections to bring such replies in. The sweep starts when
692
+ the site serves and runs every hour, within these limits:
693
+
694
+ | Limit | Value | Meaning |
695
+ | -------- | -------- | ----------------------------------------------------------------------------------------------------- |
696
+ | Interval | 6 hours | How long a thread is left alone after it was read. |
697
+ | Age | 30 days | How old a post gets before its thread is no longer read, by the post's date. |
698
+ | Depth | 4 levels | How far below the post a fediverse reply may sit and still have its collection read. |
699
+ | Pages | 3 | How many pages of one collection are read. Mastodon's first page holds the author's own replies only. |
700
+
701
+ Every request is signed as the site's first account, as the actor profile
702
+ fetches are. A reply is kept only when it answers the note whose collection
703
+ listed it, is addressed to the public, and is written by an actor on the
704
+ note's own server. A note on the site's own origin is never fetched, because
705
+ the site already knows what it published.
706
+
707
+ A reply found this way is logged as the `Create` nobody delivered: a line in
708
+ `content/_data/federation/inbox/{yyyy}-{mm}.jsonl` with `"fetched": true` in
709
+ front of it, an `actor` taken from the note's `attributedTo` and the id
710
+ `{note id}#fetched`. From there it is a reply like any other. It is threaded
711
+ under the reply it answers with its own URL, counted, given a `/replies/` feed
712
+ and listed on the federation screen, and its author's `Delete` takes it back.
713
+ A note held once is never logged twice. The sweep skips a note the inbox
714
+ already holds, and a delivery of a note the sweep fetched first replaces the
715
+ fetched line.
716
+
717
+ The site removes a fetched reply, with the fetched replies under it, when its
718
+ server answers 404 or 410 for it, or when a collection read to its end no
719
+ longer lists it. A delivered reply is never removed this way. A note with no
720
+ `replies` collection, a server that answers with any other error, and a
721
+ collection cut off by the page limit change nothing. The thread keeps what it
722
+ holds, and the sweep goes on to the next reply.
723
+
724
+ The site reads `replies` and not the conversation a note names in `context`
725
+ ([FEP-7888]), because Mastodon publishes `replies` and few servers publish a
726
+ `context` collection yet.
727
+
728
+ [FEP-7888]: https://codeberg.org/fediverse/fep/src/branch/main/fep/7888/fep-7888.md
729
+
686
730
  ### Relays
687
731
 
688
732
  A [Mastodon-style relay][fepae0c] boosts every public activity it is sent on to
@@ -1002,7 +1046,7 @@ container never starts live:
1002
1046
 
1003
1047
  ```yaml
1004
1048
  services:
1005
- server:
1049
+ geekity:
1006
1050
  environment:
1007
1051
  GEEKITY_DEV_MODE: 'true'
1008
1052
  ```
@@ -1011,7 +1055,7 @@ To go live, remove the variable, recreate the container (the file in the
1011
1055
  `data` volume keeps the mode on through the restart), then run:
1012
1056
 
1013
1057
  ```sh
1014
- docker compose exec server geekity dev-mode off
1058
+ docker compose exec geekity geekity dev-mode off
1015
1059
  ```
1016
1060
 
1017
1061
  `geekity dev-mode status` in the same container says which state the site is
@@ -1075,6 +1119,68 @@ A checker that throws is treated as having no opinion and logged, so a service
1075
1119
  that is down never stops a site taking comments. `/admin/comments` is the
1076
1120
  moderation queue and the dashboard carries the number waiting.
1077
1121
 
1122
+ ### A comment's own page
1123
+
1124
+ A comment left through the form has a page of its own at `/comment/{id}/`,
1125
+ the comment's id percent-encoded. That page is the comment's URL in the thread
1126
+ and in both comments feeds, so a site that replies to the comment fetches the
1127
+ comment rather than the whole post. The `#comment-{id}` anchor on the post
1128
+ stays, so an old link still scrolls to it. A webmention or a fediverse reply
1129
+ has no such page: its URL is where it came from.
1130
+
1131
+ The page holds the comment as its one `h-entry`, with `u-in-reply-to` the post
1132
+ or the comment it answers. Above it is the thread from the post down, each step
1133
+ a nested `u-in-reply-to h-cite`, and below it the replies to it at every depth.
1134
+ An ancestor a reader may not see is a placeholder saying the comment is no
1135
+ longer shown, and the thread on the post prints the same placeholder above an
1136
+ approved reply to a pending, spam or deleted comment. The page answers only an approved comment, and only as the post
1137
+ would: 410 once the post is deleted, 404 while the post is not public or not
1138
+ showing its conversation. It is `noindex` and absent from the sitemap, the post
1139
+ feeds and search. Backlog doc-6 has the rules.
1140
+
1141
+ ### Replying while signed in
1142
+
1143
+ A comment is something somebody else says on the site. A post is something a
1144
+ signed-in user says. So a reply written by somebody signed in to the site, in
1145
+ the thread or in the Reply box on the moderation screen, is a reply post, not a
1146
+ comment. It is one file under `posts/`, saved
1147
+ the way the editor and Micropub save one, and the thread shows it inline under
1148
+ what it answers. Its `in-reply-to` is what it answers:
1149
+
1150
+ | Answering | `in-reply-to` | It also |
1151
+ | ------------------ | --------------------------------- | -------------------------------------------------------- |
1152
+ | The post | The post's permalink | |
1153
+ | A native comment | The comment's `/comment/{id}/` | Emails the commenter once, if they asked about replies. |
1154
+ | A webmention reply | The page the webmention came from | Sends that page a webmention. |
1155
+ | A fediverse reply | The note's id | Federates in reply to the note, addressed to its author. |
1156
+ | A reply post | The reply post's permalink | |
1157
+
1158
+ The signed-in form and the moderation screen's Reply box have an "Include in
1159
+ posts and feeds" checkbox, unchecked by default. Unchecked, the reply post is [unlisted](#unlisted-posts): its own page
1160
+ and the thread, but no listing or post feed. Checked, it is public. It reaches
1161
+ whoever it answers either way. A visitor's form has no such box.
1162
+
1163
+ Somebody signed in gets a Reply link on every reply in a thread, webmentions
1164
+ and fediverse replies included. A visitor gets one on native comments only. A
1165
+ visitor's reply is always a native comment, so a visitor answering a fediverse
1166
+ reply sends nothing over ActivityPub.
1167
+
1168
+ A reply post written in the editor or over Micropub joins a thread the same way
1169
+ when its `in-reply-to` names the post or anything in its thread: a comment's
1170
+ page or `#comment-` anchor, a webmention's page, a note's id. Its reply context
1171
+ quotes that comment from the site's own records instead of the page it is on,
1172
+ and says "In reply to a comment by" its writer. A reply post answering one of
1173
+ the site's own posts or pages takes that post's title, words and author from
1174
+ the index too, so the site never fetches its own page. The commenter's email
1175
+ goes once for a reply post from any of these doors, sent when the reply post is
1176
+ first published and never again when it is edited, trashed and restored, or the
1177
+ site restarts. In the thread, a reply post's author link carries no
1178
+ `rel="nofollow ugc"`, which is kept for visitors, webmentions and the
1179
+ fediverse. A
1180
+ webmention or a fediverse reply to a reply post threads under it in the
1181
+ original thread too. Comments written before this stay comments. Backlog doc-6
1182
+ and decision-47 have the rules.
1183
+
1078
1184
  ### Being told about one
1079
1185
 
1080
1186
  With mail configured, a comment or a webmention entering the queue emails every
@@ -1207,6 +1313,19 @@ it was showing. One outcome is recorded per link (`sent`, `none` for a page that
1207
1313
  takes none, or `failed`), shown per post on `/admin/federation`, and the Resend
1208
1314
  button there sends them again from the file as it now reads.
1209
1315
 
1316
+ **Links to the site's own pages** send no webmention. A post that cites another
1317
+ post on the same site does not go through moderation, does not add to a comment
1318
+ count and does not make an HTTP request to the site itself. The index records
1319
+ the link instead. The linked post or page then lists the linking one among its
1320
+ backlinks, which the default theme prints as "Linked from" under it, apart from
1321
+ the comments. A link counts for the document the site serves at its URL: a link
1322
+ to `/` counts for the page the Reading setting makes the homepage, and a link
1323
+ to a URL the site redirects, such as an imported `/?p=123`, counts for the
1324
+ document the redirect leads to. Redirects are followed when the backlinks are
1325
+ read, so editing a redirect file moves a backlink without touching the post
1326
+ that holds the link. A draft, future-dated, unlisted, private or trashed post is never
1327
+ a backlink. See `themes/default/README.md` for the `backlinks` context.
1328
+
1210
1329
  **Receiving** advertises `/_geekity/webmention` two ways — a `Link` header on
1211
1330
  every representation of a document, and a `<link rel="webmention">` in the head
1212
1331
  — and takes a form `POST` of `source` and `target`. It answers `202` for
@@ -1219,6 +1338,12 @@ queue as everything else — through the same `commentChecker` seam, so a checke
1219
1338
  can tell one from a form submission by its `source`. Akismet is told it is a
1220
1339
  `webmention` rather than a `comment`.
1221
1340
 
1341
+ A reply whose `u-in-reply-to` names a reply already on the post (a comment by
1342
+ its own page or its `#comment-{id}` anchor, an earlier webmention by the page it
1343
+ was sent from, a fediverse reply by its url or id) threads under that reply, and
1344
+ moves with it when the source is edited and sent again. A webmention sent to a
1345
+ comment's page, `/comment/{id}/`, lands on that comment's post.
1346
+
1222
1347
  The source URL is its identity. A page that sends its webmention again updates
1223
1348
  what it left rather than adding a second, and one whose link has gone — or which
1224
1349
  answers 404 or 410 — takes it away. Closing rules do not apply: a post that
@@ -1787,8 +1912,8 @@ The rule works for pages too.
1787
1912
  | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
1788
1913
  | Its permalink, in every representation | 200, with `X-Robots-Tag: noindex`. The default theme also prints `<meta name="robots" content="noindex">`. |
1789
1914
  | The home page, tag, category and author archives, the `archive: true` page | Absent, and not counted in a pager or a tag count. |
1790
- | Previous and next links under other posts, the front page's recent posts | Absent. |
1791
- | Every RSS, Atom and JSON feed, and the site-wide comments feed | Absent. Saving one pings no notify server. |
1915
+ | Previous and next links under other posts, a front page's `newestPosts` | Absent. |
1916
+ | Every RSS, Atom and JSON feed, and the site-wide comments feed | Absent. Saving one pings no notify server. An unlisted reply post shown in a thread is still in that thread's comments feeds. |
1792
1917
  | The sitemap, search (HTML and JSON) and `llms.txt` | Absent. |
1793
1918
  | IndexNow | Not submitted. A public post that becomes unlisted is submitted once, so a search engine reads the `noindex`. |
1794
1919
  | The ActivityPub outbox and featured collection | Absent. |
@@ -2132,9 +2257,38 @@ choice is WordPress's own, and so are its two answers:
2132
2257
  the default, and it is what an empty `homepage` means.
2133
2258
  - **A static page.** The page is served at `/`, its own permalink answers `301`
2134
2259
  to `/` so the front page has one URL, and the menu links it at `/`. The
2135
- default theme draws it with `layouts/front-page.njk` — the page's own words,
2136
- then `Recent Posts`, then the line of links and the bio — and a theme lays it
2137
- out differently by overriding that one file.
2260
+ default theme draws it with `layouts/front-page.njk`, which prints the
2261
+ page's own title and words and nothing else (decision-44). It lists no
2262
+ posts, prints no links and prints no bio.
2263
+
2264
+ A site that wants more on its front page, such as its newest posts or links
2265
+ to its archives, overrides `layouts/front-page.njk` in its own theme. The
2266
+ front page's context carries `newestPosts(count)`, a function that returns the
2267
+ newest `count` published posts as the same entries a listing prints. Drafts,
2268
+ future-dated, unlisted and trashed posts are left out. The theme chooses the
2269
+ count, and the query runs only when the theme calls it. For example, this
2270
+ override lists the five newest posts under the page's words:
2271
+
2272
+ ```njk
2273
+ {% extends "layouts/base.njk" %}
2274
+
2275
+ {% block content %}
2276
+ <div class="page-body e-content">{{ content | safe }}</div>
2277
+
2278
+ <h2 class="section-title">Recent Posts</h2>
2279
+ {% set posts = newestPosts(5) %}
2280
+ {% set feedHeading = 3 %}
2281
+ {% include "partials/post-list.njk" %}
2282
+ {% endblock %}
2283
+ ```
2284
+
2285
+ On a solo author site the default theme's base layout puts the author's
2286
+ `rel="me"` claims in the head of `/` as `<link rel="me">` elements, one for
2287
+ their author archive and one for each profile link, whatever the front page
2288
+ prints (decision-45). A static front page prints no h-card. A site that wants
2289
+ its homepage to carry a representative h-card writes one in the homepage's
2290
+ words, or includes `partials/bio.njk` with `bioHome` set in its override, as
2291
+ the [theme README](./themes/default/README.md#the-front-page) shows.
2138
2292
 
2139
2293
  With a homepage set, a second pick gives the listing a page of its own: the
2140
2294
  **posts page**. Its permalink carries the listing, with the page's own title
@@ -2663,6 +2817,7 @@ Booting mounts the public site on the app. The routes are:
2663
2817
  | `/author/{username}/feed/` and siblings | The same, for one person. |
2664
2818
  | `/comments/feed/` | Every reply the inbox has been sent, as RSS 2.0. |
2665
2819
  | `{permalink}feed/` | One post's replies, the same way. |
2820
+ | `/comment/{id}/` | One comment left through the form, on a page of its own: see [Comments](#comments). Noindex and on no list. |
2666
2821
  | `/sitemap.xml` | Every public URL, for a search engine. |
2667
2822
  | `/sitemap-{n}.xml` | One file of a sitemap too big to be a single one. |
2668
2823
  | `/robots.txt` | What a crawler may have, and where the sitemap is. |
@@ -2847,9 +3002,20 @@ The list is in the file, so deleting `data/geekity.db` keeps it working.
2847
3002
 
2848
3003
  A URL in the list only redirects while nothing else lives there: a new document
2849
3004
  given that URL takes it over. Moving a document back to a URL in its list takes
2850
- that URL off the list. A draft that was never published moves without leaving
2851
- anything behind. Correcting a published post's date files it under the new
2852
- day but keeps its URL.
3005
+ that URL off the list. A draft that was never published and has no comments
3006
+ moves without leaving anything behind; one with comments was read at its URL,
3007
+ so it keeps a `redirect_from` like a published one. Correcting a published
3008
+ post's date files it under the new day but keeps its URL.
3009
+
3010
+ A moved document keeps its conversation. Its comments and stored webmentions
3011
+ move to its new slug, a reply post answering any URL in its `redirect_from`
3012
+ stays in its thread, and its feeds keep their guid: a post keeps its object id
3013
+ as its `activitypub.id`, and a page keeps the URL it left as its `guid`, so the
3014
+ `/replies/` feed a reader subscribed to keeps answering. The record of the
3015
+ webmentions a post has sent moves with it too. A move made by hand is followed
3016
+ the same way when the file names the old URL in `redirect_from`; a permalink
3017
+ changed by hand with no `redirect_from` leaves the old URL answering 404 and its
3018
+ comments where they were, until the file names it.
2853
3019
 
2854
3020
  ### Declared redirects
2855
3021
 
@@ -3065,6 +3231,7 @@ so a site moving off it keeps every subscriber it had:
3065
3231
  | `/category/{name}/feed/` | One category archive, the same three |
3066
3232
  | `/comments/feed/` | Every reply the site has been sent, in RSS |
3067
3233
  | `{permalink}feed/` | One post's replies, in RSS |
3234
+ | `/replies/{id or key}/` | One item's direct replies, in RSS |
3068
3235
 
3069
3236
  `/feed/` is RSS because that is the format nearly every existing subscriber
3070
3237
  holds. The two archive bases are settings (`tagBase`, `categoryBase`), so the
@@ -3104,15 +3271,17 @@ The three formats render one **feed item**, derived once per post, rather than
3104
3271
  each reading the file for itself. So they agree about the three things a reader
3105
3272
  actually keys on.
3106
3273
 
3107
- **Identity.** A post is named by its ActivityStreams object id, which is its
3108
- permalink (see [Federation](#federation)) or the `activitypub.id` the front
3109
- matter stores for a post migrated from elsewhere. That id is RSS's `guid`,
3110
- Atom's `id` and JSON Feed's `id`. The permalink is always the link: RSS's
3111
- `link`, Atom's `link rel="alternate"` and JSON Feed's `url`. The two are the
3112
- same URL for every post born on this CMS, and differ only for one carrying a
3113
- stored id — which is what lets a migrated post keep the name its WordPress
3274
+ **Identity.** A post is named by the first of three things it has: the `guid`
3275
+ its front matter sets, when that parses as a URL; else the `activitypub.id`
3276
+ the front matter stores for a post migrated from elsewhere; else its permalink
3277
+ (see [Federation](#federation)). That name is RSS's `guid`, Atom's `id` and
3278
+ JSON Feed's `id`. The permalink is always the link: RSS's `link`, Atom's
3279
+ `link rel="alternate"` and JSON Feed's `url`. The two are the same URL for
3280
+ every post born on this CMS, and differ only for one carrying a stored id or a
3281
+ `guid` — which is what lets a migrated post keep the name its WordPress
3114
3282
  subscribers already hold. RSS's `isPermaLink` says which of the two a `guid`
3115
- is: `true` when it is the permalink, `false` for a stored id like `?p=813`.
3283
+ is: `true` only when it equals the permalink, `false` for anything else, such
3284
+ as a stored `?p=813`.
3116
3285
 
3117
3286
  **Terms.** Every format lists the post's categories and then its tags, in file
3118
3287
  order, as one flat list: RSS's `category` elements, Atom's `category term`
@@ -3149,8 +3318,8 @@ do not know it to ignore:
3149
3318
  The key is `_geekity` because an extension is named after its publisher, and
3150
3319
  `in_reply_to` spells the microformats `in-reply-to` property the JSON way. A
3151
3320
  post that is not a reply, or whose `in-reply-to` is not such a URL, carries
3152
- neither. RSS 2.0 has no equivalent element, so an RSS item for a reply is
3153
- written the same as any other.
3321
+ neither. RSS 2.0 has no equivalent element, so an RSS item carries the target
3322
+ as the [source namespace][source-ns]'s `<source:inReplyTo>`.
3154
3323
 
3155
3324
  **Languages.** Every feed declares the site's `language` once: RSS as the
3156
3325
  channel's `language`, Atom as the feed's `xml:lang` and JSON Feed as its
@@ -3180,17 +3349,21 @@ avatar when the site has one.
3180
3349
  An item carries `title`, `link`, `guid`, `pubDate` in RFC 822, `dc:creator` from
3181
3350
  the post's author or the site's, one `category` per term, `description` holding
3182
3351
  the summary, `content:encoded` holding the whole rendered post, and
3183
- `source:markdown` holding the Markdown the post was written from. The `guid`,
3184
- the terms and the summary are the ones described above.
3352
+ `source:markdown` holding the Markdown the post was written from. A reply also
3353
+ carries `source:inReplyTo` holding the URL it answers. The `guid`, the terms
3354
+ and the summary are the ones described above.
3185
3355
 
3186
3356
  `source:markdown` is Dave Winer's [source namespace][source-ns]: a reader that
3187
3357
  understands Markdown should render from it rather than from `content:encoded`.
3188
3358
  It is the same text the ActivityStreams `Article` carries as its `source`.
3189
3359
 
3190
- Every item also says where its comments are, three ways: `comments` is the page
3191
- to read them on, `wfw:commentRss` (the [Well-Formed Web][wfw-ns] comment API) is
3192
- the feed to poll, and `source:comments` is that same feed with a `count`
3193
- attribute, so a reader can say "3 comments" without fetching anything.
3360
+ Every item also says where its comments are: `comments` is the page to read
3361
+ them on and `wfw:commentRss` (the [Well-Formed Web][wfw-ns] comment API) is the
3362
+ feed of the whole thread to poll. A post with replies also carries
3363
+ `source:comments`, whose `count` is how many replies answer the post directly
3364
+ and whose `feedUrl` is its `/replies/{key}/` feed holding only those (see
3365
+ [Walking a thread](#walking-a-thread)). A post nobody answered carries no
3366
+ `source:comments`.
3194
3367
 
3195
3368
  When the site has a license (Settings > General, or `license` in `site.json`),
3196
3369
  the channel carries a `creativeCommons:license` holding its URL, and every item
@@ -3211,34 +3384,68 @@ post as under it.
3211
3384
 
3212
3385
  ### Comments
3213
3386
 
3214
- This CMS stores no comments of its own. What it has instead is the fediverse
3215
- replies its inbox has been sent: a `Create` of a `Note` whose `inReplyTo` names
3216
- a post's ActivityStreams object id (see [Federation](#federation)). Those are
3217
- what the comments feeds publish, at the URLs WordPress publishes its own at —
3218
- `{permalink}feed/` for one post, `/comments/feed/` for the whole site.
3387
+ The comments feeds publish a document's conversation, at the URLs WordPress
3388
+ publishes its own at: `{permalink}feed/` for one post or page, and
3389
+ `/comments/feed/` for the whole site. Both are read from the same conversation
3390
+ the page draws under the post, so they carry what a reader sees there:
3391
+
3392
+ - native comments left through the form, once a moderator approves them;
3393
+ - webmention replies and mentions;
3394
+ - fediverse replies, a `Create` of a `Note` whose `inReplyTo` names the post
3395
+ or a reply under it (see [Federation](#federation)), and the quotes of a
3396
+ post its author approved;
3397
+ - reply posts written on this site that answer the post or a reply under it.
3398
+
3399
+ Likes, boosts and reposts are counted on the page and are in no feed.
3219
3400
 
3220
3401
  Both are RSS 2.0 and nothing else: a comments feed has no Atom or JSON spelling
3221
- here, so `{permalink}feed/atom/` 404s. A post with no replies answers an empty
3222
- feed rather than a 404 — it exists, and a reader that subscribed before anybody
3223
- answered should keep polling — while a permalink that is no published post 404s
3224
- like any other. A page has no comments feed at all: only posts federate, so
3225
- nothing can ever have replied to one.
3402
+ here, so `{permalink}feed/atom/` 404s. A post has a comments feed whether or
3403
+ not it still takes comments. A page has one while it takes comments
3404
+ (`comments: true` in its front matter, and comments on for the site), which is
3405
+ when it shows its conversation; any other page's `{permalink}feed/` 404s, and
3406
+ its comments are left out of `/comments/feed/` too. A document with no replies
3407
+ answers an empty feed rather than a 404, because it exists and a reader that
3408
+ subscribed before anybody answered should keep polling. A permalink that is no
3409
+ published document 404s like any other. The page advertises its own feed with
3410
+ a `<link rel="alternate" type="application/rss+xml">` in its `<head>`.
3226
3411
 
3227
3412
  A channel carries the usual `title` (`Comments on: {post}`), `link`,
3228
3413
  `description`, `language`, `lastBuildDate`, `generator`, `atom:link rel="self"`
3229
3414
  and the same notify-server elements every other feed carries. An item carries the author's name as its `title` and `dc:creator`,
3230
- the reply's `url` as its `link` and its id as `guid isPermaLink="false"`, the
3231
- `published` time the note gave (or when it arrived, if it gave none),
3232
- `description` holding a plain-text excerpt and `content:encoded` holding the
3233
- note. On the site-wide feed the title names the post as well: `{author} on
3234
- {post}`.
3235
-
3236
- The author's name is the profile the site stored when that actor followed it,
3237
- and otherwise the `@user@host` the actor's own URL implies. Naming them
3415
+ the reply's `url` as its `link` (a comment left through the form links to its
3416
+ own page at `/comment/{id}/`), a `guid`, the `published` time the reply gave (or
3417
+ when it arrived, if it gave none), `description` holding a plain-text excerpt
3418
+ and `content:encoded` holding the reply. The `guid` of a comment left through the
3419
+ form is its page, `isPermaLink="true"`; any other reply's is its own id: a
3420
+ fediverse note's id, a webmention's stored id, a reply post's object id. A
3421
+ comment imported from WordPress keeps the `guid` WordPress's comments feed
3422
+ published, so a reader sees nothing new after the move.
3423
+ `isPermaLink` is `true` exactly when the `guid` is the `link`.
3424
+
3425
+ Each item also carries, from the [source namespace][source-ns]:
3426
+
3427
+ - `source:inReplyTo`, the `guid` of what it answers in these feeds: the post's
3428
+ feed `guid` for an answer to the post, else the `guid` of the reply it
3429
+ answers. A reply under a comment a reader may not see (one waiting for a
3430
+ moderator, spam or deleted) names the nearest reply above it a reader can
3431
+ see, or the post, and is counted as that one's reply.
3432
+ - `source:comments`, only on an item with replies: `count` is how many answer
3433
+ it directly and `feedUrl` its `/replies/` feed.
3434
+ - `<source url="…">Name</source>`, the core RSS element, when the site knows a
3435
+ feed for the author: a webmention author's `h-card` URL or site, or a site
3436
+ user's author feed `/author/{username}/feed/`. A comment left through the
3437
+ form and a fediverse reply carry none.
3438
+
3439
+ On the site-wide feed the title names the post or page as well: `{author} on
3440
+ {post}`, or `{author} replying to {parent author} on {post}` for an answer to
3441
+ another reply a reader can see.
3442
+
3443
+ A fediverse reply's author is named by the profile the site stored when that
3444
+ actor followed it, and otherwise the `@user@host` the actor's own URL implies. Naming them
3238
3445
  properly would mean dereferencing the actor, which is a network round trip per
3239
3446
  comment shown.
3240
3447
 
3241
- **The note's HTML is sanitised before it is published.** It is markup a
3448
+ **A reply's HTML is sanitised before it is published.** It is markup a
3242
3449
  stranger wrote, so it is tokenised and rebuilt from an allowlist rather than
3243
3450
  passed through: `p`, `br`, `a`, `em`, `strong`, `del`, `code`, `pre`,
3244
3451
  `blockquote` and the list elements survive; `script` and `style` are dropped
@@ -3247,10 +3454,37 @@ every attribute goes except an `a`'s `href`, which must be `http`, `https` or
3247
3454
  `mailto` and is marked `rel="nofollow noopener noreferrer"`. `sanitizeCommentHtml`
3248
3455
  is exported for a site that shows comments in its own templates.
3249
3456
 
3250
- A reply to a post that has since been unpublished or moved to the trash
3251
- disappears from `/comments/feed/`, and that post's own feed 404s with the post.
3457
+ A reply to a document that has since been unpublished or moved to the trash
3458
+ disappears from `/comments/feed/`, and that document's own feed 404s with it.
3252
3459
  `feedSize` caps both feeds.
3253
3460
 
3461
+ ### Walking a thread
3462
+
3463
+ `{permalink}feed/` is the whole thread, flat, which is what WordPress
3464
+ publishes and `wfw:commentRss` points at. A program that wants the tree
3465
+ (Dave Winer's [rss.chat][rss-chat] reads conversations this way) walks it one
3466
+ level at a time instead: from a post item's `source:comments` to a feed of the
3467
+ replies that answer the post directly, and from each of those items' own
3468
+ `source:comments` to the replies that answer it, until an item has none.
3469
+ Every reply a reader can see is reached once.
3470
+
3471
+ Each of those feeds is at `/replies/{segment}/`, RSS 2.0, newest first, its
3472
+ items in the same shape as the comments feeds:
3473
+
3474
+ - `/replies/{id}/` for a comment left through the form, by the same id as its
3475
+ page at `/comment/{id}/`, percent-encoded.
3476
+ - `/replies/{key}/` for everything else: a post or a page, a webmention, a
3477
+ fediverse reply or a reply post. A document has no id of its own and slugs
3478
+ are not unique across posts and pages, so the key is the first 16 hex digits
3479
+ of the SHA-256 of the item's feed `guid`.
3480
+
3481
+ A segment of exactly 16 lowercase hex digits is a key, and anything else is a
3482
+ comment id. A comment whose id happens to look like a key is named by its key
3483
+ instead, so no segment can name two items. An item nobody answered answers an
3484
+ empty feed; a key or an id that names nothing a reader can see 404s.
3485
+
3486
+ [rss-chat]: https://github.com/scripting/rss.chat
3487
+
3254
3488
  ### Atom and JSON Feed
3255
3489
 
3256
3490
  An Atom entry carries `id` (the post's object id), `title`, `updated`,
@@ -3540,8 +3774,10 @@ commentsRssFeed({
3540
3774
 
3541
3775
  `conversation.thread(document)` is the same reading for one post — the shape the
3542
3776
  theme's `conversation.njk` is handed — `spokenIn` flattens it into the entries a
3543
- feed carries, and `conversation.counts(documents)` is the number `source:comments`
3544
- puts beside each item of a post feed.
3777
+ feed carries, each with the reply it sits under, `conversation.counts(documents)`
3778
+ is the number of direct replies `source:comments` puts beside each item of a
3779
+ post feed, and `conversation.repliesTo(segment)` is what one `/replies/` feed
3780
+ holds.
3545
3781
 
3546
3782
  ## Sitemap and robots.txt
3547
3783
 
@@ -3969,10 +4205,24 @@ Layouts are yours. The directory data files name them — `posts.json` says
3969
4205
  Eleventy layout receives is the one this package's theme mirrors, so a layout
3970
4206
  can often be moved across with only its `{% extends %}` removed.
3971
4207
 
3972
- The feeds are the one thing that does not carry over: `/feed/`, `/feed/atom/`
3973
- and `/feed/json/` are generated in code here, not by a template, so an Eleventy
3974
- build needs its own. `/sitemap.xml` and `/robots.txt` are the same story, and
3975
- for the same reason. Everything else is the same directory.
4208
+ The default theme's own layouts and partials build too, copied into
4209
+ `content/_includes/` as `layouts/` and `partials/`: the config adds the theme's
4210
+ other filters (`absoluteUrl`, `asset`, `host`, `fediverseHandle`) and the
4211
+ `newestPosts(count)` a front page override lists posts with, so a site theme's
4212
+ `front-page.njk` calling `{% set posts = newestPosts(5) %}` builds. They get
4213
+ only the context the config gives, though: `collections.menus` rather than
4214
+ `menus`, the `conversation` filter rather than the key, and no comment form,
4215
+ search or listing pagination.
4216
+
4217
+ Some things do not carry over. `/feed/`, `/feed/atom/` and `/feed/json/` are
4218
+ generated in code here, not by a template, so an Eleventy build needs its own;
4219
+ `/sitemap.xml` and `/robots.txt` are the same story, for the same reason. A
4220
+ comment's own page at `/comment/{id}/` is not written, since a build has no
4221
+ server to take a comment, so a comment links to its `#comment-{id}` anchor on
4222
+ the post. The site's own reply posts are not threaded under what they answer,
4223
+ and a document's backlinks are not listed: both read the CMS's index of every
4224
+ document, which the config's filters do not have. The config's header lists
4225
+ the same.
3976
4226
 
3977
4227
  ### The compatibility test
3978
4228
 
@@ -95,7 +95,9 @@
95
95
  {{ rowFields(row, fields, status, csrfToken) }}
96
96
  {% set replyLabel %}Reply to {{ row.author }}{% endset %}
97
97
  {{ field.textarea('reply-' + loop.index, fields.body, replyLabel, rows='4', required=true,
98
- hint='Markdown, rendered the same way a visitor’s comment is. It is posted approved, under this one.') }}
98
+ hint='Markdown. It is saved as a reply post of yours and shown under this one.') }}
99
+ {{ field.checkbox('reply-listed-' + loop.index, fields.listed, 'Include in posts and feeds',
100
+ hint='Left unticked, the reply is Unlisted: its own page, and no listing or feed.') }}
99
101
  <div class="mt-2">{{ button('Post reply', type='submit', color='primary') }}</div>
100
102
  </form>
101
103
  {% endcall %}
@@ -1,7 +1,7 @@
1
1
  {# /admin/federation: who this site's people are to the fediverse, who follows
2
2
  each of them, what arrived in the inbox, and how what went out landed. #}
3
3
  {% extends "layouts/shell.njk" %}
4
- {% from "components/badge.njk" import status %}
4
+ {% from "components/badge.njk" import badge, status %}
5
5
  {% from "components/button.njk" import button %}
6
6
  {% from "components/card.njk" import card %}
7
7
  {% from "components/table.njk" import table %}
@@ -154,11 +154,17 @@
154
154
  {{ entry.action }}
155
155
  {% endif %}
156
156
  {% if entry.post %}
157
- <a class="link" href="{{ entry.post.editUrl }}">{{ entry.post.title }}</a>
157
+ {% if entry.post.inThread %}in the thread on {% endif %}<a class="link" href="{{ entry.post.editUrl }}">{{ entry.post.title }}</a>
158
158
  {% else %}
159
159
  <em>something of ours</em>
160
160
  {% endif %}
161
161
  <span class="whitespace-nowrap text-sm">{{ entry.receivedAt | date('html') }}</span>
162
+ {% if entry.via.kind == 'fetched' %}
163
+ {{ badge('fetched', color='info', style='soft') }}
164
+ {% if entry.via.from %}<a class="link text-sm" href="{{ entry.via.from }}">from the replies to the note it answers</a>{% endif %}
165
+ {% elif entry.via.kind == 'delivered' %}
166
+ {{ badge('delivered') }}
167
+ {% endif %}
162
168
  </li>
163
169
  {% endfor %}
164
170
  </ul>
@@ -33,6 +33,8 @@ export declare const COMMENT_ADMIN_FIELDS: {
33
33
  readonly action: 'action';
34
34
  /** The reply's own text, on the reply form. */
35
35
  readonly body: 'body';
36
+ /** The reply form's "Include in posts and feeds" box: ticked is Public, else Unlisted. */
37
+ readonly listed: 'listed';
36
38
  /** Which list to go back to afterwards. */
37
39
  readonly status: 'status';
38
40
  };
@@ -1 +1 @@
1
- {"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../../src/admin/comments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,MAAM,CAAC;AAO1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAOlD,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAe,MAAM,YAAY,CAAC;AAGzE;;;;;;;;;;;;;;GAcG;AAEH,uCAAuC;AACvC,eAAO,MAAM,aAAa,oBAA6B,CAAC;AAExD,kDAAkD;AAClD,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAE3C,kCAAkC;AAClC,eAAO,MAAM,sBAAsB,6BAA8B,CAAC;AAElE,kCAAkC;AAClC,eAAO,MAAM,mBAAmB,0BAA2B,CAAC;AAE5D,kDAAkD;AAClD,eAAO,MAAM,oBAAoB;IAC/B,uCAAuC;aACvC,EAAE,EAAE,IAAI;IACR,gEAAgE;aAChE,MAAM,EAAE,QAAQ;IAChB,+CAA+C;aAC/C,IAAI,EAAE,MAAM;IACZ,2CAA2C;aAC3C,MAAM,EAAE,QAAQ;CACR,CAAC;AAEX,mCAAmC;AACnC,eAAO,MAAM,eAAe,YAAI,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAU,CAAC;AAEtE,sCAAsC;AACtC,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,qDAAqD;AACrD,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,4DAA4D;AAC5D,eAAO,MAAM,YAAY,EAAE,SAAS;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAI3E,CAAC;AAEF,0CAA0C;AAC1C,MAAM,WAAW,UAAU;IACzB,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,uBAAuB;IACvB,MAAM,EAAE,aAAa,CAAC;IACtB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,sDAAsD;IACtD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB;;;OAGG;IACH,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,sEAAsE;IACtE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,uEAAuE;AACvE,MAAM,WAAW,0BAA0B;IACzC,oFAAoF;IACpF,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,sCAAsC;AACtC,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,EACrB,OAAO,EAAE,0BAA0B,GAClC,IAAI,CA+IN;AAED,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEzD;AAED,yCAAyC;AACzC,wBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,SAAI,GAAG,MAAM,CAG/D"}
1
+ {"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../../src/admin/comments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,MAAM,CAAC;AAM1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAMlD,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAe,MAAM,YAAY,CAAC;AAGzE;;;;;;;;;;;;;;GAcG;AAEH,uCAAuC;AACvC,eAAO,MAAM,aAAa,oBAA6B,CAAC;AAExD,kDAAkD;AAClD,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAE3C,kCAAkC;AAClC,eAAO,MAAM,sBAAsB,6BAA8B,CAAC;AAElE,kCAAkC;AAClC,eAAO,MAAM,mBAAmB,0BAA2B,CAAC;AAE5D,kDAAkD;AAClD,eAAO,MAAM,oBAAoB;IAC/B,uCAAuC;aACvC,EAAE,EAAE,IAAI;IACR,gEAAgE;aAChE,MAAM,EAAE,QAAQ;IAChB,+CAA+C;aAC/C,IAAI,EAAE,MAAM;IACZ,0FAA0F;aAC1F,MAAM,EAAE,QAAQ;IAChB,2CAA2C;aAC3C,MAAM,EAAE,QAAQ;CACR,CAAC;AAEX,mCAAmC;AACnC,eAAO,MAAM,eAAe,YAAI,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAU,CAAC;AAEtE,sCAAsC;AACtC,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,qDAAqD;AACrD,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,4DAA4D;AAC5D,eAAO,MAAM,YAAY,EAAE,SAAS;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAI3E,CAAC;AAEF,0CAA0C;AAC1C,MAAM,WAAW,UAAU;IACzB,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,uBAAuB;IACvB,MAAM,EAAE,aAAa,CAAC;IACtB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,sDAAsD;IACtD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB;;;OAGG;IACH,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,sEAAsE;IACtE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,uEAAuE;AACvE,MAAM,WAAW,0BAA0B;IACzC,oFAAoF;IACpF,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,sCAAsC;AACtC,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,EACrB,OAAO,EAAE,0BAA0B,GAClC,IAAI,CAkIN;AAED,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEzD;AAED,yCAAyC;AACzC,wBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,SAAI,GAAG,MAAM,CAG/D"}