@geekity/cms 0.36.1 → 0.38.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 (214) hide show
  1. package/admin/components/admin-bar.njk +1 -2
  2. package/admin/components/blocks.njk +250 -0
  3. package/admin/components/card.njk +8 -4
  4. package/admin/layouts/base.njk +1 -1
  5. package/admin/pages/comments/all.njk +1 -1
  6. package/admin/pages/dashboard/home.njk +33 -32
  7. package/admin/pages/documents/editor.njk +6 -6
  8. package/admin/pages/federation/followers.njk +2 -2
  9. package/admin/pages/media/file.njk +1 -1
  10. package/admin/pages/media/library.njk +4 -4
  11. package/admin/pages/plugins/add.njk +46 -0
  12. package/admin/pages/plugins/installed.njk +5 -32
  13. package/admin/pages/plugins/screen.njk +5 -35
  14. package/admin/pages/settings/general.njk +1 -1
  15. package/admin/pages/tools/export.njk +1 -1
  16. package/admin/pages/tools/maintenance.njk +1 -1
  17. package/admin/pages/users/edit.njk +1 -1
  18. package/admin/static/admin.css +300 -0
  19. package/dist/admin/admin-bar.js +1 -2
  20. package/dist/admin/admin-bar.js.map +1 -1
  21. package/dist/admin/blocks.d.ts +133 -0
  22. package/dist/admin/blocks.d.ts.map +1 -0
  23. package/dist/admin/blocks.js +454 -0
  24. package/dist/admin/blocks.js.map +1 -0
  25. package/dist/admin/core-widgets.d.ts +3 -0
  26. package/dist/admin/core-widgets.d.ts.map +1 -0
  27. package/dist/admin/core-widgets.js +17 -0
  28. package/dist/admin/core-widgets.js.map +1 -0
  29. package/dist/admin/engagement.d.ts +16 -0
  30. package/dist/admin/engagement.d.ts.map +1 -0
  31. package/dist/admin/engagement.js +38 -0
  32. package/dist/admin/engagement.js.map +1 -0
  33. package/dist/admin/federation.d.ts +2 -0
  34. package/dist/admin/federation.d.ts.map +1 -1
  35. package/dist/admin/federation.js +2 -1
  36. package/dist/admin/federation.js.map +1 -1
  37. package/dist/admin/formatting.d.ts +1 -0
  38. package/dist/admin/formatting.d.ts.map +1 -1
  39. package/dist/admin/formatting.js +18 -0
  40. package/dist/admin/formatting.js.map +1 -1
  41. package/dist/admin/index.d.ts +1 -1
  42. package/dist/admin/index.d.ts.map +1 -1
  43. package/dist/admin/index.js +1 -1
  44. package/dist/admin/index.js.map +1 -1
  45. package/dist/admin/plugins.d.ts.map +1 -1
  46. package/dist/admin/plugins.js +27 -13
  47. package/dist/admin/plugins.js.map +1 -1
  48. package/dist/admin/posts-by-path.d.ts +11 -0
  49. package/dist/admin/posts-by-path.d.ts.map +1 -0
  50. package/dist/admin/posts-by-path.js +59 -0
  51. package/dist/admin/posts-by-path.js.map +1 -0
  52. package/dist/admin/routes.d.ts +0 -2
  53. package/dist/admin/routes.d.ts.map +1 -1
  54. package/dist/admin/routes.js +23 -13
  55. package/dist/admin/routes.js.map +1 -1
  56. package/dist/admin/templates.d.ts +6 -0
  57. package/dist/admin/templates.d.ts.map +1 -1
  58. package/dist/admin/templates.js +14 -0
  59. package/dist/admin/templates.js.map +1 -1
  60. package/dist/admin/tools.js +1 -1
  61. package/dist/admin/tools.js.map +1 -1
  62. package/dist/admin/widget-fediverse.d.ts +3 -0
  63. package/dist/admin/widget-fediverse.d.ts.map +1 -0
  64. package/dist/admin/widget-fediverse.js +49 -0
  65. package/dist/admin/widget-fediverse.js.map +1 -0
  66. package/dist/admin/widget-followers.d.ts +3 -0
  67. package/dist/admin/widget-followers.d.ts.map +1 -0
  68. package/dist/admin/widget-followers.js +37 -0
  69. package/dist/admin/widget-followers.js.map +1 -0
  70. package/dist/admin/widget-health.d.ts +3 -0
  71. package/dist/admin/widget-health.d.ts.map +1 -0
  72. package/dist/admin/widget-health.js +103 -0
  73. package/dist/admin/widget-health.js.map +1 -0
  74. package/dist/admin/widget-interactions.d.ts +4 -0
  75. package/dist/admin/widget-interactions.d.ts.map +1 -0
  76. package/dist/admin/widget-interactions.js +99 -0
  77. package/dist/admin/widget-interactions.js.map +1 -0
  78. package/dist/admin/widget-posting.d.ts +3 -0
  79. package/dist/admin/widget-posting.d.ts.map +1 -0
  80. package/dist/admin/widget-posting.js +111 -0
  81. package/dist/admin/widget-posting.js.map +1 -0
  82. package/dist/admin/widget-scheduled.d.ts +4 -0
  83. package/dist/admin/widget-scheduled.d.ts.map +1 -0
  84. package/dist/admin/widget-scheduled.js +30 -0
  85. package/dist/admin/widget-scheduled.js.map +1 -0
  86. package/dist/admin/widget-top-posts.d.ts +4 -0
  87. package/dist/admin/widget-top-posts.d.ts.map +1 -0
  88. package/dist/admin/widget-top-posts.js +55 -0
  89. package/dist/admin/widget-top-posts.js.map +1 -0
  90. package/dist/admin/widgets.d.ts +35 -0
  91. package/dist/admin/widgets.d.ts.map +1 -0
  92. package/dist/admin/widgets.js +103 -0
  93. package/dist/admin/widgets.js.map +1 -0
  94. package/dist/cms/app.d.ts.map +1 -1
  95. package/dist/cms/app.js +2 -1
  96. package/dist/cms/app.js.map +1 -1
  97. package/dist/cms/boot.d.ts.map +1 -1
  98. package/dist/cms/boot.js +3 -2
  99. package/dist/cms/boot.js.map +1 -1
  100. package/dist/cms/server-error.d.ts.map +1 -1
  101. package/dist/cms/server-error.js +1 -2
  102. package/dist/cms/server-error.js.map +1 -1
  103. package/dist/cms/services.d.ts.map +1 -1
  104. package/dist/cms/services.js +19 -0
  105. package/dist/cms/services.js.map +1 -1
  106. package/dist/content/alt-text.d.ts +1 -0
  107. package/dist/content/alt-text.d.ts.map +1 -1
  108. package/dist/content/alt-text.js +26 -1
  109. package/dist/content/alt-text.js.map +1 -1
  110. package/dist/content/sqlite/catalog.d.ts.map +1 -1
  111. package/dist/content/sqlite/catalog.js +15 -2
  112. package/dist/content/sqlite/catalog.js.map +1 -1
  113. package/dist/content/store.d.ts +10 -0
  114. package/dist/content/store.d.ts.map +1 -1
  115. package/dist/content/store.js.map +1 -1
  116. package/dist/content/write.d.ts.map +1 -1
  117. package/dist/content/write.js +3 -1
  118. package/dist/content/write.js.map +1 -1
  119. package/dist/federation/backfill.d.ts +1 -1
  120. package/dist/federation/backfill.d.ts.map +1 -1
  121. package/dist/federation/backfill.js +5 -1
  122. package/dist/federation/backfill.js.map +1 -1
  123. package/dist/federation/follower-history.d.ts +16 -0
  124. package/dist/federation/follower-history.d.ts.map +1 -0
  125. package/dist/federation/follower-history.js +61 -0
  126. package/dist/federation/follower-history.js.map +1 -0
  127. package/dist/federation/inbox.d.ts.map +1 -1
  128. package/dist/federation/inbox.js +2 -1
  129. package/dist/federation/inbox.js.map +1 -1
  130. package/dist/federation/records.d.ts +1 -0
  131. package/dist/federation/records.d.ts.map +1 -1
  132. package/dist/federation/records.js +24 -6
  133. package/dist/federation/records.js.map +1 -1
  134. package/dist/files/data-files.d.ts +1 -0
  135. package/dist/files/data-files.d.ts.map +1 -1
  136. package/dist/files/data-files.js +6 -0
  137. package/dist/files/data-files.js.map +1 -1
  138. package/dist/jobs/queue.d.ts.map +1 -1
  139. package/dist/jobs/queue.js +5 -0
  140. package/dist/jobs/queue.js.map +1 -1
  141. package/dist/jobs/types.d.ts +7 -0
  142. package/dist/jobs/types.d.ts.map +1 -1
  143. package/dist/jobs/types.js.map +1 -1
  144. package/dist/plugin.d.ts +275 -21
  145. package/dist/plugin.d.ts.map +1 -1
  146. package/dist/plugin.js +9 -8
  147. package/dist/plugin.js.map +1 -1
  148. package/dist/plugins/fetch.d.ts +0 -5
  149. package/dist/plugins/fetch.d.ts.map +1 -1
  150. package/dist/plugins/fetch.js +31 -7
  151. package/dist/plugins/fetch.js.map +1 -1
  152. package/dist/plugins/host-api.d.ts +3 -0
  153. package/dist/plugins/host-api.d.ts.map +1 -0
  154. package/dist/plugins/host-api.js +8 -0
  155. package/dist/plugins/host-api.js.map +1 -0
  156. package/dist/plugins/install.d.ts.map +1 -1
  157. package/dist/plugins/install.js +4 -2
  158. package/dist/plugins/install.js.map +1 -1
  159. package/dist/plugins/page-tags.d.ts +8 -0
  160. package/dist/plugins/page-tags.d.ts.map +1 -0
  161. package/dist/plugins/page-tags.js +73 -0
  162. package/dist/plugins/page-tags.js.map +1 -0
  163. package/dist/plugins/registry.d.ts +4 -1
  164. package/dist/plugins/registry.d.ts.map +1 -1
  165. package/dist/plugins/registry.js +50 -9
  166. package/dist/plugins/registry.js.map +1 -1
  167. package/dist/plugins/site.js +1 -1
  168. package/dist/plugins/site.js.map +1 -1
  169. package/dist/store/admin-store.d.ts +2 -2
  170. package/dist/store/admin-store.d.ts.map +1 -1
  171. package/dist/store/admin-store.js.map +1 -1
  172. package/dist/store/comments.d.ts +7 -0
  173. package/dist/store/comments.d.ts.map +1 -1
  174. package/dist/store/comments.js +21 -0
  175. package/dist/store/comments.js.map +1 -1
  176. package/dist/store/deliveries.d.ts +1 -0
  177. package/dist/store/deliveries.d.ts.map +1 -1
  178. package/dist/store/deliveries.js +6 -0
  179. package/dist/store/deliveries.js.map +1 -1
  180. package/dist/store/inbox.d.ts +16 -0
  181. package/dist/store/inbox.d.ts.map +1 -1
  182. package/dist/store/inbox.js +62 -0
  183. package/dist/store/inbox.js.map +1 -1
  184. package/dist/store/queue-database.d.ts +6 -0
  185. package/dist/store/queue-database.d.ts.map +1 -1
  186. package/dist/store/queue-database.js +16 -1
  187. package/dist/store/queue-database.js.map +1 -1
  188. package/dist/store/webmentions.d.ts +1 -0
  189. package/dist/store/webmentions.d.ts.map +1 -1
  190. package/dist/store/webmentions.js +6 -0
  191. package/dist/store/webmentions.js.map +1 -1
  192. package/dist/web/env.d.ts +3 -0
  193. package/dist/web/env.d.ts.map +1 -1
  194. package/dist/web/feed-item.d.ts +4 -3
  195. package/dist/web/feed-item.d.ts.map +1 -1
  196. package/dist/web/feed-item.js +8 -2
  197. package/dist/web/feed-item.js.map +1 -1
  198. package/dist/web/listing-pages.d.ts.map +1 -1
  199. package/dist/web/listing-pages.js +7 -2
  200. package/dist/web/listing-pages.js.map +1 -1
  201. package/dist/web/page-tags.d.ts +4 -0
  202. package/dist/web/page-tags.d.ts.map +1 -0
  203. package/dist/web/page-tags.js +39 -0
  204. package/dist/web/page-tags.js.map +1 -0
  205. package/dist/web/render.d.ts +2 -0
  206. package/dist/web/render.d.ts.map +1 -1
  207. package/dist/web/render.js +14 -0
  208. package/dist/web/render.js.map +1 -1
  209. package/dist/web/routes.d.ts +2 -1
  210. package/dist/web/routes.d.ts.map +1 -1
  211. package/dist/web/routes.js +3 -1
  212. package/dist/web/routes.js.map +1 -1
  213. package/package.json +12 -12
  214. package/themes/default/layouts/base.njk +6 -4
