@geekity/cms 0.39.0 → 0.41.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 (297) hide show
  1. package/admin/pages/appearance/look.njk +135 -0
  2. package/admin/pages/dashboard/home.njk +9 -3
  3. package/admin/pages/documents/conflict.njk +35 -10
  4. package/admin/pages/documents/editor.njk +28 -0
  5. package/admin/pages/documents/list.njk +44 -1
  6. package/admin/pages/settings/general.njk +4 -0
  7. package/admin/pages/users/edit.njk +1 -1
  8. package/admin/src/admin.css +32 -0
  9. package/admin/static/admin.css +197 -3
  10. package/admin/static/dashboard.js +51 -23
  11. package/admin/static/editor.js +19 -19
  12. package/dist/admin/autosave-client.d.ts +85 -0
  13. package/dist/admin/autosave-client.d.ts.map +1 -0
  14. package/dist/admin/autosave-client.js +218 -0
  15. package/dist/admin/autosave-client.js.map +1 -0
  16. package/dist/admin/autosave.d.ts +11 -0
  17. package/dist/admin/autosave.d.ts.map +1 -0
  18. package/dist/admin/autosave.js +76 -0
  19. package/dist/admin/autosave.js.map +1 -0
  20. package/dist/admin/dashboard.d.ts +2 -2
  21. package/dist/admin/dashboard.d.ts.map +1 -1
  22. package/dist/admin/dashboard.js +23 -15
  23. package/dist/admin/dashboard.js.map +1 -1
  24. package/dist/admin/documents.d.ts.map +1 -1
  25. package/dist/admin/documents.js +205 -16
  26. package/dist/admin/documents.js.map +1 -1
  27. package/dist/admin/flash.d.ts +2 -0
  28. package/dist/admin/flash.d.ts.map +1 -1
  29. package/dist/admin/flash.js +12 -0
  30. package/dist/admin/flash.js.map +1 -1
  31. package/dist/admin/focus-mode.d.ts +39 -0
  32. package/dist/admin/focus-mode.d.ts.map +1 -0
  33. package/dist/admin/focus-mode.js +81 -0
  34. package/dist/admin/focus-mode.js.map +1 -0
  35. package/dist/admin/formatting.d.ts +0 -2
  36. package/dist/admin/formatting.d.ts.map +1 -1
  37. package/dist/admin/formatting.js +0 -8
  38. package/dist/admin/formatting.js.map +1 -1
  39. package/dist/admin/look.d.ts +12 -0
  40. package/dist/admin/look.d.ts.map +1 -0
  41. package/dist/admin/look.js +179 -0
  42. package/dist/admin/look.js.map +1 -0
  43. package/dist/admin/menu.d.ts.map +1 -1
  44. package/dist/admin/menu.js +5 -3
  45. package/dist/admin/menu.js.map +1 -1
  46. package/dist/admin/preview.d.ts.map +1 -1
  47. package/dist/admin/preview.js +1 -0
  48. package/dist/admin/preview.js.map +1 -1
  49. package/dist/admin/routes.d.ts.map +1 -1
  50. package/dist/admin/routes.js +6 -4
  51. package/dist/admin/routes.js.map +1 -1
  52. package/dist/admin/settings-fields.d.ts +1 -1
  53. package/dist/admin/settings-fields.d.ts.map +1 -1
  54. package/dist/admin/settings-fields.js +1 -0
  55. package/dist/admin/settings-fields.js.map +1 -1
  56. package/dist/admin/settings-general.d.ts.map +1 -1
  57. package/dist/admin/settings-general.js +1 -0
  58. package/dist/admin/settings-general.js.map +1 -1
  59. package/dist/admin/templates.d.ts +1 -0
  60. package/dist/admin/templates.d.ts.map +1 -1
  61. package/dist/admin/templates.js +2 -1
  62. package/dist/admin/templates.js.map +1 -1
  63. package/dist/admin/users.d.ts.map +1 -1
  64. package/dist/admin/users.js +2 -0
  65. package/dist/admin/users.js.map +1 -1
  66. package/dist/admin/widget-fediverse.js +1 -1
  67. package/dist/admin/widget-posting.d.ts.map +1 -1
  68. package/dist/admin/widget-posting.js +4 -75
  69. package/dist/admin/widget-posting.js.map +1 -1
  70. package/dist/admin/widget-scheduled.js +1 -1
  71. package/dist/admin/widget-top-posts.js +1 -1
  72. package/dist/autosave/index.d.ts +5 -0
  73. package/dist/autosave/index.d.ts.map +1 -0
  74. package/dist/autosave/index.js +3 -0
  75. package/dist/autosave/index.js.map +1 -0
  76. package/dist/autosave/key.d.ts +13 -0
  77. package/dist/autosave/key.d.ts.map +1 -0
  78. package/dist/autosave/key.js +29 -0
  79. package/dist/autosave/key.js.map +1 -0
  80. package/dist/autosave/store.d.ts +23 -0
  81. package/dist/autosave/store.d.ts.map +1 -0
  82. package/dist/autosave/store.js +114 -0
  83. package/dist/autosave/store.js.map +1 -0
  84. package/dist/cli.js +1 -1
  85. package/dist/cli.js.map +1 -1
  86. package/dist/cms/app.d.ts.map +1 -1
  87. package/dist/cms/app.js +1 -0
  88. package/dist/cms/app.js.map +1 -1
  89. package/dist/cms/boot.d.ts.map +1 -1
  90. package/dist/cms/boot.js +2 -0
  91. package/dist/cms/boot.js.map +1 -1
  92. package/dist/cms/create.d.ts +6 -0
  93. package/dist/cms/create.d.ts.map +1 -1
  94. package/dist/cms/create.js +1 -0
  95. package/dist/cms/create.js.map +1 -1
  96. package/dist/cms/server-error.d.ts.map +1 -1
  97. package/dist/cms/server-error.js +6 -6
  98. package/dist/cms/server-error.js.map +1 -1
  99. package/dist/cms/services.d.ts +2 -0
  100. package/dist/cms/services.d.ts.map +1 -1
  101. package/dist/cms/services.js +9 -0
  102. package/dist/cms/services.js.map +1 -1
  103. package/dist/{micropub/content.d.ts → content/html-to-markdown.d.ts} +1 -1
  104. package/dist/content/html-to-markdown.d.ts.map +1 -0
  105. package/dist/{micropub/content.js → content/html-to-markdown.js} +11 -9
  106. package/dist/content/html-to-markdown.js.map +1 -0
  107. package/dist/content/post-type.d.ts +1 -0
  108. package/dist/content/post-type.d.ts.map +1 -1
  109. package/dist/content/post-type.js +1 -1
  110. package/dist/content/post-type.js.map +1 -1
  111. package/dist/content/review.d.ts +14 -0
  112. package/dist/content/review.d.ts.map +1 -1
  113. package/dist/content/review.js +31 -8
  114. package/dist/content/review.js.map +1 -1
  115. package/dist/content/write.d.ts +8 -0
  116. package/dist/content/write.d.ts.map +1 -1
  117. package/dist/content/write.js +1 -1
  118. package/dist/content/write.js.map +1 -1
  119. package/dist/files/data-files.d.ts +2 -0
  120. package/dist/files/data-files.d.ts.map +1 -1
  121. package/dist/files/data-files.js +12 -0
  122. package/dist/files/data-files.js.map +1 -1
  123. package/dist/fonts/css.d.ts +25 -0
  124. package/dist/fonts/css.d.ts.map +1 -0
  125. package/dist/fonts/css.js +88 -0
  126. package/dist/fonts/css.js.map +1 -0
  127. package/dist/fonts/family.d.ts +23 -0
  128. package/dist/fonts/family.d.ts.map +1 -0
  129. package/dist/fonts/family.js +41 -0
  130. package/dist/fonts/family.js.map +1 -0
  131. package/dist/fonts/index.d.ts +10 -0
  132. package/dist/fonts/index.d.ts.map +1 -0
  133. package/dist/fonts/index.js +6 -0
  134. package/dist/fonts/index.js.map +1 -0
  135. package/dist/fonts/job.d.ts +20 -0
  136. package/dist/fonts/job.d.ts.map +1 -0
  137. package/dist/fonts/job.js +49 -0
  138. package/dist/fonts/job.js.map +1 -0
  139. package/dist/fonts/roles.d.ts +53 -0
  140. package/dist/fonts/roles.d.ts.map +1 -0
  141. package/dist/fonts/roles.js +64 -0
  142. package/dist/fonts/roles.js.map +1 -0
  143. package/dist/fonts/store.d.ts +69 -0
  144. package/dist/fonts/store.d.ts.map +1 -0
  145. package/dist/fonts/store.js +237 -0
  146. package/dist/fonts/store.js.map +1 -0
  147. package/dist/maintenance.d.ts +1 -0
  148. package/dist/maintenance.d.ts.map +1 -1
  149. package/dist/maintenance.js +1 -1
  150. package/dist/maintenance.js.map +1 -1
  151. package/dist/micropub/create.js +1 -1
  152. package/dist/micropub/create.js.map +1 -1
  153. package/dist/plugin.d.ts +1 -1
  154. package/dist/plugin.d.ts.map +1 -1
  155. package/dist/settings/registry.d.ts.map +1 -1
  156. package/dist/settings/registry.js +5 -1
  157. package/dist/settings/registry.js.map +1 -1
  158. package/dist/settings/types.d.ts +1 -1
  159. package/dist/settings/types.d.ts.map +1 -1
  160. package/dist/site/dashboard-layout.d.ts +33 -26
  161. package/dist/site/dashboard-layout.d.ts.map +1 -1
  162. package/dist/site/dashboard-layout.js +94 -39
  163. package/dist/site/dashboard-layout.js.map +1 -1
  164. package/dist/site/namespace.d.ts +5 -0
  165. package/dist/site/namespace.d.ts.map +1 -1
  166. package/dist/site/namespace.js +40 -1
  167. package/dist/site/namespace.js.map +1 -1
  168. package/dist/store/schema.d.ts.map +1 -1
  169. package/dist/store/schema.js +4 -0
  170. package/dist/store/schema.js.map +1 -1
  171. package/dist/store/sessions.d.ts +2 -1
  172. package/dist/store/sessions.d.ts.map +1 -1
  173. package/dist/store/sessions.js +30 -12
  174. package/dist/store/sessions.js.map +1 -1
  175. package/dist/theme/font-family.d.ts +13 -0
  176. package/dist/theme/font-family.d.ts.map +1 -0
  177. package/dist/theme/font-family.js +21 -0
  178. package/dist/theme/font-family.js.map +1 -0
  179. package/dist/theme/scheme-tokens.d.ts +50 -0
  180. package/dist/theme/scheme-tokens.d.ts.map +1 -0
  181. package/dist/theme/scheme-tokens.js +240 -0
  182. package/dist/theme/scheme-tokens.js.map +1 -0
  183. package/dist/theme/site-data.d.ts +12 -0
  184. package/dist/theme/site-data.d.ts.map +1 -1
  185. package/dist/theme/site-data.js +15 -0
  186. package/dist/theme/site-data.js.map +1 -1
  187. package/dist/theme/themes.d.ts +74 -0
  188. package/dist/theme/themes.d.ts.map +1 -1
  189. package/dist/theme/themes.js +100 -9
  190. package/dist/theme/themes.js.map +1 -1
  191. package/dist/util/file-size.d.ts +3 -0
  192. package/dist/util/file-size.d.ts.map +1 -0
  193. package/dist/util/file-size.js +9 -0
  194. package/dist/util/file-size.js.map +1 -0
  195. package/dist/web/asset-routes.d.ts.map +1 -1
  196. package/dist/web/asset-routes.js +2 -1
  197. package/dist/web/asset-routes.js.map +1 -1
  198. package/dist/web/conversation.d.ts +15 -0
  199. package/dist/web/conversation.d.ts.map +1 -1
  200. package/dist/web/conversation.js +10 -3
  201. package/dist/web/conversation.js.map +1 -1
  202. package/dist/web/document-page.d.ts.map +1 -1
  203. package/dist/web/document-page.js +11 -13
  204. package/dist/web/document-page.js.map +1 -1
  205. package/dist/web/embed.d.ts.map +1 -1
  206. package/dist/web/embed.js +4 -2
  207. package/dist/web/embed.js.map +1 -1
  208. package/dist/web/env.d.ts +2 -0
  209. package/dist/web/env.d.ts.map +1 -1
  210. package/dist/web/federation-routes.d.ts.map +1 -1
  211. package/dist/web/federation-routes.js +2 -1
  212. package/dist/web/federation-routes.js.map +1 -1
  213. package/dist/web/feed-routes.d.ts +4 -2
  214. package/dist/web/feed-routes.d.ts.map +1 -1
  215. package/dist/web/feed-routes.js +22 -4
  216. package/dist/web/feed-routes.js.map +1 -1
  217. package/dist/web/feed-source.d.ts +1 -1
  218. package/dist/web/feed-source.d.ts.map +1 -1
  219. package/dist/web/feed-source.js +1 -1
  220. package/dist/web/feed-source.js.map +1 -1
  221. package/dist/web/font-routes.d.ts +13 -0
  222. package/dist/web/font-routes.d.ts.map +1 -0
  223. package/dist/web/font-routes.js +27 -0
  224. package/dist/web/font-routes.js.map +1 -0
  225. package/dist/web/listing-pages.d.ts.map +1 -1
  226. package/dist/web/listing-pages.js +12 -14
  227. package/dist/web/listing-pages.js.map +1 -1
  228. package/dist/web/maintenance.d.ts.map +1 -1
  229. package/dist/web/maintenance.js +12 -8
  230. package/dist/web/maintenance.js.map +1 -1
  231. package/dist/web/manifest.d.ts +8 -5
  232. package/dist/web/manifest.d.ts.map +1 -1
  233. package/dist/web/manifest.js +10 -7
  234. package/dist/web/manifest.js.map +1 -1
  235. package/dist/web/markdown-conversation.d.ts +72 -0
  236. package/dist/web/markdown-conversation.d.ts.map +1 -0
  237. package/dist/web/markdown-conversation.js +63 -0
  238. package/dist/web/markdown-conversation.js.map +1 -0
  239. package/dist/web/markdown-page.d.ts +39 -0
  240. package/dist/web/markdown-page.d.ts.map +1 -1
  241. package/dist/web/markdown-page.js +5 -2
  242. package/dist/web/markdown-page.js.map +1 -1
  243. package/dist/web/markdown-representation.d.ts +31 -1
  244. package/dist/web/markdown-representation.d.ts.map +1 -1
  245. package/dist/web/markdown-representation.js +161 -8
  246. package/dist/web/markdown-representation.js.map +1 -1
  247. package/dist/web/negotiate.d.ts +3 -2
  248. package/dist/web/negotiate.d.ts.map +1 -1
  249. package/dist/web/negotiate.js +7 -3
  250. package/dist/web/negotiate.js.map +1 -1
  251. package/dist/web/oembed.d.ts +17 -3
  252. package/dist/web/oembed.d.ts.map +1 -1
  253. package/dist/web/oembed.js +68 -26
  254. package/dist/web/oembed.js.map +1 -1
  255. package/dist/web/render.d.ts +64 -5
  256. package/dist/web/render.d.ts.map +1 -1
  257. package/dist/web/render.js +185 -10
  258. package/dist/web/render.js.map +1 -1
  259. package/dist/web/request.d.ts +0 -3
  260. package/dist/web/request.d.ts.map +1 -1
  261. package/dist/web/request.js +0 -7
  262. package/dist/web/request.js.map +1 -1
  263. package/dist/web/routes.d.ts.map +1 -1
  264. package/dist/web/routes.js +13 -7
  265. package/dist/web/routes.js.map +1 -1
  266. package/dist/web/search.d.ts +6 -3
  267. package/dist/web/search.d.ts.map +1 -1
  268. package/dist/web/search.js +20 -5
  269. package/dist/web/search.js.map +1 -1
  270. package/dist/web/site-index.d.ts.map +1 -1
  271. package/dist/web/site-index.js +2 -1
  272. package/dist/web/site-index.js.map +1 -1
  273. package/dist/web/status-pages.d.ts +19 -0
  274. package/dist/web/status-pages.d.ts.map +1 -0
  275. package/dist/web/status-pages.js +44 -0
  276. package/dist/web/status-pages.js.map +1 -0
  277. package/dist/webmention/fetch-public.d.ts +6 -0
  278. package/dist/webmention/fetch-public.d.ts.map +1 -1
  279. package/dist/webmention/fetch-public.js +6 -1
  280. package/dist/webmention/fetch-public.js.map +1 -1
  281. package/package.json +1 -1
  282. package/themes/default/layouts/404.md.njk +8 -0
  283. package/themes/default/layouts/410.md.njk +7 -0
  284. package/themes/default/layouts/500.md.njk +8 -0
  285. package/themes/default/layouts/503.md.njk +9 -0
  286. package/themes/default/layouts/base.njk +28 -16
  287. package/themes/default/layouts/page.njk +4 -8
  288. package/themes/default/partials/bio.njk +1 -1
  289. package/themes/default/partials/contact-form.njk +1 -1
  290. package/themes/default/partials/markdown-footer.md.njk +121 -3
  291. package/themes/default/partials/markdown-header.md.njk +102 -0
  292. package/themes/default/partials/markdown-reply.md.njk +31 -0
  293. package/themes/default/src/style.css +326 -15
  294. package/themes/default/static/style.css +489 -3
  295. package/themes/default/theme.json +23 -1
  296. package/dist/micropub/content.d.ts.map +0 -1
  297. package/dist/micropub/content.js.map +0 -1
