@geekity/cms 0.15.0 → 0.16.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 (125) hide show
  1. package/README.md +107 -0
  2. package/admin/pages/documents/editor.njk +51 -0
  3. package/admin/pages/documents/syndication.njk +110 -0
  4. package/admin/pages/federation/followers.njk +7 -0
  5. package/admin/static/admin.css +11 -0
  6. package/dist/admin/documents.d.ts +101 -0
  7. package/dist/admin/documents.d.ts.map +1 -1
  8. package/dist/admin/documents.js +199 -67
  9. package/dist/admin/documents.js.map +1 -1
  10. package/dist/admin/federation.d.ts +15 -0
  11. package/dist/admin/federation.d.ts.map +1 -1
  12. package/dist/admin/federation.js +19 -0
  13. package/dist/admin/federation.js.map +1 -1
  14. package/dist/admin/menu.d.ts.map +1 -1
  15. package/dist/admin/menu.js +3 -0
  16. package/dist/admin/menu.js.map +1 -1
  17. package/dist/admin/photo-field.d.ts +39 -0
  18. package/dist/admin/photo-field.d.ts.map +1 -0
  19. package/dist/admin/photo-field.js +91 -0
  20. package/dist/admin/photo-field.js.map +1 -0
  21. package/dist/admin/preview.d.ts.map +1 -1
  22. package/dist/admin/preview.js +6 -1
  23. package/dist/admin/preview.js.map +1 -1
  24. package/dist/admin/routes.d.ts.map +1 -1
  25. package/dist/admin/routes.js +4 -0
  26. package/dist/admin/routes.js.map +1 -1
  27. package/dist/admin/syndication-targets.d.ts +57 -0
  28. package/dist/admin/syndication-targets.d.ts.map +1 -0
  29. package/dist/admin/syndication-targets.js +266 -0
  30. package/dist/admin/syndication-targets.js.map +1 -0
  31. package/dist/admin/templates.d.ts +1 -0
  32. package/dist/admin/templates.d.ts.map +1 -1
  33. package/dist/admin/templates.js +1 -0
  34. package/dist/admin/templates.js.map +1 -1
  35. package/dist/admin/uploads.d.ts +6 -0
  36. package/dist/admin/uploads.d.ts.map +1 -1
  37. package/dist/admin/uploads.js +1 -1
  38. package/dist/admin/uploads.js.map +1 -1
  39. package/dist/content/citation.d.ts +36 -0
  40. package/dist/content/citation.d.ts.map +1 -0
  41. package/dist/content/citation.js +31 -0
  42. package/dist/content/citation.js.map +1 -0
  43. package/dist/content/index.d.ts +2 -0
  44. package/dist/content/index.d.ts.map +1 -1
  45. package/dist/content/index.js +1 -0
  46. package/dist/content/index.js.map +1 -1
  47. package/dist/content/photo.d.ts +45 -0
  48. package/dist/content/photo.d.ts.map +1 -0
  49. package/dist/content/photo.js +55 -0
  50. package/dist/content/photo.js.map +1 -0
  51. package/dist/content/post-type.d.ts +20 -8
  52. package/dist/content/post-type.d.ts.map +1 -1
  53. package/dist/content/post-type.js +17 -0
  54. package/dist/content/post-type.js.map +1 -1
  55. package/dist/federation/article.d.ts.map +1 -1
  56. package/dist/federation/article.js +59 -5
  57. package/dist/federation/article.js.map +1 -1
  58. package/dist/federation/citations.d.ts +38 -0
  59. package/dist/federation/citations.d.ts.map +1 -0
  60. package/dist/federation/citations.js +98 -0
  61. package/dist/federation/citations.js.map +1 -0
  62. package/dist/federation/delivery.d.ts.map +1 -1
  63. package/dist/federation/delivery.js +79 -31
  64. package/dist/federation/delivery.js.map +1 -1
  65. package/dist/images/alt-text.d.ts +6 -0
  66. package/dist/images/alt-text.d.ts.map +1 -1
  67. package/dist/images/alt-text.js +10 -0
  68. package/dist/images/alt-text.js.map +1 -1
  69. package/dist/index.d.ts +4 -4
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +23 -2
  72. package/dist/index.js.map +1 -1
  73. package/dist/indieauth/bearer.d.ts +6 -1
  74. package/dist/indieauth/bearer.d.ts.map +1 -1
  75. package/dist/indieauth/bearer.js +39 -12
  76. package/dist/indieauth/bearer.js.map +1 -1
  77. package/dist/indieauth/discovery.d.ts +9 -4
  78. package/dist/indieauth/discovery.d.ts.map +1 -1
  79. package/dist/indieauth/discovery.js +18 -7
  80. package/dist/indieauth/discovery.js.map +1 -1
  81. package/dist/micropub/create.d.ts +56 -0
  82. package/dist/micropub/create.d.ts.map +1 -0
  83. package/dist/micropub/create.js +200 -0
  84. package/dist/micropub/create.js.map +1 -0
  85. package/dist/micropub/endpoint.d.ts +21 -0
  86. package/dist/micropub/endpoint.d.ts.map +1 -0
  87. package/dist/micropub/endpoint.js +353 -0
  88. package/dist/micropub/endpoint.js.map +1 -0
  89. package/dist/micropub/media.d.ts +14 -0
  90. package/dist/micropub/media.d.ts.map +1 -0
  91. package/dist/micropub/media.js +93 -0
  92. package/dist/micropub/media.js.map +1 -0
  93. package/dist/micropub/update.d.ts +44 -0
  94. package/dist/micropub/update.d.ts.map +1 -0
  95. package/dist/micropub/update.js +133 -0
  96. package/dist/micropub/update.js.map +1 -0
  97. package/dist/web/context.d.ts +42 -3
  98. package/dist/web/context.d.ts.map +1 -1
  99. package/dist/web/context.js +41 -1
  100. package/dist/web/context.js.map +1 -1
  101. package/dist/web/render.d.ts +15 -0
  102. package/dist/web/render.d.ts.map +1 -1
  103. package/dist/web/render.js +16 -0
  104. package/dist/web/render.js.map +1 -1
  105. package/dist/web/routes.js +2 -2
  106. package/dist/webmention/index.d.ts +2 -0
  107. package/dist/webmention/index.d.ts.map +1 -1
  108. package/dist/webmention/index.js +1 -0
  109. package/dist/webmention/index.js.map +1 -1
  110. package/dist/webmention/service.d.ts.map +1 -1
  111. package/dist/webmention/service.js +96 -18
  112. package/dist/webmention/service.js.map +1 -1
  113. package/dist/webmention/syndication.d.ts +135 -0
  114. package/dist/webmention/syndication.d.ts.map +1 -0
  115. package/dist/webmention/syndication.js +337 -0
  116. package/dist/webmention/syndication.js.map +1 -0
  117. package/package.json +1 -1
  118. package/themes/default/README.md +40 -9
  119. package/themes/default/layouts/post.njk +19 -6
  120. package/themes/default/partials/citations.njk +17 -0
  121. package/themes/default/partials/jsonld.njk +12 -1
  122. package/themes/default/partials/photos.njk +10 -0
  123. package/themes/default/partials/post-list.njk +7 -0
  124. package/themes/default/src/style.css +9 -0
  125. package/themes/default/static/style.css +16 -0