@@ -6,7 +6,6 @@
6
6
  - `barScheme`, `adminColorScheme` of the user's admin theme; `auto` puts no
7
7
  data-scheme on the host.
8
8
  - `cspNonce`, on the admin only.
9
- - `assetPrefix`, where static/admin-bar.js is served from.
10
9
 
11
10
  WordPress's "Howdy," menu, greeting the way this site's owner does. A
12
11
  popover rather than a script: the browser opens and closes it, shuts it on
@@ -45,4 +44,4 @@ html { margin-top: var(--geekity-admin-bar-height) !important; }
45
44
  </div>
46
45
  {% endif %}
47
46
  </nav>
48
- <script src="{{ assetPrefix }}admin-bar.js" defer></script></template></geekity-admin-bar>
47
+ <script src="{{ adminAsset('admin-bar.js') }}" defer></script></template></geekity-admin-bar>
@@ -0,0 +1,250 @@
1
+ {% from "components/table.njk" import table %}
2
+
3
+ {% set SERIES = [
4
+ { dash: 'none', marker: 'circle' },
5
+ { dash: '6 4', marker: 'square' },
6
+ { dash: '2 3', marker: 'triangle' },
7
+ { dash: '10 3 2 3', marker: 'diamond' }
8
+ ] %}
9
+
10
+ {% set TINTS = {
11
+ '0': 'fill-primary stroke-primary',
12
+ '1': 'fill-secondary stroke-secondary',
13
+ '2': 'fill-accent stroke-accent',
14
+ '3': 'fill-info stroke-info'
15
+ } %}
16
+
17
+ {% set TONES = { better: 'text-success', worse: 'text-error', 'none': '' } %}
18
+
19
+ {% macro text(parts) -%}
20
+ {%- for part in parts -%}
21
+ {%- if part.code is defined -%}<code>{{ part.code }}</code>{%- else -%}{{ part }}{%- endif -%}
22
+ {%- endfor -%}
23
+ {%- endmacro %}
24
+
25
+ {% macro marker(shape, x, y) -%}
26
+ {%- if shape == 'circle' -%}
27
+ <circle class="stroke-base-100" stroke-width="2" cx="{{ x }}" cy="{{ y }}" r="4" />
28
+ {%- elif shape == 'square' -%}
29
+ <rect class="stroke-base-100" stroke-width="2" x="{{ x - 4 }}" y="{{ y - 4 }}" width="8" height="8" />
30
+ {%- elif shape == 'triangle' -%}
31
+ <polygon class="stroke-base-100" stroke-width="2" points="{{ x }},{{ y - 5 }} {{ x + 5 }},{{ y + 4 }} {{ x - 5 }},{{ y + 4 }}" />
32
+ {%- else -%}
33
+ <polygon class="stroke-base-100" stroke-width="2" points="{{ x }},{{ y - 5 }} {{ x + 5 }},{{ y }} {{ x }},{{ y + 5 }} {{ x - 5 }},{{ y }}" />
34
+ {%- endif -%}
35
+ {%- endmacro %}
36
+
37
+ {% macro textures(id, count) -%}
38
+ <defs>
39
+ {% if count > 1 %}
40
+ <pattern id="{{ id }}-texture-1" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
41
+ <line class="stroke-base-100" x1="0" y1="0" x2="0" y2="6" stroke-width="2" />
42
+ </pattern>
43
+ {% endif %}
44
+ {% if count > 2 %}
45
+ <pattern id="{{ id }}-texture-2" width="5" height="5" patternUnits="userSpaceOnUse">
46
+ <circle class="fill-base-100" cx="2.5" cy="2.5" r="1.25" />
47
+ </pattern>
48
+ {% endif %}
49
+ {% if count > 3 %}
50
+ <pattern id="{{ id }}-texture-3" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(-45)">
51
+ <line class="stroke-base-100" x1="0" y1="0" x2="0" y2="6" stroke-width="2" />
52
+ </pattern>
53
+ {% endif %}
54
+ </defs>
55
+ {%- endmacro %}
56
+
57
+ {% macro numbers(view) -%}
58
+ {% if view.rows.length %}
59
+ <details class="text-sm">
60
+ <summary class="cursor-pointer text-base-content/70">Show the numbers</summary>
61
+ {% call table(view.description, inCard=true) %}
62
+ <thead>
63
+ <tr>{% for column in view.columns %}<th scope="col">{{ column }}</th>{% endfor %}</tr>
64
+ </thead>
65
+ <tbody>
66
+ {% for row in view.rows %}
67
+ <tr><th scope="row">{{ row.heading }}</th>{% for cell in row.cells %}<td>{{ cell }}</td>{% endfor %}</tr>
68
+ {% endfor %}
69
+ </tbody>
70
+ {% endcall %}
71
+ </details>
72
+ {% endif %}
73
+ {%- endmacro %}
74
+
75
+ {% macro timeSeries(view) -%}
76
+ <div class="grid gap-3">
77
+ <p id="{{ view.id }}" class="sr-only">{{ view.description }}</p>
78
+ {% if view.series.length > 1 %}
79
+ <ul class="flex flex-wrap gap-x-4 gap-y-1 text-sm" aria-hidden="true">
80
+ {% for entry in view.series %}
81
+ {% set look = SERIES[loop.index0] %}
82
+ <li class="flex items-center gap-2">
83
+ <svg class="h-3 w-7" viewBox="0 0 28 12">
84
+ <g class="{{ TINTS | modifier(loop.index0 | string) | trim }}">
85
+ <line x1="0" y1="6" x2="28" y2="6" stroke-width="2" stroke-dasharray="{{ look.dash }}" />
86
+ {{ marker(look.marker, 14, 6) }}
87
+ </g>
88
+ </svg>
89
+ <span>{{ entry.name }}</span></li>
90
+ {% endfor %}
91
+ </ul>
92
+ {% endif %}
93
+ <svg role="img" aria-labelledby="{{ view.id }}" viewBox="0 0 {{ view.width }} {{ view.height }}" class="h-auto w-full max-w-xl">
94
+ {% for tick in view.yTicks %}
95
+ <line class="{{ 'stroke-base-content/30' if loop.first else 'stroke-base-content/10' }}" x1="{{ view.plot.left }}" x2="{{ view.plot.right }}" y1="{{ tick.y }}" y2="{{ tick.y }}" stroke-width="1" />
96
+ <text class="fill-base-content/70" x="{{ view.plot.left - 6 }}" y="{{ tick.y + 4 }}" text-anchor="end" font-size="12">{{ tick.label }}</text>
97
+ {% endfor %}
98
+ {% for tick in view.xTicks %}
99
+ <text class="fill-base-content/70" x="{{ tick.x }}" y="{{ view.height - 8 }}" text-anchor="middle" font-size="12">{{ tick.label }}</text>
100
+ {% endfor %}
101
+ {% for entry in view.series %}
102
+ {% set look = SERIES[loop.index0] %}
103
+ <g class="{{ TINTS | modifier(loop.index0 | string) | trim }}">
104
+ <path class="fill-none" stroke-dasharray="{{ look.dash }}" stroke-width="2" stroke-linejoin="round" stroke-linecap="round" d="{{ entry.path }}" />
105
+ </g>
106
+ {% endfor %}
107
+ {% for entry in view.series %}
108
+ {% set look = SERIES[loop.index0] %}
109
+ <g class="{{ TINTS | modifier(loop.index0 | string) | trim }}">{{ marker(look.marker, entry.end.x, entry.end.y) }}</g>
110
+ {% endfor %}
111
+ {% for band in view.bands %}
112
+ <rect class="fill-transparent" x="{{ band.x }}" y="{{ view.plot.top }}" width="{{ band.width }}" height="{{ view.plot.bottom - view.plot.top }}" pointer-events="all"><title>{{ band.title }}</title></rect>
113
+ {% endfor %}
114
+ </svg>
115
+ {{ numbers(view) }}
116
+ </div>
117
+ {%- endmacro %}
118
+
119
+ {% macro bars(view) -%}
120
+ <div class="grid gap-3">
121
+ <p id="{{ view.id }}" class="sr-only">{{ view.description }}</p>
122
+ {% if view.series.length > 1 %}
123
+ <ul class="flex flex-wrap gap-x-4 gap-y-1 text-sm" aria-hidden="true">
124
+ {% for name in view.series %}
125
+ <li class="flex items-center gap-2">
126
+ <svg class="size-3" viewBox="0 0 12 12">
127
+ <g class="{{ TINTS | modifier(loop.index0 | string) | trim }}"><rect width="12" height="12" rx="2" /></g>
128
+ {% if not loop.first %}<rect width="12" height="12" rx="2" fill="url(#{{ view.id }}-texture-{{ loop.index0 }})" />{% endif %}
129
+ </svg>
130
+ <span>{{ name }}</span></li>
131
+ {% endfor %}
132
+ </ul>
133
+ {% endif %}
134
+ <svg role="img" aria-labelledby="{{ view.id }}" viewBox="0 0 {{ view.width }} {{ view.height }}" class="h-auto w-full max-w-xl">
135
+ {{ textures(view.id, view.series.length) }}
136
+ {% for tick in view.yTicks %}
137
+ <line class="{{ 'stroke-base-content/30' if loop.first else 'stroke-base-content/10' }}" x1="{{ view.plot.left }}" x2="{{ view.plot.right }}" y1="{{ tick.y }}" y2="{{ tick.y }}" stroke-width="1" />
138
+ <text class="fill-base-content/70" x="{{ view.plot.left - 6 }}" y="{{ tick.y + 4 }}" text-anchor="end" font-size="12">{{ tick.label }}</text>
139
+ {% endfor %}
140
+ {% for bar in view.bars %}
141
+ {% for segment in bar.segments %}
142
+ <g class="{{ TINTS | modifier(segment.series | string) | trim }}"><rect x="{{ segment.x }}" y="{{ segment.y }}" width="{{ segment.width }}" height="{{ segment.height }}"><title>{{ segment.title }}</title></rect></g>
143
+ {% if segment.series > 0 %}<rect x="{{ segment.x }}" y="{{ segment.y }}" width="{{ segment.width }}" height="{{ segment.height }}" fill="url(#{{ view.id }}-texture-{{ segment.series }})" pointer-events="none" />{% endif %}
144
+ {% endfor %}
145
+ {% if bar.total %}
146
+ <text class="fill-base-content font-semibold" x="{{ bar.centerX }}" y="{{ bar.total.y }}" text-anchor="middle" font-size="11">{{ bar.total.text }}</text>
147
+ {% endif %}
148
+ {% if bar.tick %}
149
+ <text class="fill-base-content/70" x="{{ bar.centerX }}" y="{{ view.height - 8 }}" text-anchor="middle" font-size="12">{{ bar.tick }}</text>
150
+ {% endif %}
151
+ {% endfor %}
152
+ </svg>
153
+ {{ numbers(view) }}
154
+ </div>
155
+ {%- endmacro %}
156
+
157
+ {% macro calendar(view) -%}
158
+ <div class="grid gap-3">
159
+ <p id="{{ view.id }}" class="sr-only">{{ view.description }}</p>
160
+ <div class="flex flex-row-reverse overflow-x-auto">
161
+ <svg role="img" aria-labelledby="{{ view.id }}" viewBox="0 0 {{ view.width }} {{ view.height }}" width="{{ view.width * 1.25 }}" height="{{ view.height * 1.25 }}" class="mr-auto h-auto{% if view.width * 1.25 > 640 %} max-w-full min-w-160{% endif %} shrink-0">
162
+ {% for month in view.months %}
163
+ <text class="fill-base-content/70" x="{{ month.x }}" y="11" text-anchor="start" font-size="10">{{ month.label }}</text>
164
+ {% endfor %}
165
+ {% for day in view.weekdays %}
166
+ <text class="fill-base-content/70" x="24" y="{{ day.y }}" text-anchor="end" font-size="9">{{ day.label }}</text>
167
+ {% endfor %}
168
+ {% for cell in view.cells %}
169
+ <rect class="{% if cell.level == 0 %}fill-base-content/10{% elif cell.level == 1 %}fill-primary/25{% elif cell.level == 2 %}fill-primary/50{% elif cell.level == 3 %}fill-primary/75{% else %}fill-primary{% endif %}" x="{{ cell.x }}" y="{{ cell.y }}" width="{{ view.cell }}" height="{{ view.cell }}" rx="2"><title>{{ cell.title }}</title></rect>
170
+ {% endfor %}
171
+ </svg>
172
+ </div>
173
+ <div class="flex items-center gap-1 text-xs text-base-content/70" aria-hidden="true">
174
+ <span>Less</span>
175
+ <svg class="h-3 w-[4.25rem]" viewBox="0 0 68 12">
176
+ <rect class="fill-base-content/10" x="0" y="0" width="12" height="12" rx="2" />
177
+ <rect class="fill-primary/25" x="14" y="0" width="12" height="12" rx="2" />
178
+ <rect class="fill-primary/50" x="28" y="0" width="12" height="12" rx="2" />
179
+ <rect class="fill-primary/75" x="42" y="0" width="12" height="12" rx="2" />
180
+ <rect class="fill-primary" x="56" y="0" width="12" height="12" rx="2" />
181
+ </svg>
182
+ <span>More</span>
183
+ </div>
184
+ {{ numbers(view) }}
185
+ </div>
186
+ {%- endmacro %}
187
+
188
+ {% macro block(data) -%}
189
+ {% set view = data | drawn %}
190
+ {% if view.kind == 'paragraph' %}
191
+ <p class="text-sm">{{ text(view.parts) }}</p>
192
+ {% elif view.kind == 'table' %}
193
+ {% call table(view.table.caption, inCard=true) %}
194
+ <thead>
195
+ <tr>
196
+ {% for column in view.table.columns %}<th scope="col">{{ column }}</th>{% endfor %}
197
+ </tr>
198
+ </thead>
199
+ <tbody>
200
+ {% for row in view.table.rows %}
201
+ <tr>
202
+ {% for cell in row %}
203
+ {% if cell.time is defined %}
204
+ <td class="whitespace-nowrap">{% if cell.time %}{{ cell.time | date('readable') }}{% else %}Never{% endif %}</td>
205
+ {% elif cell.text is defined %}
206
+ <td class="wrap-anywhere">{% if cell.href %}<a class="link" href="{{ cell.href }}">{{ cell.text }}</a>{% else %}{{ cell.text }}{% endif %}</td>
207
+ {% else %}
208
+ <td class="wrap-anywhere">{{ text([cell]) }}</td>
209
+ {% endif %}
210
+ {% endfor %}
211
+ </tr>
212
+ {% endfor %}
213
+ </tbody>
214
+ {% endcall %}
215
+ {% elif view.kind == 'stats' %}
216
+ <div class="stats flex flex-wrap gap-px bg-base-content/10 shadow">
217
+ {% for stat in view.stats %}
218
+ <div class="stat w-auto flex-auto border-0 bg-base-100">
219
+ <div class="stat-title">{{ stat.label }}</div>
220
+ <div class="stat-value">{{ stat.value }}</div>
221
+ {% if stat.change %}
222
+ <div class="stat-desc{{ TONES | modifier(stat.change.tone) }}">{% if stat.change.arrow %}<span aria-hidden="true">{{ stat.change.arrow }}</span> {% endif %}{{ stat.change.text }}</div>
223
+ {% endif %}
224
+ </div>
225
+ {% endfor %}
226
+ </div>
227
+ {% elif view.kind == 'timeSeries' %}
228
+ {{ timeSeries(view) }}
229
+ {% elif view.kind == 'ranked' %}
230
+ <ol aria-label="{{ view.caption }}" class="grid gap-3 text-sm">
231
+ {% for item in view.items %}
232
+ <li class="grid grid-cols-[minmax(0,1fr)_auto] gap-x-3 gap-y-1">
233
+ <span class="truncate">{% if item.href %}<a class="link" href="{{ item.href }}">{{ item.label }}</a>{% else %}{{ item.label }}{% endif %}</span>
234
+ <span class="tabular-nums">{{ item.value }}</span>
235
+ <svg class="col-span-2 h-1.5 w-full" viewBox="0 0 100 6" preserveAspectRatio="none" aria-hidden="true">
236
+ <rect class="fill-base-content/10" width="100" height="6" />
237
+ <rect class="fill-primary" width="{{ item.widthPercent }}" height="6" />
238
+ </svg>
239
+ </li>
240
+ {% endfor %}
241
+ </ol>
242
+ {% elif view.kind == 'bars' %}
243
+ {{ bars(view) }}
244
+ {% elif view.kind == 'calendar' %}
245
+ {{ calendar(view) }}
246
+ {% else %}
247
+ {% if view.caption %}<p class="text-sm font-semibold">{{ view.caption }}</p>{% endif %}
248
+ <p class="text-sm text-base-content/70">No data to show.</p>
249
+ {% endif %}
250
+ {%- endmacro %}
@@ -15,17 +15,21 @@
15
15
 