@@ -0,0 +1,135 @@
1
+ {% extends "layouts/shell.njk" %}
2
+ {% from "components/alert.njk" import alert %}
3
+ {% from "components/badge.njk" import badge %}
4
+ {% from "components/button.njk" import button %}
5
+ {% from "components/card.njk" import card, cardActions %}
6
+ {% import "components/fields.njk" as field %}
7
+
8
+ {% macro swatch(mode, colours) -%}
9
+ <span class="flex flex-col gap-1" aria-hidden="true">
10
+ <svg data-mode="{{ mode }}" viewBox="0 0 96 56" class="h-auto w-full" focusable="false">
11
+ <rect x="0.5" y="0.5" width="95" height="55" rx="6" fill="{{ colours.page }}" stroke="currentColor" stroke-opacity="0.3" />
12
+ <text x="10" y="35" font-size="20" font-weight="700" fill="{{ colours.text }}">Aa</text>
13
+ <circle cx="58" cy="28" r="9" fill="{{ colours.accent }}" />
14
+ <circle cx="80" cy="28" r="9" fill="{{ colours.secondary }}" />
15
+ </svg>
16
+ <span class="text-xs">{{ 'Light' if mode == 'light' else 'Dark' }}</span>
17
+ </span>
18
+ {%- endmacro %}
19
+
20
+ {% block title %}{{ heading }}{% endblock %}
21
+
22
+ {% block content %}
23
+ <h1 class="mb-6 text-2xl font-bold">{{ heading }}</h1>
24
+
25
+ <div class="flex max-w-5xl flex-col gap-6">
26
+ {% call card(title='Colours') %}
27
+ {% if problems %}
28
+ {% call alert(color='error') %}
29
+ <p>Nothing was changed. {{ problems | join(' ') }}</p>
30
+ {% endcall %}
31
+ {% endif %}
32
+
33
+ <form method="post" action="{{ coloursUrl }}">
34
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
35
+
36
+ {% if schemes.length %}
37
+ <fieldset class="fieldset">
38
+ <legend class="fieldset-legend">Colour scheme</legend>
39
+ <ul class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3">
40
+ {% for scheme in schemes %}
41
+ <li>
42
+ <label data-scheme-card class="card card-border cursor-pointer bg-base-100 has-checked:border-2 has-checked:border-primary">
43
+ <span class="card-body gap-3 p-4">
44
+ <span class="flex flex-wrap items-center gap-2">
45
+ <input type="radio" class="radio radio-sm" name="{{ fields.scheme }}" value="{{ scheme.name }}"{% if scheme.chosen %} checked{% endif %} />
46
+ <span class="font-semibold">{{ scheme.label }}</span>
47
+ {% if scheme.name == savedScheme %}{{ badge('In use', color='primary', style='soft') }}{% endif %}
48
+ </span>
49
+ {% if scheme.light and scheme.dark %}
50
+ <span class="grid grid-cols-2 gap-2">
51
+ {{ swatch('light', scheme.light) }}
52
+ {{ swatch('dark', scheme.dark) }}
53
+ </span>
54
+ {% endif %}
55
+ </span>
56
+ </label>
57
+ </li>
58
+ {% endfor %}
59
+ </ul>
60
+ <p class="label whitespace-normal">Each swatch shows the page, the text, the accent and the secondary colour.</p>
61
+ </fieldset>
62
+ {% else %}
63
+ <p>This theme has one palette, so there is no colour scheme to choose.</p>
64
+ {% endif %}
65
+
66
+ <fieldset class="fieldset">
67
+ <legend class="fieldset-legend">Mode</legend>
68
+ {% for mode in modes %}
69
+ <label class="label cursor-pointer justify-start gap-2">
70
+ <input type="radio" class="radio radio-sm" name="{{ fields.mode }}" value="{{ mode.value }}"{% if mode.chosen %} checked{% endif %} />
71
+ <span class="text-base-content">{{ mode.label }}</span>
72
+ </label>
73
+ {% endfor %}
74
+ <p class="label whitespace-normal">
75
+ {% if schemes.length %}
76
+ Follow the system draws light or dark as each reader's device is set.
77
+ {% else %}
78
+ The theme's stylesheet decides light and dark; the mode applies once the theme declares colour schemes.
79
+ {% endif %}
80
+ </p>
81
+ </fieldset>
82
+
83
+ {% call cardActions() %}
84
+ {{ button('Save colours', type='submit', color='primary') }}
85
+ {% endcall %}
86
+ </form>
87
+ {% endcall %}
88
+
89
+ {% call card(title='Fonts') %}
90
+ {% if fonts.length %}
91
+ {% for font in fonts %}{% if font.sample %}
92
+ <link rel="stylesheet" href="{{ font.sample.href }}" />
93
+ {% endif %}{% endfor %}
94
+ <style nonce="{{ cspNonce }}">
95
+ {% for font in fonts %}
96
+ [data-font-sample="{{ font.role }}"] { font-family: {% if font.sample %}"{{ font.sample.family }}", {% endif %}{{ font.fallback | safe }}; }
97
+ {% endfor %}
98
+ </style>
99
+
100
+ {% if fontProblems %}
101
+ {% call field.summary('The fonts were not saved. Fix the fields marked below.') %}
102
+ {% for font in fonts %}
103
+ {{ field.problem('font-' + font.role + ('-custom' if font.customChosen else ''), font.problem) }}
104
+ {% endfor %}
105
+ {% endcall %}
106
+ {% endif %}
107
+
108
+ <form method="post" action="{{ fontsUrl }}">
109
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
110
+ <div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
111
+ {% for font in fonts %}
112
+ <fieldset class="fieldset" data-font-role="{{ font.role }}">
113
+ <legend class="fieldset-legend">{{ font.label }}</legend>
114
+ <p data-font-sample="{{ font.role }}" class="text-xl">The quick brown fox jumps over the lazy dog.</p>
115
+ {% call field.select('font-' + font.role, font.fields.select, 'Font', error=(font.problem if not font.customChosen else '')) %}
116
+ {% for option in font.options %}
117
+ <option value="{{ option.value }}"{% if option.selected %} selected{% endif %}>{{ option.label }}</option>
118
+ {% endfor %}
119
+ {% endcall %}
120
+ {{ field.text('font-' + font.role + '-custom', font.fields.custom, 'Custom Google Fonts family', value=font.custom, error=(font.problem if font.customChosen else ''), hint='Used when Custom… is chosen. Type the family as Google Fonts names it, such as Bitter.', spellcheck='false', autocomplete='off') }}
121
+ </fieldset>
122
+ {% endfor %}
123
+ </div>
124
+ <p class="label mt-2 block whitespace-normal">Saving downloads each family from Google Fonts to this site, so readers load it from here. Body text needs regular, italic and bold; headings need bold.</p>
125
+
126
+ {% call cardActions() %}
127
+ {{ button('Save fonts', type='submit', color='primary') }}
128
+ {% endcall %}
129
+ </form>
130
+ {% else %}
131
+ <p>This theme declares no fonts to choose.</p>
132
+ {% endif %}
133
+ {% endcall %}
134
+ </div>
135
+ {% endblock %}
@@ -36,6 +36,8 @@
36
36
  {% for key in widget.shown %}<input type="hidden" name="shown" value="{{ key }}" />{% endfor %}