package/README.md CHANGED
@@ -1185,6 +1185,113 @@ answers 404 or 410 — takes it away. Closing rules do not apply: a post that
1185
1185
  stopped taking comments still hears about a page that links to it, exactly as it
1186
1186
  still hears a fediverse reply.
1187
1187
 
1188
+ ### Syndication targets
1189
+
1190
+ Some services copy a post when it links to them and sends them a webmention,
1191
+ and answer with the address of the copy. IndieNews lists posts about the
1192
+ IndieWeb; Bridgy Publish posts to Mastodon, Bluesky, GitHub and Flickr. The
1193
+ CMS knows none of them by name. A site lists the ones it uses on the admin's
1194
+ **Posts > Syndication** screen (`/admin/syndication`), which reads
1195
+ and writes `content/_data/syndicationTargets.json`. The file stays the source of
1196
+ truth, so it can still be edited by hand, and it looks like this:
1197
+
1198
+ ```json
1199
+ [
1200
+ {
1201
+ "id": "indienews",
1202
+ "name": "IndieNews",
1203
+ "url": "https://news.indieweb.org/en",
1204
+ "tag": "indienews"
1205
+ },
1206
+ {
1207
+ "id": "mastodon",
1208
+ "name": "Mastodon",
1209
+ "url": "https://brid.gy/publish/mastodon"
1210
+ }
1211
+ ]
1212
+ ```
1213
+
1214
+ | Key | What it is |
1215
+ | ----------- | --------------------------------------------------------------------------------------------- |
1216
+ | `id` | What a post's `syndicate-to` lists. Letters, digits, `.`, `-` and `_`. |
1217
+ | `name` | What the editor's checkbox and the post's link say. |
1218
+ | `url` | The page the post links to and sends its webmention to. May hold `{lang}`. |
1219
+ | `tag` | Optional. A post carrying this tag is sent to the target without listing it. |
1220
+ | `languages` | Optional. The language tags the target takes posts in. A post in any other language skips it. |
1221
+
1222
+ An entry missing any of the first three, repeating an id, or with a `languages`
1223
+ value that is not a list of language tags, is ignored. It is reported in the log
1224
+ when the site starts, and the Syndication screen shows it with its problem so it
1225
+ can be fixed or removed there. No file means no targets.
1226
+
1227
+ The screen has a panel for each entry, in file order, and an **Add a target**
1228
+ form. A save is checked by the same rules the file is read by: a field that
1229
+ breaks one is marked on the form and the file is left as it was. A save that
1230
+ passes is written atomically, and the editor's **Syndicate to** checkboxes and
1231
+ Micropub's `q=syndicate-to` offer the change on the next request. A save or
1232
+ remove is refused when the entry changed in the file since the page was drawn.
1233
+ A file that is not a JSON list is shown with its problem and never saved over.
1234
+ To keep the default theme's old IndieNews link, add a target with the id
1235
+ `indienews`, the url `https://news.indieweb.org/{lang}` and the tag `indienews`.
1236
+
1237
+ Some targets have a page per language. IndieNews has `news.indieweb.org/en`,
1238
+ `/de`, `/fr` and more. Declare one target and put `{lang}` where the language
1239
+ goes:
1240
+
1241
+ ```json
1242
+ {
1243
+ "id": "indienews",
1244
+ "name": "IndieNews",
1245
+ "url": "https://news.indieweb.org/{lang}",
1246
+ "tag": "indienews",
1247
+ "languages": ["en", "de", "fr"]
1248
+ }
1249
+ ```
1250
+
1251
+ The CMS fills `{lang}` with the post's language: its `lang` front matter, or the
1252
+ site's language when the post names none. With `languages`, a post in a
1253
+ language the list does not name neither links to the target nor notifies it. A
1254
+ regional tag counts as its language, so a `de-AT` post goes to
1255
+ `news.indieweb.org/de`, and `{lang}` takes the tag from the list. Without
1256
+ `languages`, `{lang}` takes the post's own tag as written, region and all.
1257
+
1258
+ A post selects targets in one of two ways:
1259
+
1260
+ - **By listing them.** The post editor has a **Syndicate to** checkbox for each
1261
+ target, and saving writes the ticked ids into the front matter as
1262
+ `syndicate-to: [indienews, mastodon]`. An id no target declares is kept as
1263
+ written.
1264
+ - **By its tags.** A post tagged `indienews` is sent to the target whose `tag`
1265
+ is `indienews`, as the WordPress IndieNews plugin did.
1266
+
1267
+ The default theme links to each selected target at the start of the Published
1268
+ line, inside the post's `h-entry`, as `<a class="u-syndication"
1269
+ href="https://news.indieweb.org/en">IndieNews</a>`. That link is what IndieNews
1270
+ and Bridgy Publish check for when they fetch the post. When the post is
1271
+ published or updated, the sender adds each selected target to the pages it
1272
+ notifies, the same way it adds the post a reply answers. Nothing is sent while a
1273
+ page is served.
1274
+
1275
+ A target that answers `201` or `202` with a `Location` header has made a copy.
1276
+ The address is kept in `content/_data/syndication.json`, keyed by the post's
1277
+ permalink (decision-26), and the theme prints it after the dates as **Also on**
1278
+ with a `u-syndication` link. A copy made by hand goes in the post's own front
1279
+ matter, and is printed the same way:
1280
+
1281
+ ```yaml
1282
+ syndication:
1283
+ - https://mastodon.social/@me/113000000000000000
1284
+ ```
1285
+
1286
+ Removing a target from `syndicate-to`, or removing its tag, removes the link.
1287
+ The sender then tells the target, as it tells any page a post stops linking to,
1288
+ and the copy is dropped from `syndication.json`. IndieNews takes the post down
1289
+ when it finds the link gone. Drafting or trashing the post does the same.
1290
+
1291
+ The posts table on `/admin/federation` lists each selected target with how its
1292
+ last webmention went and a link to its copy, and **Resend** sends to the targets
1293
+ again.
1294
+
1188
1295
  ## Email
1189
1296
 
1190
1297
  The CMS sends its mail through one service and one seam. Everything that will
@@ -64,6 +64,28 @@
64
64
  </div>
65
65
 
66
66
  {% if kind.type == "post" %}
67
+ <fieldset class="admin-editor-enclosure admin-editor-photos">
68
+ <legend>Photos</legend>
69
+ <p class="admin-hint">
70
+ Each is an image in the media library or an address starting https://. Leave the alt text
71
+ empty to use the media library’s. Empty an address to remove that photo.
72
+ </p>
73
+ {% for row in photoRows %}
74
+ {% set n = loop.index0 %}
75
+ <fieldset class="admin-editor-alternate">
76
+ <legend>{% if row.url %}Photo {{ loop.index }}{% else %}Add a photo{% endif %}</legend>
77
+ <label for="editor-photo-url-{{ n }}">Address</label>
78
+ <input id="editor-photo-url-{{ n }}" name="{{ photoFields.url }}{{ n }}" type="text" value="{{ row.url }}" list="editor-photo-uploads" autocomplete="off" />
79
+ <label for="editor-photo-alt-{{ n }}">Alt text</label>
80
+ <input id="editor-photo-alt-{{ n }}" name="{{ photoFields.alt }}{{ n }}" type="text" value="{{ row.alt }}"{% if row.libraryAlt %} placeholder="{{ row.libraryAlt }}"{% endif %} />
81
+ </fieldset>
82
+ {% endfor %}
83
+ <datalist id="editor-photo-uploads">
84
+ {% for url in photoChoices %}
85
+ <option value="{{ url }}"></option>
86
+ {% endfor %}
87
+ </datalist>
88
+ </fieldset>
67
89
  <fieldset class="admin-editor-enclosure">
68
90
  <legend>Recording</legend>
69
91
 
@@ -167,6 +189,16 @@
167
189
  <label for="editor-in-reply-to">In reply to</label>
168
190
  <input id="editor-in-reply-to" name="in-reply-to" type="url" value="{{ form.inReplyTo }}" />
169
191
  <p class="admin-hint">The address of the post this one answers. Leave it empty for a post that answers nobody.</p>
192
+
193
+ {#- The mf2 `like-of`, `repost-of` and `bookmark-of` (TASK-169): each
194
+ cites a page the way a reply does, and sends it a webmention. -#}
195
+ <label for="editor-like-of">Like of</label>
196
+ <input id="editor-like-of" name="like-of" type="url" value="{{ form.likeOf }}" />
197
+ <label for="editor-repost-of">Repost of</label>
198
+ <input id="editor-repost-of" name="repost-of" type="url" value="{{ form.repostOf }}" />
199
+ <label for="editor-bookmark-of">Bookmark of</label>
200
+ <input id="editor-bookmark-of" name="bookmark-of" type="url" value="{{ form.bookmarkOf }}" />
201
+ <p class="admin-hint">The address of a post this one likes, reposts or bookmarks. Leave them empty for a post that cites nothing.</p>
170
202
  {% endif %}
171
203
 
172
204
  <label for="editor-description">Description</label>
@@ -210,6 +242,25 @@
210
242
  </p>
211
243
  {% endif %}
212
244
 
245
+ {% if kind.type == "post" and syndicationTargets.length %}
246
+ {# TASK-155: the targets content/_data/syndicationTargets.json declares.
247
+ A target whose tag the post carries is selected without a tick. #}
248
+ <fieldset class="admin-editor-syndication">
249
+ <legend>Syndicate to</legend>
250
+ {% for target in syndicationTargets %}
251
+ <p class="admin-check">
252
+ <input id="editor-{{ target.field }}" name="{{ target.field }}" type="checkbox" value="1"{% if target.checked %} checked{% endif %} />
253
+ <label for="editor-{{ target.field }}">{{ target.name }}</label>
254
+ </p>
255
+ {% endfor %}
256
+ <p class="admin-hint">
257
+ Writes <code>syndicate-to:</code> into the front matter. The post links to each target and
258
+ sends it a webmention when it is published or updated. A post tagged with a target’s tag is
259
+ sent there without a tick.
260
+ </p>
261
+ </fieldset>
262
+ {% endif %}
263
+
213
264
  {# What this document says about comments, if anything. Three answers
214
265
  rather than two: the site's own rules, always open, always closed. #}
215
266
  <label for="editor-comments">Comments</label>
@@ -0,0 +1,110 @@
1
+ {# /admin/syndication: the services a post can be sent to, which is
2
+ content/_data/syndicationTargets.json itself.
3
+
4
+ One panel per entry the file holds, in file order, the broken ones too:
5
+ an entry that declares nothing says why, so it can be fixed or removed here.
6
+ Each panel saves or removes its own entry, and the Add form at the bottom
7
+ appends one. A file that is not a JSON list is shown with its problem and
8
+ no forms, because saving over it would lose what it holds. #}
9
+ {% extends "layouts/shell.njk" %}
10
+ {% import "components/fields.njk" as field %}
11
+
12
+ {% block title %}{{ heading }}{% endblock %}
13
+
14
+ {# The five boxes of one target, ids prefixed with the panel's key. #}
15
+ {% macro boxes(panel, fields) %}
16
+ {{ field.text(panel.key + '-id', fields.id, 'Id', panel.form.id, error=panel.problems.id,
17
+ spellcheck='false', required=true,
18
+ hint='What a post&rsquo;s <code>syndicate-to</code> lists and what Micropub offers as the uid. Letters, digits, <code>.</code>, <code>-</code> and <code>_</code>.') }}
19
+ {{ field.text(panel.key + '-name', fields.name, 'Name', panel.form.name, error=panel.problems.name,
20
+ required=true, hint='What the editor&rsquo;s checkbox and the post&rsquo;s link say.') }}
21
+ {{ field.text(panel.key + '-url', fields.url, 'URL', panel.form.url, error=panel.problems.url,
22
+ inputmode='url', spellcheck='false', required=true,
23
+ hint='The page the post links to and sends its webmention to. Put <code>{lang}</code> where the post&rsquo;s language goes, as in <code>https://news.indieweb.org/{lang}</code>.') }}
24
+ {{ field.text(panel.key + '-tag', fields.tag, 'Tag', panel.form.tag, error=panel.problems.tag,
25
+ spellcheck='false',
26
+ hint='Optional. A post carrying this tag is sent here without listing the target.') }}
27
+ {{ field.text(panel.key + '-languages', fields.languages, 'Languages', panel.form.languages,
28
+ error=panel.problems.languages, spellcheck='false',
29
+ hint='Optional. The language tags this target takes posts in, separated by commas, such as <code>en, de</code>. Empty takes every language.') }}
30
+ {% endmacro %}
31
+
32
+ {% block content %}
33
+ <h1>{{ heading }}</h1>
34
+
35
+ {% if hasProblems %}
36
+ {% call field.summary('Nothing was saved. Fix the fields marked below and try again.') %}
37
+ {% for panel in panels.concat([adding]) %}
38
+ {% if panel.key == refusedKey %}
39
+ {{ field.problem(panel.key + '-id', panel.problems.id) }}
40
+ {{ field.problem(panel.key + '-name', panel.problems.name) }}
41
+ {{ field.problem(panel.key + '-url', panel.problems.url) }}
42
+ {{ field.problem(panel.key + '-tag', panel.problems.tag) }}
43
+ {{ field.problem(panel.key + '-languages', panel.problems.languages) }}
44
+ {% endif %}
45
+ {% endfor %}
46
+ {% endcall %}
47
+ {% endif %}
48
+
49
+ <p class="admin-hint">
50
+ Services such as IndieNews and Bridgy Publish copy a post when it links to them and sends
51
+ them a webmention. Each target listed here gets a checkbox in the post editor and is offered
52
+ to Micropub clients. They are kept in <code>content/{{ file }}</code>, which this screen reads
53
+ and writes, so the file can still be edited by hand.
54
+ </p>
55
+
56
+ {% if fileProblem %}
57
+ <div class="admin-error" role="alert">
58
+ <p>{{ fileProblem }} No target is offered until it is fixed. Fix or remove the file by hand;
59
+ this screen will not save over it, because that would lose what it holds.</p>
60
+ </div>
61
+ {% else %}
62
+ <h2>Targets</h2>
63
+
64
+ {% if panels.length %}
65
+ <ul class="admin-menu-boxes">
66
+ {% for panel in panels %}
67
+ <li class="admin-menu-box">
68
+ <h3>{{ panel.form.name or ('Entry ' + (panel.index + 1)) }}</h3>
69
+ {% if panel.fileProblems.length %}
70
+ {% for problem in panel.fileProblems %}
71
+ <p class="admin-field-error">This entry is ignored. {{ problem }}</p>
72
+ {% endfor %}
73
+ {% else %}
74
+ <p class="admin-menu-name"><code>{{ panel.form.id }}</code></p>
75
+ {% endif %}
76
+
77
+ <form method="post" action="{{ saveUrl }}">
78
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
79
+ <input type="hidden" name="{{ fields.entry }}" value="{{ panel.index }}" />
80
+ <input type="hidden" name="{{ fields.was }}" value="{{ panel.was }}" />
81
+ {{ boxes(panel, fields) }}
82
+ <p class="admin-menu-buttons">
83
+ <button type="submit">Save target</button>
84
+ </p>
85
+ </form>
86
+
87
+ <form class="admin-menu-delete" method="post" action="{{ deleteUrl }}">
88
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
89
+ <input type="hidden" name="{{ fields.entry }}" value="{{ panel.index }}" />
90
+ <input type="hidden" name="{{ fields.was }}" value="{{ panel.was }}" />
91
+ <button type="submit">Remove this target</button>
92
+ </form>
93
+ </li>
94
+ {% endfor %}
95
+ </ul>
96
+ {% else %}
97
+ <p class="admin-hint">This site declares no targets, so the editor offers none.</p>
98
+ {% endif %}
99
+
100
+ <h2>Add a target</h2>
101
+
102
+ <form class="admin-menu-add" method="post" action="{{ addUrl }}">
103
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
104
+ {{ boxes(adding, fields) }}
105
+ <p class="admin-menu-buttons">
106
+ <button type="submit">Add target</button>
107
+ </p>
108
+ </form>
109
+ {% endif %}
110
+ {% endblock %}
@@ -214,6 +214,13 @@
214
214
  {% else %}
215
215
  <span class="admin-empty">—</span>
216
216
  {% endif %}
217
+ {% if row.syndication.length %}
218
+ <ul class="admin-syndication">
219
+ {% for target in row.syndication %}
220
+ <li>{{ target.name }}: {% if target.status == "failed" %}<span class="admin-status admin-status-failed">failed</span>{% if target.error %} ({{ target.error }}){% endif %}{% elif target.status == "none" %}takes none{% elif target.status %}sent{% else %}not sent yet{% endif %}{% if target.copy %}, <a href="{{ target.copy }}">copy</a>{% endif %}</li>
221
+ {% endfor %}
222
+ </ul>
223
+ {% endif %}
217
224
  </td>
218
225
  <td class="admin-actions">
219
226
  <form method="post" action="{{ resendUrl }}">
@@ -496,6 +496,12 @@ button[type='submit']:hover,
496
496
  border: 1px solid var(--admin-border);
497
497
  }
498
498
 
499
+ .admin-editor-syndication {
500
+ margin: 1.25rem 0 0;
501
+ padding: 0.75rem 1rem 1rem;
502
+ border: 1px solid var(--admin-border);
503
+ }
504
+
499
505
  .admin-editor-enclosure-heading {
500
506
  margin: 1.25rem 0 0;
501
507
  font-size: 1em;
@@ -880,6 +886,11 @@ button[type='submit']:hover,
880
886
  font-weight: 600;
881
887
  }
882
888
 
889
+ .admin-syndication {
890
+ margin: 0.25rem 0 0;
891
+ padding-left: 1rem;
892
+ }
893
+
883
894
  .admin-inbox-when {
884
895
  color: var(--admin-muted);
885
896
  }
@@ -1,8 +1,14 @@
1
1
  import type { Context, Hono } from 'hono';
2
2
  import type { Document, DocumentType } from '../content/document.ts';
3
+ import type { Enclosure } from '../content/enclosure.ts';
4
+ import type { Photo } from '../content/photo.ts';
3
5
  import type { ContentStore, ListAllOptions } from '../content/store.ts';
6
+ import type { ResolvedConfig } from '../config.ts';
7
+ import type { DocumentChange } from '../content/sync.ts';
4
8
  import type { GeekityEnv } from '../env.ts';
9
+ import type { UndescribedImage } from '../images/alt-text.ts';
5
10
  import type { EnclosureForm } from './enclosure-field.ts';
11
+ import type { PhotoRow } from './photo-field.ts';
6
12
  /**
7
13
  * Everything that differs between the posts screens and the pages screens.
8
14
  *
@@ -107,6 +113,55 @@ export interface MountDocumentScreensOptions {
107
113
  * date and a taxonomy, and which section of the navigation marks itself.
108
114
  */
109
115
  export declare function mountDocumentScreens(app: Hono<GeekityEnv>, options: MountDocumentScreensOptions): void;
116
+ /** What a write needs from the site, as plain values rather than a request. */
117
+ export interface DocumentSite {
118
+ readonly store: ContentStore;
119
+ readonly config: ResolvedConfig;
120
+ readonly announce: (change: DocumentChange) => Promise<void>;
121
+ /** The username of whoever is writing: the author a new document starts on. */
122
+ readonly writer: string | undefined;
123
+ }
124
+ /** What {@link writeDocument} is asked to write. */
125
+ export interface DocumentWrite {
126
+ readonly kind: DocumentKind;
127
+ /** The document being replaced, or `undefined` when one is being made. */
128
+ readonly document: Document | undefined;
129
+ readonly form: EditorForm;
130
+ /** Whether it is saved as a draft, which the editor's buttons decide. */
131
+ readonly draft: boolean;
132
+ }
133
+ /** What came of a {@link writeDocument}. */
134
+ export type WriteOutcome = {
135
+ readonly outcome: 'saved';
136
+ readonly saved: Document;
137
+ /** Images published without alt text, which the editor warns about. */
138
+ readonly undescribed: readonly UndescribedImage[];
139
+ } | {
140
+ readonly outcome: 'refused';
141
+ readonly message: string;
142
+ } | {
143
+ readonly outcome: 'conflict';
144
+ /** The document as the form loaded it, which only an edit has. */
145
+ readonly document: Document;
146
+ /** The recording and the photos the form resolved to. */
147
+ readonly media: ResolvedMedia;
148
+ readonly conflict: Conflict;
149
+ };
150
+ /** The post's recording and photos, as a form resolved them against the media library. */
151
+ interface ResolvedMedia {
152
+ readonly recording: Enclosure | undefined;
153
+ readonly photos: readonly Photo[];
154
+ }
155
+ /**
156
+ * The write path behind the editor and Micropub (TASK-164): one form in, one
157
+ * file out, announced to every subscriber.
158
+ *
159
+ * The order matters: the form is checked before anything is touched, the
160
+ * on-disk file is re-hashed and compared with the hash the form loaded with
161
+ * (doc-1's conflict rule), and only then is a file written. A write that is
162
+ * refused leaves the content directory exactly as it was.
163
+ */
164
+ export declare function writeDocument(site: DocumentSite, write: DocumentWrite): Promise<WriteOutcome>;
110
165
  /**
111
166
  * A comma-separated taxonomy field as a list, without the blanks and the
112
167
  * repeats. Tags and categories are both entered this way.
@@ -135,6 +190,32 @@ export interface AuthorChoice {
135
190
  * editor does and what {@link blankForm} deliberately leaves to a request.
136
191
  */
137
192
  export declare function authorChoices(c: Context<GeekityEnv>, current: string): AuthorChoice[];
193
+ /** The file as it is now, when it is not the file the form was filled in from. */
194
+ interface Conflict {
195
+ /** The text of the file on disk. */
196
+ current: string;
197
+ /** Its hash, which is what a resubmission has to carry to go through. */
198
+ hash: string;
199
+ }
200
+ /** What moving a document's file needs from the site. */
201
+ export interface MoveSite {
202
+ readonly store: ContentStore;
203
+ readonly contentDir: string;
204
+ readonly announce: (change: DocumentChange) => Promise<void>;
205
+ }
206
+ /**
207
+ * Move a document into `content/_trash/` or back out of it, the move behind
208
+ * the editor's trash and restore and Micropub's delete and undelete
209
+ * (TASK-167). The document is the moved one, or `undefined` when its file
210
+ * could not be moved.
211
+ *
212
+ * The trash mirrors the content tree — `posts/2026-03-04-x.md` becomes
213
+ * `_trash/posts/2026-03-04-x.md` — so restoring is the same move backwards and
214
+ * needs to remember nothing. The index is corrected as soon as the file has
215
+ * landed rather than waiting for the watcher, so the public site stops or
216
+ * starts serving the document with this request (doc-1).
217
+ */
218
+ export declare function moveDocumentFile(site: MoveSite, document: Document, action: 'trash' | 'restore'): Promise<Document | undefined>;
138
219
  /**
139
220
  * A submitted return path, when it is one this admin could have rendered.
140
221
  *
@@ -142,6 +223,15 @@ export declare function authorChoices(c: Context<GeekityEnv>, current: string):
142
223
  * as another host, so the value cannot be used to bounce somebody off the site.
143
224
  */
144
225
  export declare function returnPath(value: string): string | undefined;
226
+ /**
227
+ * The editor field each citing property fills (TASK-169). The form submits it
228
+ * under the property's own name, as `in-reply-to` is.
229
+ */
230
+ export declare const CITATION_FIELDS: {
231
+ readonly 'repost-of': 'repostOf';
232
+ readonly 'like-of': 'likeOf';
233
+ readonly 'bookmark-of': 'bookmarkOf';
234
+ };
145
235
  /** The editor's fields, as strings, which is what a form has. */
146
236
  export interface EditorForm {
147
237
  title: string;
@@ -162,6 +252,12 @@ export interface EditorForm {
162
252
  author: string;
163
253
  /** The post this one replies to, the mf2 `in-reply-to`. Posts only. */
164
254
  inReplyTo: string;
255
+ /** The post this one reposts, the mf2 `repost-of` (TASK-169). Posts only. */
256
+ repostOf: string;
257
+ /** The post this one likes, the mf2 `like-of` (TASK-169). Posts only. */
258
+ likeOf: string;
259
+ /** The page this one bookmarks, the mf2 `bookmark-of` (TASK-169). Posts only. */
260
+ bookmarkOf: string;
165
261
  /** The language it is written in, the `lang` key; empty for the site's. */
166
262
  lang: string;
167
263
  draft: boolean;
@@ -182,6 +278,10 @@ export interface EditorForm {
182
278
  comments: string;
183
279
  /** The post's recording (TASK-213). Posts only; blank on a page. */
184
280
  enclosure: EnclosureForm;
281
+ /** The post's photos (TASK-166), without the blank row the editor adds. Posts only. */
282
+ photos: PhotoRow[];
283
+ /** The ids of the declared syndication targets it selects (TASK-155). Posts only. */
284
+ syndicateTo: string[];
185
285
  body: string;
186
286
  /** The hash of the file the form was filled in from; empty for a new one. */
187
287
  hash: string;
@@ -248,4 +348,5 @@ export declare const PAGE_ROLE_LABELS: {
248
348
  };
249
349
  /** The URL of one view of a listing. Page one is the listing's own URL. */
250
350
  export declare function listingUrl(kind: DocumentKind, filter: DocumentFilter, page?: number): string;
351
+ export {};
251
352
  //# sourceMappingURL=documents.d.ts.map
@@ -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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"}