16
16
  size xs, sm, md, lg, xl
17
17
  style border, dash
18
+ tone warning, a warning-coloured border for a card that flags a problem
18
19
 
19
20
  A value outside its set is a render error.
20
21
 
21
22
  `{% call cardActions() %}…{% endcall %}`, inside a card's body, is the row
22
- of buttons at its foot: `<div class="card-actions justify-end">`. #}
23
+ of buttons at its foot: `<div class="card-actions mt-2 justify-end">`. The
24
+ margin is there because card-body's gap spaces only its own children, and
25
+ the row usually sits inside a form, under the last field. #}
23
26
 
24
27
  {% set SIZES = { xs: 'card-xs', sm: 'card-sm', md: 'card-md', lg: 'card-lg', xl: 'card-xl' } %}
25
28
  {% set STYLES = { border: 'card-border', dash: 'card-dash' } %}
29
+ {% set TONES = { warning: 'border-2 border-warning' } %}
26
30
 
27
- {% macro card(title='', size=none, style=none) -%}
28
- <div class="card bg-base-100 shadow-sm{{ SIZES | modifier(size) }}{{ STYLES | modifier(style) }}">
31
+ {% macro card(title='', size=none, style=none, tone=none) -%}
32
+ <div class="card bg-base-100 shadow-sm{{ SIZES | modifier(size) }}{{ STYLES | modifier(style) }}{{ TONES | modifier(tone) }}">
29
33
  <div class="card-body">