37
37
  {{ button('↑', type='submit', name='action', value='earlier', style='ghost', size='xs', shape='square', ariaLabel='Move ' + widget.title + ' earlier', disabled=widget.first) }}
38
38
  {{ button('↓', type='submit', name='action', value='later', style='ghost', size='xs', shape='square', ariaLabel='Move ' + widget.title + ' later', disabled=widget.last) }}
39
+ {{ button('←', type='submit', name='action', value='left', style='ghost', size='xs', shape='square', ariaLabel='Move ' + widget.title + ' to the left column', hidden=widget.side == 'left') }}
40
+ {{ button('→', type='submit', name='action', value='right', style='ghost', size='xs', shape='square', ariaLabel='Move ' + widget.title + ' to the right column', hidden=widget.side == 'right') }}
39
41
  {% if widget.collapsed %}
40
42
  {{ button('+', type='submit', name='action', value='expand', style='ghost', size='xs', shape='square', ariaLabel='Expand ' + widget.title) }}
41
43
  {% else %}
@@ -107,9 +109,13 @@
107
109
  </div>
108
110
  </div>
109
111
  <div class="grid grid-cols-1 gap-6 @3xl:grid-cols-2" data-dashboard-cards data-layout-url="{{ layoutUrl }}">
110
- {% for widget in widgets %}
111
- <div class="min-w-0{% if widget.width == 'full' %} @3xl:col-span-2{% endif %}" data-dashboard-card="{{ widget.key }}">
112
- {{ arrangedCard(widget) }}
112
+ {% for column in columns %}
113
+ <div class="flex min-w-0 flex-col gap-6 @3xl:min-h-24" data-dashboard-column="{{ column.side }}">
114
+ {% for widget in column.cards %}
115
+ <div class="min-w-0" data-dashboard-card="{{ widget.key }}">
116
+ {{ arrangedCard(widget) }}
117
+ </div>
118
+ {% endfor %}
113
119
  </div>