30
34
  {% if title %}
31
35
  <h2 class="card-title">{{ title }}</h2>
@@ -36,5 +40,5 @@
36
40
  {%- endmacro %}
37
41
 
38
42
  {% macro cardActions() -%}
39
- <div class="card-actions justify-end">{{ caller() }}</div>
43
+ <div class="card-actions mt-2 justify-end">{{ caller() }}</div>
40
44
  {%- endmacro %}
@@ -8,7 +8,7 @@
8
8
  <meta name="viewport" content="width=device-width, initial-scale=1" />
9
9
  <title>{% block title %}Admin{% endblock %}{% if site.title %} · {{ site.title }}{% endif %}</title>
10
10
  <meta name="robots" content="noindex, nofollow" />
11
- <link rel="stylesheet" href="{{ assetPrefix }}admin.css" />
11
+ <link rel="stylesheet" href="{{ adminAsset('admin.css') }}" />
12
12
  </head>
13
13
  <body class="min-h-screen bg-base-200 text-base-content">
14
14
  {# First on every screen, so a keyboard gets past the bar and the menu in
@@ -31,7 +31,7 @@
31
31
  {% else %}
32
32
  <ul class="mb-6 flex max-w-3xl flex-col gap-4">
33
33
  {% for row in rows %}
34
- <li>
34
+ <li id="comment-{{ row.id }}">
35
35
  {% call card() %}
36
36
  <p class="flex flex-wrap items-center gap-x-3 gap-y-1">
37
37
  <span class="font-semibold">{{ row.author }}</span>
@@ -1,11 +1,23 @@
1
1
  {% extends "layouts/shell.njk" %}
2
2
  {% from "components/stat.njk" import stats, stat %}
3
3
  {% from "components/card.njk" import card %}
4
- {% from "components/table.njk" import table %}
5
- {% from "components/badge.njk" import status %}
4
+ {% from "components/badge.njk" import badge %}
5
+ {% from "components/blocks.njk" import block %}
6
+ {% from "components/tabs.njk" import tabs %}
6
7
 
7
8
  {% block title %}Dashboard{% endblock %}
8
9
 
10
+ {% macro widgetCard(widget, tone=none) -%}
11
+ {% call card(title=widget.title, tone=tone) %}
12
+ {% if widget.status == 'stale' %}
13
+ <p class="text-sm">{{ badge('Stale', color='warning') }} The latest load failed. These figures are from {{ widget.loadedAt }}.</p>
14
+ {% elif widget.status == 'unavailable' %}
15
+ <p>These stats are unavailable right now.</p>
16
+ {% endif %}
17
+ {% for data in widget.blocks %}{{ block(data) }}{% endfor %}
18
+ {% endcall %}
19
+ {%- endmacro %}
20
+
9
21
  {% block content %}
10
22
  <h1 class="mb-6 text-2xl font-bold">Dashboard</h1>
11
23
 
@@ -27,34 +39,23 @@
27
39
  </div>
28
40
  </section>
29
41
 
30
- {% call card(title='Recent posts') %}
31
- {% if recent.length %}
32
- {% call table('Recent posts', inCard=true) %}
33
- <thead>
34
- <tr>
35
- <th scope="col">Title</th>
36
- <th scope="col">Date</th>
37
- <th scope="col">Status</th>
38
- </tr>
39
- </thead>
40
- <tbody>
41
- {% for post in recent %}
42
- <tr>
43
- <td><a class="link link-hover" href="{{ post.editUrl }}">{{ post.title }}</a></td>
44
- <td class="whitespace-nowrap">{{ post.date | date('html') }}</td>
45
- <td>
46
- {% if post.draft %}
47
- {{ status('draft', 'Draft') }}
48
- {% else %}
49
- {{ status('published', 'Published') }}
50
- {% endif %}
51
- </td>
52
- </tr>
53
- {% endfor %}
54
- </tbody>
55
- {% endcall %}
56
- {% else %}
57
- <p>Nothing written yet.</p>
58
- {% endif %}
59
- {% endcall %}
42
+ {% for widget in alerts %}
43
+ <section class="mb-8">{{ widgetCard(widget, tone='warning') }}</section>
44
+ {% endfor %}
45
+
46
+ {% if widgets.length %}
47
+ <div class="@container">
48
+ <div class="mb-3 flex flex-wrap items-center justify-between gap-3">
49
+ <h2 class="text-lg font-semibold">Activity</h2>
50
+ {{ tabs(periods, 'Period', size='sm') }}
51
+ </div>
52
+ <div class="grid grid-cols-1 gap-6 @3xl:grid-cols-2">
53
+ {% for widget in widgets %}
54
+ <div class="min-w-0{% if widget.width == 'full' %} @3xl:col-span-2{% endif %}">
55
+ {{ widgetCard(widget) }}
56
+ </div>
57
+ {% endfor %}
58
+ </div>
59
+ </div>
60
+ {% endif %}
60
61
  {% endblock %}
@@ -674,21 +674,21 @@
674
674
  <script
675
675
  type="module"
676
676
  id="editor-script"
677
- src="{{ assetPrefix }}editor.js"
677
+ src="{{ adminAsset('editor.js') }}"
678
678
  nonce="{{ cspNonce }}"
679
679
  ></script>
680
680
 
681
681
  {# The slug and permalink auto-fill, in a file of its own so the policy never
682
682
  has to allow an inline script. #}
683
- <script defer src="{{ assetPrefix }}slug.js"></script>
684
- <script defer src="{{ assetPrefix }}upload-drop.js"></script>
683
+ <script defer src="{{ adminAsset('slug.js') }}"></script>
684
+ <script defer src="{{ adminAsset('upload-drop.js') }}"></script>
685
685
  {% if kind.type == 'post' %}
686
- <script defer src="{{ assetPrefix }}location.js"></script>
686
+ <script defer src="{{ adminAsset('location.js') }}"></script>
687
687
  {% endif %}
688
688
  {% if editorActions | length %}
689
- <script defer src="{{ assetPrefix }}editor-actions.js"></script>
689
+ <script defer src="{{ adminAsset('editor-actions.js') }}"></script>
690
690
  {% endif %}
691
691
  {% if sections and sections.shown.review %}
692
- <script defer src="{{ assetPrefix }}item-lookup.js"></script>
692
+ <script defer src="{{ adminAsset('item-lookup.js') }}"></script>
693
693
  {% endif %}
694
694
  {% endblock %}
@@ -226,7 +226,7 @@
226
226
  {% endcall %}
227
227
  </section>
228
228
 
229
- <section>
229
+ <section id="delivery">
230
230
  <h2 class="mb-3 text-lg font-semibold">Delivery</h2>
231
231
  {% if posts.length %}
232
232
  {% call table('Delivery') %}
@@ -295,5 +295,5 @@
295
295
  <p>No post has been announced to the fediverse yet.</p>
296
296
  {% endif %}
297
297
  </section>
298
- <script defer src="{{ assetPrefix }}job-status.js"></script>
298
+ <script defer src="{{ adminAsset('job-status.js') }}"></script>
299
299
  {% endblock %}
@@ -139,5 +139,5 @@
139
139
  </div>
140
140
  </div>
141
141
 
142
- <script defer src="{{ assetPrefix }}copy.js"></script>
142
+ <script defer src="{{ adminAsset('copy.js') }}"></script>
143
143
  {% endblock %}
@@ -130,8 +130,8 @@
130
130
  </p>
131
131
  {% endif %}
132
132
 
133
- <script defer src="{{ assetPrefix }}copy.js"></script>
134
- <script defer src="{{ assetPrefix }}upload-drop.js"></script>
135
- <script defer src="{{ assetPrefix }}media.js"></script>
136
- <script defer src="{{ assetPrefix }}job-status.js"></script>
133
+ <script defer src="{{ adminAsset('copy.js') }}"></script>
134
+ <script defer src="{{ adminAsset('upload-drop.js') }}"></script>
135
+ <script defer src="{{ adminAsset('media.js') }}"></script>
136
+ <script defer src="{{ adminAsset('job-status.js') }}"></script>
137
137
  {% endblock %}
@@ -0,0 +1,46 @@
1
+ {# /admin/plugins/add: install a plugin from the npm registry (TASK-466).
2
+
3
+ Reached from the Add plugin button on the Plugins screen. A refused or
4
+ failed install comes back here with what was typed still in the boxes. #}
5
+ {% extends "layouts/shell.njk" %}
6
+ {% from "components/button.njk" import button %}
7
+ {% from "components/card.njk" import card, cardActions %}
8
+ {% import "components/fields.njk" as fields %}
9
+
10
+ {% block title %}Add plugin{% endblock %}
11
+
12
+ {% block content %}
13
+ <h1 class="mb-6 text-2xl font-bold">Add plugin</h1>
14
+
15
+ <div class="flex max-w-2xl flex-col gap-6">
16
+ {% call card() %}
17
+ <p class="text-sm">
18
+ A plugin runs as the site, with access to everything under <code>data/</code>,
19
+ the accounts and keys and other plugins' secrets. Add only plugins you trust.
20
+ </p>
21
+ {% if problems.package or problems.password %}
22
+ {% call fields.summary('Nothing was installed. Fix the fields marked below and try again.') %}
23
+ {{ fields.problem('plugin-package', problems.package) }}
24
+ {{ fields.problem('plugin-add-password', problems.password) }}
25
+ {% endcall %}
26
+ {% endif %}
27
+ <form id="plugin-add" method="post" action="{{ addUrl }}">
28
+ <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
29
+ {{ fields.text('plugin-package', 'package', 'Package', value=package,
30
+ hint='An npm package name, such as @geekity/plugin-llm.', error=problems.package,
31
+ required=true,
32
+ spellcheck='false', autocomplete='off') }}
33
+ {{ fields.text('plugin-version', 'version', 'Version (optional)', value=version,
34
+ hint='A version, a range or a dist-tag. Empty takes latest.', spellcheck='false',
35
+ autocomplete='off') }}
36
+ {{ fields.password('plugin-add-password', 'password', 'Your password',
37
+ error=problems.password, autocomplete='current-password', required=true) }}
38
+ {% call cardActions() %}
39
+ {{ button('Add plugin', type='submit', color='primary') }}
40
+ {% endcall %}
41
+ </form>
42
+ {% endcall %}
43
+
44
+ <p class="text-sm"><a class="link" href="{{ backUrl }}">Back to Plugins</a></p>
45
+ </div>
46
+ {% endblock %}
@@ -8,16 +8,17 @@
8
8
  (TASK-288). #}
9
9
  {% extends "layouts/shell.njk" %}
10
10
  {% from "components/badge.njk" import status %}
11
- {% from "components/button.njk" import button %}
11
+ {% from "components/button.njk" import button, buttonLink %}
12
12
  {% from "components/card.njk" import card, cardActions %}
13
- {% from "components/button.njk" import buttonLink %}
14
- {% import "components/fields.njk" as fields %}
15
13
  {% from "components/table.njk" import table %}
16
14
 
17
15
  {% block title %}Plugins{% endblock %}
18
16
 
19
17
  {% block content %}
20
- <h1 class="mb-6 text-2xl font-bold">Plugins</h1>
18
+ <div class="mb-6 flex flex-wrap items-center gap-4">
19
+ <h1 class="text-2xl font-bold">Plugins</h1>
20
+ {% if installer == 'on' %}{{ buttonLink('Add plugin', addUrl, color='primary') }}{% endif %}
21
+ </div>
21
22
 
22
23
  {% if reloaded %}
23
24
  <div role="status" class="alert alert-success mb-6"><span>Plugins reloaded.</span></div>
@@ -55,34 +56,6 @@
55
56
 
56
57
  {% if installer == 'on' %}
57
58
  <div class="mb-8 grid grid-cols-1 items-start gap-6 xl:grid-cols-2">
58
- {% call card(title="Add plugin") %}
59
- <p class="text-sm">
60
- A plugin runs as the site, with access to everything under <code>data/</code>,
61
- the accounts and keys and other plugins' secrets. Add only plugins you trust.
62
- </p>
63
- {% if addProblems.package or addProblems.password %}
64
- {% call fields.summary('Nothing was installed. Fix the fields marked below and try again.') %}
65
- {{ fields.problem('plugin-package', addProblems.package) }}
66
- {{ fields.problem('plugin-add-password', addProblems.password) }}
67
- {% endcall %}
68
- {% endif %}
69
- <form id="plugin-add" method="post" action="{{ add.url }}">
70
- <input type="hidden" name="csrf_token" value="{{ csrfToken }}" />
71
- {{ fields.text('plugin-package', 'package', 'Package', value=add.package,
72
- hint='An npm package name, such as @geekity/plugin-llm.', error=addProblems.package,
73
- required=true,
74
- spellcheck='false', autocomplete='off') }}
75
- {{ fields.text('plugin-version', 'version', 'Version (optional)', value=add.version,
76
- hint='A version, a range or a dist-tag. Empty takes latest.', spellcheck='false',
77
- autocomplete='off') }}
78
- {{ fields.password('plugin-add-password', 'password', 'Your password',
79
- error=addProblems.password, autocomplete='current-password', required=true) }}
80
- {% call cardActions() %}
81
- {{ button('Add plugin', type='submit', color='primary') }}
82
- {% endcall %}
83
- </form>
84
- {% endcall %}
85
-
86
59
  {% call card(title="Updates") %}
87
60
  <p class="text-sm">
88
61
  {% if checkedAt %}Last checked {{ checkedAt }}.{% else %}Not checked since this server started.{% endif %}
@@ -1,18 +1,13 @@
1
1
  {# /admin/plugins/<package name>: a plugin's own screen (decision-33). The
2
- plugin hands over cards of paragraphs and tables as data and this draws
3
- them, so a plugin writes no markup and everything it says is escaped. #}
2
+ plugin hands over cards of blocks as data (components/blocks.njk) and this
3
+ draws them, so a plugin writes no markup and everything it says is
4
+ escaped. #}
4
5
  {% extends "layouts/shell.njk" %}
5
6
  {% import "components/fields.njk" as field %}
6
7
  {% from "components/setting-fields.njk" import settingFields %}
7
8
  {% from "components/button.njk" import button %}
8
9
  {% from "components/card.njk" import card, cardActions %}
9
- {% from "components/table.njk" import table %}
10
-
11
- {% macro text(parts) -%}
12
- {%- for part in parts -%}
13
- {%- if part.code is defined -%}<code>{{ part.code }}</code>{%- else -%}{{ part }}{%- endif -%}
14
- {%- endfor -%}
15
- {%- endmacro %}
10
+ {% from "components/blocks.njk" import block %}
16
11
 
17
12
  {% block title %}{{ title }}{% endblock %}
18
13
 
@@ -56,32 +51,7 @@
56
51
 
57
52
  {% for entry in cards %}
58
53
  {% call card(title=entry.title) %}
59
- {% for block in entry.blocks %}
60
- {% if block.table %}
61
- {% call table(block.table.caption, inCard=true) %}
62
- <thead>
63
- <tr>
64
- {% for column in block.table.columns %}<th scope="col">{{ column }}</th>{% endfor %}
65
- </tr>
66
- </thead>
67
- <tbody>
68
- {% for row in block.table.rows %}
69
- <tr>
70
- {% for cell in row %}
71
- {% if cell.time is defined %}
72
- <td class="whitespace-nowrap">{% if cell.time %}{{ cell.time | date('readable') }}{% else %}Never{% endif %}</td>
73
- {% else %}
74
- <td>{{ text([cell]) }}</td>
75
- {% endif %}
76
- {% endfor %}
77
- </tr>
78
- {% endfor %}
79
- </tbody>
80
- {% endcall %}
81
- {% else %}
82
- <p class="text-sm">{{ text(block.paragraph) }}</p>
83
- {% endif %}
84
- {% endfor %}
54
+ {% for data in entry.blocks %}{{ block(data) }}{% endfor %}
85
55
  {% endcall %}
86
56
  {% endfor %}
87
57
  </div>
@@ -54,7 +54,7 @@
54
54
  {{ field.text('settings-icon', fields.icon, 'Site icon', form.icon,
55
55
  error=problems.icon, hintHtml=iconHint) }}
56
56
  {{ uploadControl('field', 'settings-icon', uploadsUrl, images=true) }}
57
- <script defer src="{{ assetPrefix }}upload-drop.js"></script>
57
+ <script defer src="{{ adminAsset('upload-drop.js') }}"></script>
58
58
 
59
59
  <div class="flex items-center gap-4">
60
60
  {% if iconPreview %}
@@ -88,5 +88,5 @@
88
88
  {% endif %}
89
89
  </div>
90
90
  </div>
91
- <script defer src="{{ assetPrefix }}job-status.js"></script>
91
+ <script defer src="{{ adminAsset('job-status.js') }}"></script>
92
92
  {% endblock %}