114
120
  {% endfor %}
115
121
  </div>
@@ -4,7 +4,7 @@
4
4
  saved last. #}
5
5
  {% extends "layouts/shell.njk" %}
6
6
 
7
- {% block title %}Conflicting edit{% endblock %}
7
+ {% block title %}{% if restoring %}Restore unsaved changes{% else %}Conflicting edit{% endif %}{% endblock %}
8
8
 
9
9
  {% import "components/fields.njk" as field %}
10
10
  {% from "components/alert.njk" import alert %}
@@ -12,16 +12,29 @@
12
12
  {% from "components/card.njk" import card %}
13
13
 
14
14
  {% block content %}
15
- <h1 class="mb-6 text-2xl font-bold">This {{ kind.singular }} changed while you were editing it</h1>
15
+ {% if restoring %}
16
+ <h1 class="mb-6 text-2xl font-bold">This {{ kind.singular }} changed since your unsaved changes</h1>
16
17
 
17
- <div class="mb-6">
18
- {% call alert(color='error', style='soft', polite=true) %}
19
- <span>
20
- Nothing has been saved. The file on disk is not the one this form was filled in
21
- from — somebody else, or something else, has written to it.
22
- </span>
23
- {% endcall %}
24
- </div>
18
+ <div class="mb-6">
19
+ {% call alert(color='warning', style='soft', polite=true) %}
20
+ <span>
21
+ Nothing has been saved. The file on disk has been written to since your
22
+ unsaved changes were made from it. Read both versions before you choose.
23
+ </span>
24
+ {% endcall %}
25
+ </div>
26
+ {% else %}
27
+ <h1 class="mb-6 text-2xl font-bold">This {{ kind.singular }} changed while you were editing it</h1>
28
+
29
+ <div class="mb-6">
30
+ {% call alert(color='error', style='soft', polite=true) %}
31
+ <span>
32
+ Nothing has been saved. The file on disk is not the one this form was filled in
33
+ from — somebody else, or something else, has written to it.
34
+ </span>
35
+ {% endcall %}
36
+ </div>
37
+ {% endif %}
25
38
 
26
39
  <div class="grid gap-6 lg:grid-cols-2">
27
40
  {% call card(title='Your version') %}
@@ -32,6 +45,17 @@
32
45
  {% endcall %}
33
46
  </div>
34
47
 
48
+ {% if restoring %}
49
+ <div class="mt-6 flex flex-wrap items-center gap-2">
50
+ <form method="post" action="{{ restoring.discardUrl }}">
51
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
52
+ <input type="hidden" name="autosave" value="{{ restoring.key }}" />
53
+ <input type="hidden" name="return" value="{{ editUrl }}" />
54
+ {{ button('Discard mine and edit the file on disk', type='submit', style='soft') }}
55
+ </form>
56
+ {{ buttonLink('Edit mine over the file', restoring.rebaseUrl, color='primary') }}
57
+ </div>
58
+ {% else %}
35
59
  {# Everything the refused form carried, with the hash the file has now. One
36
60
  click is then a deliberate overwrite of a version its author has read. #}
37
61
  <form class="mt-6 flex flex-wrap items-center gap-2" method="post" action="{{ saveUrl }}">
@@ -52,4 +76,5 @@
52
76
  {{ buttonLink('Discard mine and edit the file on disk', editUrl, style='soft') }}
53
77
  {{ button('Keep mine and overwrite the file', type='submit', name='action', value=action, color='error') }}
54
78
  </form>
79
+ {% endif %}
55
80
  {% endblock %}
@@ -453,12 +453,33 @@
453
453
  </div>
454
454
  {% endif %}
455
455
 
456
+ {% if resume %}
457
+ <div class="my-6" id="autosave-resume" data-saved-at="{{ resume.savedAt }}">
458
+ {% call alert(color='warning', direction='responsive', labelledby='autosave-resume-message') %}
459
+ <p id="autosave-resume-message">
460
+ Unsaved changes from <time datetime="{{ resume.savedAt }}">{{ resume.at }}</time>.
461
+ {% if resume.changedSince %}This {{ resume.noun }} has changed since. Restore shows both versions side by side.{% endif %}
462
+ </p>
463
+ <div class="flex flex-wrap gap-2">
464
+ {{ buttonLink('Restore', resume.restoreUrl, color='primary', size='sm') }}
465
+ <form method="post" action="{{ resume.discardUrl }}" id="autosave-resume-discard">
466
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
467
+ <input type="hidden" name="autosave" value="{{ resume.key }}" />
468
+ <input type="hidden" name="return" value="{{ resume.returnUrl }}" />
469
+ {{ button('Discard', type='submit', style='soft', size='sm') }}
470
+ </form>
471
+ </div>
472
+ {% endcall %}
473
+ </div>
474
+ {% endif %}
475
+
456
476
  <form class="group/editor grid items-start gap-6 xl:grid-cols-[minmax(0,1fr)_20rem]" id="editor-form" method="post" action="{{ saveUrl }}">
457
477
  <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
458
478
  {# The hash of the file this form was filled in from. A save whose hash no
459
479
  longer matches what is on disk is refused rather than overwriting
460
480
  somebody else's edit (doc-1). #}
461
481
  <input type="hidden" name="hash" value="{{ form.hash }}" />
482
+ <input type="hidden" name="autosave" value="{{ autosaveKey }}" />
462
483
  {# Which layout the preview renders through. The save handler ignores it. #}
463
484
  <input type="hidden" name="type" value="{{ kind.type }}" />
464
485
  {% if editorKind %}
@@ -494,6 +515,13 @@
494
515
  data-preview-url="{{ previewUrl }}"
495
516
  data-upload-url="{{ uploadsUrl }}"
496
517
  data-kind="{{ kind.type }}"
518
+ data-autosave-url="{{ autosaveUrl }}"
519
+ data-autosave-user="{{ autosaveUser }}"
520
+ data-autosave-discard-url="{{ autosaveDiscardUrl }}"
521
+ data-autosave-timezone="{{ autosaveTimezone }}"
522
+ {% if autosaveCopySavedAt %}data-autosave-copy-saved-at="{{ autosaveCopySavedAt }}"{% endif %}
523
+ {% if autosaveFileTime %}data-autosave-file-time="{{ autosaveFileTime }}"{% endif %}
524
+ {% if clearedAutosaves and clearedAutosaves.length %}data-autosave-cleared="{{ clearedAutosaves | dump }}"{% endif %}
497
525
  >
498
526
  {{ button('Preview', type='submit', id='editor-preview-fallback', style='soft', formaction=previewUrl, formtarget='_blank', formnovalidate=true) }}
499
527
  </div>
@@ -22,7 +22,50 @@
22
22
  {{ tabs(filters, 'Show ' + (kind.plural | lower)) }}
23
23
  </div>
24
24
 
25
- {% if documents.length %}
25
+ {% if unsaved %}
26
+ {% if unsaved.length %}
27
+ {% call table('Unsaved ' + (kind.plural | lower)) %}
28
+ <thead>
29
+ <tr>
30
+ <th scope="col">Title</th>
31
+ {{ heading('Kind') }}
32
+ {{ heading('Of') }}
33
+ {{ heading('Saved') }}
34
+ </tr>
35
+ </thead>
36
+ <tbody>
37
+ {% for copy in unsaved %}
38
+ {% call row() %}
39
+ <td>
40
+ {% if copy.resumeUrl %}
41
+ <a class="link link-hover font-medium" href="{{ copy.resumeUrl }}">{{ copy.label }}</a>
42
+ {% else %}
43
+ <span class="font-medium">{{ copy.label }}</span>
44
+ {% endif %}
45
+ <div class="-ms-2 mt-1 flex items-center gap-1 whitespace-nowrap">
46
+ {% if copy.resumeUrl %}
47
+ {{ buttonLink('Resume', copy.resumeUrl, style='ghost', size='xs') }}
48
+ {% endif %}
49
+ <form method="post" action="{{ discardUrl }}">
50
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
51
+ <input type="hidden" name="autosave" value="{{ copy.key }}" />
52
+ <input type="hidden" name="return" value="{{ returnUrl }}" />
53
+ {{ button('Discard', type='submit', style='ghost', size='xs', ariaLabel='Discard the unsaved changes to ' + copy.label) }}
54
+ </form>
55
+ </div>
56
+ {{ rowToggle(copy.label) }}
57
+ </td>
58
+ {% call cell('Kind', nowrap=true) %}{{ copy.kind }}{% endcall %}
59
+ {% call cell('Of') %}{{ copy.of }}{% endcall %}
60
+ {% call cell('Saved', nowrap=true) %}<time datetime="{{ copy.savedAt }}">{{ copy.at }}</time>{% endcall %}
61
+ {% endcall %}
62
+ {% endfor %}
63
+ </tbody>
64
+ {% endcall %}
65
+ {% else %}
66
+ <p>No unsaved changes.</p>
67
+ {% endif %}
68
+ {% elif documents.length %}
26
69
  {% call table(kind.plural) %}
27
70
  <thead>
28
71
  <tr>
@@ -83,6 +83,10 @@
83
83
  </p>
84
84
  </div>
85
85
 
86
+ {{ field.checkbox('settings-header-icon', fields.header_icon,
87
+ 'Show the icon before the site title in the header', checked=form.header_icon,
88
+ hint='The default theme shows it at the size of the title on every page, once the site has an icon.') }}
89
+
86
90
  {% 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 %}
87
91
  {% call field.select('settings-license', fields.license, 'License', error=problems.license, hintHtml=licenseHint) %}
88
92
  <option value=""{% if not form.license %} selected{% endif %}>No license (all rights reserved)</option>
@@ -124,7 +124,7 @@
124
124
  Every link here is published with <code>rel="me"</code> whether or not
125
125
  you type it: it is how Mastodon verifies a profile field that points back
126
126
  at this site, and how IndieAuth knows these are yours. Nothing else in
127
- Geekity needs it.
127
+ Geekity CMS needs it.
128
128
  {% endset %}
129
129
  {{ field.textarea('profile-links', fields.links, 'Links', account.profile.links, rows='4',
130
130
  hintHtml=linksHint, error=profileProblems.links) }}
@@ -175,3 +175,35 @@
175
175
  #editor-surface .cm-md-mark {
176
176
  color: color-mix(in oklab, var(--color-base-content) 60%, transparent);
177
177
  }
178
+
179
+ html.editor-focus #editor-surface .cm-editor {
180
+ height: auto;
181
+ min-height: 60dvh;
182
+ border-color: transparent;
183
+ background-color: transparent;
184
+ font-size: 1.0625rem;
185
+ }
186
+
187
+ html.editor-focus #editor-surface .cm-editor.cm-focused {
188
+ outline: none;
189
+ }
190
+
191
+ html.editor-focus #editor-surface .cm-scroller {
192
+ line-height: 1.8;
193
+ }
194
+
195
+ html.editor-focus #editor-surface .cm-content {
196
+ padding-block: 0;
197
+ }
198
+
199
+ html.editor-focus #editor-surface .cm-line {
200
+ padding-inline: 0;
201
+ }
202
+
203
+ html.editor-focus #editor-surface .cm-gutters {
204
+ display: none;
205
+ }
206
+
207
+ html.editor-focus #editor-surface .cm-activeLine {
208
+ background-color: transparent;
209
+ }