@jant/core 0.6.16 → 0.7.1

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 (592) hide show
  1. package/README.md +11 -11
  2. package/bin/commands/assets/upload.js +192 -15
  3. package/bin/commands/export.js +3 -1
  4. package/bin/commands/import-site.js +749 -220
  5. package/bin/commands/reset-password.js +21 -8
  6. package/bin/commands/setup.js +136 -0
  7. package/bin/commands/site/export.js +71 -34
  8. package/bin/commands/site/pull-media.js +2 -6
  9. package/bin/commands/site/snapshot/export.js +19 -61
  10. package/bin/commands/site/snapshot/import.js +23 -24
  11. package/bin/lib/d1-query.js +92 -18
  12. package/bin/lib/hugo-markdown.js +4 -0
  13. package/bin/lib/node-env.js +40 -4
  14. package/bin/lib/site-pull-media.js +21 -28
  15. package/bin/lib/site-selection.js +94 -29
  16. package/bin/lib/site-snapshot.js +338 -3
  17. package/bin/lib/sql-export.js +68 -5
  18. package/bin/lib/wrangler-cli.js +37 -2
  19. package/bin/lib/wrangler-json.js +35 -0
  20. package/bin/lib/zip-archive.js +187 -0
  21. package/dist/{app-C7_4oab9.js → app-ZcaI1kPN.js} +53190 -13529
  22. package/dist/client/.vite/manifest.json +255 -90
  23. package/dist/client/_assets/chunks/confirm-D7GrwsCN.js +45 -0
  24. package/dist/client/_assets/chunks/create-editor-B-m7X7S5.js +261 -0
  25. package/dist/client/_assets/chunks/{native-DpcrFAPh.js → emoji-mart-C4RpRKcU.js} +716 -1
  26. package/dist/client/_assets/chunks/footnote-rail-CmeoVtnU.js +1 -0
  27. package/dist/client/_assets/chunks/heic-to-CuP2Qlsw.js +1 -0
  28. package/dist/client/_assets/chunks/jant-locale-picker-DL6mIOT5.js +69 -0
  29. package/dist/client/_assets/chunks/lit-DW8VmJAT.js +2 -0
  30. package/dist/client/_assets/chunks/mediabunny-DRaCQ1r-.js +31 -0
  31. package/dist/client/_assets/chunks/rolldown-runtime-hePW80VL.js +1 -0
  32. package/dist/client/_assets/chunks/settings-paths-BcdySNx6.js +84 -0
  33. package/dist/client/_assets/chunks/site-header-nav-DzXsFk05.js +1 -0
  34. package/dist/client/_assets/chunks/slug-format-CRXO1AL5.js +1 -0
  35. package/dist/client/_assets/chunks/slugify-373citbs.js +1 -0
  36. package/dist/client/_assets/chunks/sortable-list-BJyd-LXE.js +2 -0
  37. package/dist/client/_assets/chunks/types-D5iF3H1a.js +1 -0
  38. package/dist/client/_assets/chunks/unsafe-html-B5ZEXaxg.js +1 -0
  39. package/dist/client/_assets/chunks/unsafe-svg-BkscJx69.js +2 -0
  40. package/dist/client/_assets/client-DAhoqPdr.css +2 -0
  41. package/dist/client/_assets/client-auth-CAMfTrKW.js +828 -0
  42. package/dist/client/_assets/client-author-DQ8R-8KR.css +2 -0
  43. package/dist/client/_assets/client-compose-D6rExKVK.js +2004 -0
  44. package/dist/client/_assets/client-knSEyUJO.js +295 -0
  45. package/dist/client/_assets/client-manage-sVOkqVbH.js +2170 -0
  46. package/dist/client/_assets/client-settings-CGR_nZ78.js +1112 -0
  47. package/dist/github-sync-Gw4orAHk.js +8398 -0
  48. package/dist/index.js +2 -5
  49. package/dist/node.js +11 -9
  50. package/package.json +15 -5
  51. package/src/__tests__/auth-session-freshness.test.ts +57 -0
  52. package/src/__tests__/basecoat-variants.test.ts +121 -0
  53. package/src/__tests__/bin/assets-upload.test.ts +68 -0
  54. package/src/__tests__/dev-scripts.test.ts +203 -0
  55. package/src/__tests__/export-collection-order.test.ts +276 -0
  56. package/src/__tests__/export-feed-ids.test.ts +184 -0
  57. package/src/__tests__/export-feed-order.test.ts +294 -0
  58. package/src/__tests__/export-hugo-build.test.ts +390 -0
  59. package/src/__tests__/export-import-roundtrip.test.ts +94 -0
  60. package/src/__tests__/export-service.test.ts +842 -28
  61. package/src/__tests__/export-smart-collection.test.ts +329 -0
  62. package/src/__tests__/helpers/component-css.ts +91 -0
  63. package/src/__tests__/helpers/db.ts +33 -0
  64. package/src/__tests__/helpers/export-fixtures.ts +3 -0
  65. package/src/__tests__/helpers/hugo-site.ts +146 -0
  66. package/src/__tests__/import-site-command.test.ts +487 -1
  67. package/src/__tests__/mise-config.test.ts +75 -4
  68. package/src/__tests__/node-dev-tasks.test.ts +264 -0
  69. package/src/__tests__/site-export-canonical-import.test.ts +149 -0
  70. package/src/__tests__/snapshot-canonical-replay.test.ts +179 -0
  71. package/src/__tests__/snapshot-settings.test.ts +97 -0
  72. package/src/__tests__/snapshot-tables.test.ts +219 -0
  73. package/src/__tests__/sql-export.test.ts +173 -0
  74. package/src/__tests__/stylesheet-audience.test.ts +159 -0
  75. package/src/__tests__/stylesheet-breakpoints.test.ts +183 -0
  76. package/src/__tests__/zip-archive.test.ts +106 -0
  77. package/src/app.tsx +86 -22
  78. package/src/auth.ts +18 -1
  79. package/src/client/__tests__/collection-page-actions.test.ts +51 -2
  80. package/src/client/__tests__/compose-bridge-image-upload.test.ts +196 -0
  81. package/src/client/__tests__/compose-bridge-video-poster.test.ts +159 -0
  82. package/src/client/__tests__/compose-bridge.test.ts +272 -0
  83. package/src/client/__tests__/compose-launch.test.ts +62 -1
  84. package/src/client/__tests__/compose-shortcuts.test.ts +38 -9
  85. package/src/client/__tests__/compose-triggers.test.ts +90 -0
  86. package/src/client/__tests__/copy-field.test.ts +162 -0
  87. package/src/client/__tests__/custom-url-menu.test.ts +127 -6
  88. package/src/client/__tests__/image-processor.test.ts +324 -2
  89. package/src/client/__tests__/lazy-entries.test.ts +92 -0
  90. package/src/client/__tests__/media-scroll-hint.test.ts +155 -0
  91. package/src/client/__tests__/runtime-paths.test.ts +84 -0
  92. package/src/client/__tests__/smart-collection-page-actions.test.ts +158 -0
  93. package/src/client/__tests__/toast.test.ts +23 -0
  94. package/src/client/__tests__/video-processor-probe.test.ts +175 -0
  95. package/src/client/__tests__/video-processor.test.ts +181 -0
  96. package/src/client/audio-processor.ts +24 -15
  97. package/src/client/audio-waveform.ts +49 -0
  98. package/src/client/collection-dialog-host.ts +72 -0
  99. package/src/client/collection-navigation.ts +51 -16
  100. package/src/client/collection-page-actions.ts +26 -0
  101. package/src/client/components/__tests__/jant-collection-dialog.test.ts +329 -0
  102. package/src/client/components/__tests__/jant-collection-directory.test.ts +274 -10
  103. package/src/client/components/__tests__/jant-collection-form.test.ts +59 -178
  104. package/src/client/components/__tests__/jant-command-palette.test.ts +28 -0
  105. package/src/client/components/__tests__/jant-compose-dialog-draft-storage.test.ts +340 -0
  106. package/src/client/components/__tests__/jant-compose-dialog.test.ts +1250 -53
  107. package/src/client/components/__tests__/jant-compose-editor.test.ts +544 -40
  108. package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +0 -1
  109. package/src/client/components/__tests__/jant-locale-picker.test.ts +207 -0
  110. package/src/client/components/__tests__/jant-nav-manager.test.ts +234 -15
  111. package/src/client/components/__tests__/jant-post-menu.test.ts +413 -2
  112. package/src/client/components/__tests__/jant-post-picker.test.ts +197 -0
  113. package/src/client/components/__tests__/jant-settings-avatar.test.ts +15 -12
  114. package/src/client/components/__tests__/jant-settings-general.test.ts +561 -172
  115. package/src/client/components/__tests__/jant-settings-language.test.ts +851 -0
  116. package/src/client/components/__tests__/jant-smart-collection-dialog.test.ts +665 -0
  117. package/src/client/components/collection-dialog-types.ts +49 -0
  118. package/src/client/components/collection-manager-types.ts +32 -1
  119. package/src/client/components/collection-types.ts +1 -15
  120. package/src/client/components/compose-types.ts +75 -1
  121. package/src/client/components/jant-collection-dialog.ts +722 -0
  122. package/src/client/components/jant-collection-directory.ts +567 -182
  123. package/src/client/components/jant-collection-form.ts +53 -232
  124. package/src/client/components/jant-command-palette.ts +100 -49
  125. package/src/client/components/jant-compose-dialog.ts +1486 -707
  126. package/src/client/components/jant-compose-editor.ts +673 -336
  127. package/src/client/components/jant-compose-fullscreen.ts +51 -43
  128. package/src/client/components/jant-config-editor.ts +65 -53
  129. package/src/client/components/jant-confirm-dialog.ts +13 -11
  130. package/src/client/components/jant-locale-picker.ts +418 -0
  131. package/src/client/components/jant-media-lightbox.ts +146 -136
  132. package/src/client/components/jant-nav-manager.ts +690 -366
  133. package/src/client/components/jant-post-menu.ts +1086 -282
  134. package/src/client/components/jant-post-picker.ts +324 -0
  135. package/src/client/components/jant-repo-picker-types.ts +6 -1
  136. package/src/client/components/jant-repo-picker.ts +137 -108
  137. package/src/client/components/jant-settings-avatar.ts +31 -27
  138. package/src/client/components/jant-settings-general.ts +622 -543
  139. package/src/client/components/jant-settings-language.ts +1087 -0
  140. package/src/client/components/jant-smart-collection-dialog.ts +1021 -0
  141. package/src/client/components/jant-text-preview.ts +94 -86
  142. package/src/client/components/nav-manager-types.ts +62 -1
  143. package/src/client/components/settings-types.ts +14 -29
  144. package/src/client/components/smart-collection-conditions.ts +285 -0
  145. package/src/client/components/smart-collection-dialog-types.ts +68 -0
  146. package/src/client/compose-bridge.ts +214 -141
  147. package/src/client/compose-launch.ts +76 -2
  148. package/src/client/compose-shortcuts.ts +6 -8
  149. package/src/client/compose-triggers.ts +50 -0
  150. package/src/client/copy-field.ts +108 -0
  151. package/src/client/custom-url-menu.ts +60 -4
  152. package/src/client/icons.ts +14 -0
  153. package/src/client/image-plan.ts +165 -0
  154. package/src/client/image-processor.ts +195 -117
  155. package/src/client/image-worker-client.ts +45 -0
  156. package/src/client/image-worker.ts +135 -0
  157. package/src/client/lazy-entries.ts +95 -0
  158. package/src/client/lazy-slugify.ts +2 -2
  159. package/src/client/media-scroll-hint.ts +43 -2
  160. package/src/client/mediabunny.ts +29 -0
  161. package/src/client/post-picker.ts +41 -0
  162. package/src/client/runtime-paths.ts +74 -1
  163. package/src/client/settings-bridge.ts +4 -10
  164. package/src/client/site-header-nav.js +18 -6
  165. package/src/client/smart-collection-dialog-host.ts +116 -0
  166. package/src/client/smart-collection-page-actions.ts +208 -0
  167. package/src/client/tiptap/__tests__/bubble-menu.test.ts +110 -0
  168. package/src/client/tiptap/__tests__/floating-position.test.ts +136 -1
  169. package/src/client/tiptap/__tests__/inline-image-upload.test.ts +37 -0
  170. package/src/client/tiptap/__tests__/list-editing.test.ts +224 -86
  171. package/src/client/tiptap/__tests__/mark-exit.test.ts +1 -2
  172. package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +26 -0
  173. package/src/client/tiptap/bubble-menu.ts +47 -16
  174. package/src/client/tiptap/extensions.ts +0 -3
  175. package/src/client/tiptap/floating-position.ts +216 -2
  176. package/src/client/tiptap/inline-image-upload.ts +7 -3
  177. package/src/client/tiptap/link-toolbar.ts +22 -9
  178. package/src/client/tiptap/structural-keymap.ts +158 -47
  179. package/src/client/toast.ts +13 -0
  180. package/src/client/types/vite-worker.d.ts +12 -0
  181. package/src/client/upload-with-metadata.ts +3 -13
  182. package/src/client/video-processor.ts +351 -107
  183. package/src/client-auth.ts +14 -31
  184. package/src/client-compose.ts +19 -0
  185. package/src/client-manage.ts +13 -0
  186. package/src/client-settings.ts +14 -0
  187. package/src/client-site.ts +10 -3
  188. package/src/client.ts +4 -0
  189. package/src/db/__tests__/backfill-nav-label-mirrors.test.ts +149 -0
  190. package/src/db/__tests__/d1-query.test.ts +56 -1
  191. package/src/db/__tests__/schema-enum-sources.test.ts +33 -0
  192. package/src/db/__tests__/smart-collection-placement.test.ts +402 -0
  193. package/src/db/backfills/0006_clear_mirrored_page_nav_labels.sql +31 -0
  194. package/src/db/migrations/0032_married_tomas.sql +3 -0
  195. package/src/db/migrations/0033_busy_johnny_storm.sql +198 -0
  196. package/src/db/migrations/0034_cheerful_rumiko_fujikawa.sql +35 -0
  197. package/src/db/migrations/meta/0032_snapshot.json +2582 -0
  198. package/src/db/migrations/meta/0033_snapshot.json +2816 -0
  199. package/src/db/migrations/meta/0034_snapshot.json +2816 -0
  200. package/src/db/migrations/meta/_journal.json +21 -0
  201. package/src/db/migrations/pg/0030_abandoned_nighthawk.sql +3 -0
  202. package/src/db/migrations/pg/0031_strong_ultimatum.sql +128 -0
  203. package/src/db/migrations/pg/0032_furry_multiple_man.sql +6 -0
  204. package/src/db/migrations/pg/meta/0030_snapshot.json +3329 -0
  205. package/src/db/migrations/pg/meta/0031_snapshot.json +3660 -0
  206. package/src/db/migrations/pg/meta/0032_snapshot.json +3660 -0
  207. package/src/db/migrations/pg/meta/_journal.json +21 -0
  208. package/src/db/pg/schema.ts +195 -27
  209. package/src/db/post-visibility.ts +117 -0
  210. package/src/db/schema.ts +222 -27
  211. package/src/i18n/__tests__/detect.test.ts +26 -81
  212. package/src/i18n/__tests__/supported-locales.test.ts +83 -0
  213. package/src/i18n/context.tsx +12 -2
  214. package/src/i18n/detect.ts +7 -66
  215. package/src/i18n/locales/glossary.zh-Hans.yml +44 -4
  216. package/src/i18n/locales/glossary.zh-Hant.yml +44 -4
  217. package/src/i18n/locales/public/en.po +490 -158
  218. package/src/i18n/locales/public/en.ts +1 -1
  219. package/src/i18n/locales/public/zh-Hans.po +487 -155
  220. package/src/i18n/locales/public/zh-Hans.ts +1 -1
  221. package/src/i18n/locales/public/zh-Hant.po +487 -155
  222. package/src/i18n/locales/public/zh-Hant.ts +1 -1
  223. package/src/i18n/locales/settings/en.po +555 -73
  224. package/src/i18n/locales/settings/en.ts +1 -1
  225. package/src/i18n/locales/settings/zh-Hans.po +601 -119
  226. package/src/i18n/locales/settings/zh-Hans.ts +1 -1
  227. package/src/i18n/locales/settings/zh-Hant.po +614 -132
  228. package/src/i18n/locales/settings/zh-Hant.ts +1 -1
  229. package/src/i18n/locales.ts +94 -0
  230. package/src/i18n/middleware.ts +12 -2
  231. package/src/i18n/supported-locales.ts +80 -0
  232. package/src/lib/__tests__/api-settings.test.ts +11 -2
  233. package/src/lib/__tests__/asset-path.test.ts +92 -0
  234. package/src/lib/__tests__/deferred.test.ts +90 -0
  235. package/src/lib/__tests__/discover-ping.test.ts +89 -0
  236. package/src/lib/__tests__/discover.test.ts +410 -0
  237. package/src/lib/__tests__/feed-policy.test.ts +26 -0
  238. package/src/lib/__tests__/feed.test.ts +1526 -15
  239. package/src/lib/__tests__/filter-dimensions.test.ts +341 -0
  240. package/src/lib/__tests__/github-sync-repo-name.test.ts +40 -0
  241. package/src/lib/__tests__/hosted-signin.test.ts +1 -1
  242. package/src/lib/__tests__/http-cache.test.ts +162 -0
  243. package/src/lib/__tests__/image.test.ts +27 -1
  244. package/src/lib/__tests__/jant-docs-dev.test.ts +21 -0
  245. package/src/lib/__tests__/jant-docs.test.ts +23 -0
  246. package/src/lib/__tests__/lang-detect.test.ts +231 -0
  247. package/src/lib/__tests__/markdown-to-tiptap.test.ts +105 -0
  248. package/src/lib/__tests__/markdown.test.ts +10 -0
  249. package/src/lib/__tests__/media-helpers.test.ts +81 -0
  250. package/src/lib/__tests__/navigation.test.ts +135 -1
  251. package/src/lib/__tests__/resolve-config.test.ts +131 -3
  252. package/src/lib/__tests__/schemas.test.ts +69 -5
  253. package/src/lib/__tests__/site-unavailable-page.test.ts +25 -0
  254. package/src/lib/__tests__/slug.test.ts +70 -0
  255. package/src/lib/__tests__/slugify.test.ts +85 -0
  256. package/src/lib/__tests__/storage.test.ts +32 -0
  257. package/src/lib/__tests__/thread-fold.test.ts +113 -0
  258. package/src/lib/__tests__/timeline.test.ts +87 -0
  259. package/src/lib/__tests__/tiptap-to-markdown.test.ts +172 -4
  260. package/src/lib/__tests__/upload-sideload.test.ts +14 -0
  261. package/src/lib/__tests__/url-fetch.test.ts +1 -2
  262. package/src/lib/__tests__/url.test.ts +78 -72
  263. package/src/lib/__tests__/view-language.test.ts +413 -0
  264. package/src/lib/__tests__/worker-response-cache.test.ts +90 -0
  265. package/src/lib/address.ts +57 -0
  266. package/src/lib/api-posts.ts +12 -0
  267. package/src/lib/asset-path.ts +98 -1
  268. package/src/lib/build-env.ts +16 -0
  269. package/src/lib/collection-groups.ts +5 -1
  270. package/src/lib/collection-paths.ts +3 -10
  271. package/src/lib/constants.ts +28 -3
  272. package/src/lib/copy-field.ts +36 -0
  273. package/src/lib/crypto.ts +40 -0
  274. package/src/lib/csp-builder.ts +2 -1
  275. package/src/lib/deferred.ts +79 -0
  276. package/src/lib/discover-ping.ts +141 -0
  277. package/src/lib/discover.ts +439 -0
  278. package/src/lib/env.ts +134 -0
  279. package/src/lib/errors.ts +33 -0
  280. package/src/lib/feed-policy.ts +236 -2
  281. package/src/lib/feed.ts +926 -73
  282. package/src/lib/filter-dimensions.ts +1349 -0
  283. package/src/lib/github-sync-repo-name.ts +45 -0
  284. package/src/lib/github-sync-site-config.ts +3 -0
  285. package/src/lib/hosted-signin.ts +1 -1
  286. package/src/lib/http-cache.ts +148 -0
  287. package/src/lib/hugo-markdown.ts +62 -0
  288. package/src/lib/icons.ts +9 -2
  289. package/src/lib/ids.ts +3 -0
  290. package/src/lib/image.ts +17 -4
  291. package/src/lib/jant-docs.ts +42 -0
  292. package/src/lib/lang-detect.ts +308 -0
  293. package/src/lib/markdown-manager.ts +392 -2
  294. package/src/lib/media-helpers.ts +13 -1
  295. package/src/lib/navigation.ts +78 -11
  296. package/src/lib/pagination.ts +28 -24
  297. package/src/lib/per-language-surfaces.ts +67 -0
  298. package/src/lib/post-body-html.ts +11 -4
  299. package/src/lib/post-meta.ts +19 -0
  300. package/src/lib/render.tsx +55 -7
  301. package/src/lib/resolve-config.ts +111 -6
  302. package/src/lib/schemas.ts +251 -15
  303. package/src/lib/site-header-fragment.tsx +2 -1
  304. package/src/lib/site-unavailable-page.ts +47 -0
  305. package/src/lib/slug.ts +51 -7
  306. package/src/lib/slugify.ts +113 -0
  307. package/src/lib/startup-config.ts +4 -6
  308. package/src/lib/storage.ts +7 -0
  309. package/src/lib/summary.ts +50 -0
  310. package/src/lib/thread-fold.ts +161 -0
  311. package/src/lib/time.ts +32 -0
  312. package/src/lib/timeline.ts +43 -2
  313. package/src/lib/tiptap-render.ts +1 -2
  314. package/src/lib/tiptap-to-markdown.ts +13 -7
  315. package/src/lib/upload.ts +16 -13
  316. package/src/lib/url.ts +159 -86
  317. package/src/lib/version.ts +10 -8
  318. package/src/lib/view-language.ts +436 -0
  319. package/src/lib/view.ts +53 -25
  320. package/src/lib/viewer-context.ts +62 -0
  321. package/src/middleware/__tests__/auth.test.ts +70 -2
  322. package/src/middleware/__tests__/cache-control.test.ts +71 -3
  323. package/src/middleware/__tests__/onboarding.test.ts +76 -5
  324. package/src/middleware/__tests__/secure-headers.test.ts +1 -1
  325. package/src/middleware/__tests__/session.test.ts +181 -33
  326. package/src/middleware/auth.ts +50 -31
  327. package/src/middleware/cache-control.ts +75 -10
  328. package/src/middleware/config.ts +6 -6
  329. package/src/middleware/error-handler.ts +12 -0
  330. package/src/middleware/onboarding.ts +32 -10
  331. package/src/middleware/secure-headers.ts +1 -1
  332. package/src/middleware/session.ts +96 -8
  333. package/src/node/__tests__/cli-migrate.test.ts +21 -19
  334. package/src/node/__tests__/cli-reset-password.test.ts +81 -8
  335. package/src/node/__tests__/cli-runtime-target.test.ts +48 -1
  336. package/src/node/__tests__/cli-setup.test.ts +163 -0
  337. package/src/node/__tests__/cli-site-selection.test.ts +128 -38
  338. package/src/node/__tests__/cli-site-snapshot.test.ts +67 -0
  339. package/src/node/__tests__/cli-snapshot-meta.test.ts +20 -0
  340. package/src/node/__tests__/runtime.test.ts +75 -0
  341. package/src/node/index.ts +3 -0
  342. package/src/node/request-handler.ts +3 -1
  343. package/src/preset.css +76 -22
  344. package/src/routes/__tests__/feed-validator.test.ts +156 -0
  345. package/src/routes/api/__tests__/collections.test.ts +114 -0
  346. package/src/routes/api/__tests__/discover.test.ts +222 -0
  347. package/src/routes/api/__tests__/nav-items.test.ts +212 -2
  348. package/src/routes/api/__tests__/post-translations.test.ts +829 -0
  349. package/src/routes/api/__tests__/posts.test.ts +24 -0
  350. package/src/routes/api/__tests__/settings.test.ts +24 -3
  351. package/src/routes/api/__tests__/telegram.test.ts +6 -5
  352. package/src/routes/api/__tests__/upload.test.ts +34 -0
  353. package/src/routes/api/collections.ts +49 -6
  354. package/src/routes/api/custom-urls.ts +0 -1
  355. package/src/routes/api/discover.ts +95 -0
  356. package/src/routes/api/export.ts +6 -3
  357. package/src/routes/api/internal/__tests__/sites.test.ts +42 -2
  358. package/src/routes/api/internal/sites.ts +0 -1
  359. package/src/routes/api/nav-items.ts +55 -8
  360. package/src/routes/api/posts.ts +161 -0
  361. package/src/routes/api/public/__tests__/archive.test.ts +111 -0
  362. package/src/routes/api/public/archive.ts +77 -81
  363. package/src/routes/api/public/posts.ts +44 -7
  364. package/src/routes/api/smart-collections.ts +132 -0
  365. package/src/routes/api/telegram.ts +2 -40
  366. package/src/routes/api/upload.ts +10 -3
  367. package/src/routes/api/uploads.ts +1 -2
  368. package/src/routes/auth/__tests__/setup-auth.test.ts +137 -0
  369. package/src/routes/auth/__tests__/setup-discover.test.ts +204 -0
  370. package/src/routes/auth/__tests__/setup-flow.test.ts +270 -0
  371. package/src/routes/auth/__tests__/setup-page.test.tsx +329 -0
  372. package/src/routes/auth/__tests__/setup-steps.test.ts +310 -0
  373. package/src/routes/auth/__tests__/setup.test.ts +278 -26
  374. package/src/routes/auth/__tests__/signin.test.ts +135 -0
  375. package/src/routes/auth/setup.tsx +934 -186
  376. package/src/routes/auth/signin.tsx +10 -8
  377. package/src/routes/compose.tsx +9 -0
  378. package/src/routes/dash/__tests__/custom-urls-archive.test.ts +44 -0
  379. package/src/routes/dash/__tests__/custom-urls-page.test.ts +49 -0
  380. package/src/routes/dash/__tests__/delete-account.test.ts +106 -0
  381. package/src/routes/dash/__tests__/github-sync-app.test.ts +365 -0
  382. package/src/routes/dash/__tests__/settings-discover.test.ts +213 -0
  383. package/src/routes/dash/custom-urls.tsx +1 -37
  384. package/src/routes/dash/settings.tsx +714 -111
  385. package/src/routes/discover-announce.ts +96 -0
  386. package/src/routes/feed/__tests__/feed.test.ts +165 -2
  387. package/src/routes/feed/feed.ts +69 -40
  388. package/src/routes/feed/sitemap.ts +95 -11
  389. package/src/routes/pages/__tests__/archive-indexing.test.ts +200 -0
  390. package/src/routes/pages/__tests__/archive-params.test.ts +370 -6
  391. package/src/routes/pages/__tests__/collection-routing.test.ts +16 -0
  392. package/src/routes/pages/__tests__/collections-feed-links.test.ts +72 -0
  393. package/src/routes/pages/__tests__/collections.test.ts +9 -3
  394. package/src/routes/pages/__tests__/custom-archive-url.test.ts +119 -0
  395. package/src/routes/pages/__tests__/feed-autodiscovery.test.ts +148 -0
  396. package/src/routes/pages/__tests__/language-routing.test.ts +679 -0
  397. package/src/routes/pages/__tests__/post-page-round-trips.test.ts +211 -0
  398. package/src/routes/pages/__tests__/post-preview-partial.test.ts +96 -0
  399. package/src/routes/pages/__tests__/smart-collection.test.ts +298 -0
  400. package/src/routes/pages/__tests__/subscribe.test.ts +248 -0
  401. package/src/routes/pages/__tests__/thread-order.test.ts +179 -0
  402. package/src/routes/pages/archive.tsx +487 -379
  403. package/src/routes/pages/collection.tsx +94 -96
  404. package/src/routes/pages/collections.tsx +38 -50
  405. package/src/routes/pages/featured.tsx +43 -18
  406. package/src/routes/pages/home.tsx +25 -6
  407. package/src/routes/pages/language.tsx +113 -0
  408. package/src/routes/pages/latest.tsx +26 -16
  409. package/src/routes/pages/new.tsx +3 -0
  410. package/src/routes/pages/page.tsx +228 -45
  411. package/src/routes/pages/partials.tsx +41 -0
  412. package/src/routes/pages/search.tsx +22 -5
  413. package/src/routes/pages/smart-collection.tsx +340 -0
  414. package/src/routes/pages/subscribe.tsx +182 -0
  415. package/src/runtime/__tests__/index.test.ts +19 -0
  416. package/src/runtime/__tests__/node.test.ts +129 -4
  417. package/src/runtime/__tests__/readiness.test.ts +23 -0
  418. package/src/runtime/cloudflare.ts +21 -2
  419. package/src/runtime/index.ts +27 -1
  420. package/src/runtime/node.ts +67 -1
  421. package/src/runtime/readiness.ts +15 -0
  422. package/src/runtime/site.ts +95 -24
  423. package/src/services/__tests__/auth.test.ts +116 -7
  424. package/src/services/__tests__/bootstrap-setup-instance.test.ts +230 -0
  425. package/src/services/__tests__/collection.test.ts +108 -4
  426. package/src/services/__tests__/custom-url.test.ts +34 -0
  427. package/src/services/__tests__/github-app-installations.test.ts +153 -0
  428. package/src/services/__tests__/github-sync-push.test.ts +46 -0
  429. package/src/services/__tests__/language.test.ts +495 -0
  430. package/src/services/__tests__/media.test.ts +31 -0
  431. package/src/services/__tests__/navigation.test.ts +213 -10
  432. package/src/services/__tests__/path.test.ts +236 -0
  433. package/src/services/__tests__/post-language.test.ts +830 -0
  434. package/src/services/__tests__/post-timeline.test.ts +127 -0
  435. package/src/services/__tests__/post.test.ts +120 -9
  436. package/src/services/__tests__/settings.test.ts +228 -17
  437. package/src/services/__tests__/site-admin.test.ts +5 -5
  438. package/src/services/__tests__/smart-collection.test.ts +651 -0
  439. package/src/services/about-page.ts +22 -1
  440. package/src/services/auth.ts +51 -49
  441. package/src/services/bootstrap.ts +408 -39
  442. package/src/services/collection-directory-position.ts +76 -0
  443. package/src/services/collection.ts +331 -46
  444. package/src/services/custom-url.ts +29 -24
  445. package/src/services/export-theme/assets/client-site.css +1 -1
  446. package/src/services/export-theme/assets/client-site.js +123 -123
  447. package/src/services/export-theme/layouts/_default/alias.html +27 -1
  448. package/src/services/export-theme/layouts/_default/list.html +2 -63
  449. package/src/services/export-theme/layouts/_default/rss.xml +103 -38
  450. package/src/services/export-theme/layouts/collections/list.html +3 -3
  451. package/src/services/export-theme/layouts/featured/list.html +1 -4
  452. package/src/services/export-theme/layouts/index.html +40 -26
  453. package/src/services/export-theme/layouts/partials/collection-members.html +100 -0
  454. package/src/services/export-theme/layouts/partials/collection-threads.html +33 -0
  455. package/src/services/export-theme/layouts/partials/featured-members.html +35 -0
  456. package/src/services/export-theme/layouts/partials/featured-thread.html +1 -1
  457. package/src/services/export-theme/layouts/partials/feed-post-content.xml +105 -2
  458. package/src/services/export-theme/layouts/partials/footer.html +1 -1
  459. package/src/services/export-theme/layouts/partials/head.html +1 -1
  460. package/src/services/export-theme/layouts/partials/header.html +5 -3
  461. package/src/services/export-theme/layouts/partials/jant-data.html +23 -0
  462. package/src/services/export-theme/layouts/partials/latest-members.html +48 -0
  463. package/src/services/export-theme/layouts/partials/media-gallery.html +35 -21
  464. package/src/services/export-theme/layouts/partials/pagination.html +14 -13
  465. package/src/services/export-theme/layouts/partials/post-card.html +8 -4
  466. package/src/services/export-theme/layouts/partials/reply.html +2 -1
  467. package/src/services/export-theme/layouts/partials/smart-collection-members.html +129 -0
  468. package/src/services/export-theme/layouts/partials/thread-preview.html +41 -31
  469. package/src/services/export-theme/layouts/post/list.html +1 -1
  470. package/src/services/export-theme/layouts/smart_collection/list.html +24 -0
  471. package/src/services/export-theme/styles/main.css +13 -2
  472. package/src/services/export-theme/theme.toml +1 -1
  473. package/src/services/export.ts +880 -72
  474. package/src/services/github-app-installations.ts +171 -4
  475. package/src/services/github-sync.ts +69 -15
  476. package/src/services/index.ts +34 -3
  477. package/src/services/language.ts +412 -0
  478. package/src/services/mcp.ts +22 -2
  479. package/src/services/media.ts +22 -7
  480. package/src/services/navigation.ts +429 -67
  481. package/src/services/path.ts +254 -6
  482. package/src/services/post.ts +1234 -190
  483. package/src/services/search.ts +39 -2
  484. package/src/services/settings.ts +238 -43
  485. package/src/services/site-admin.ts +27 -14
  486. package/src/services/site.ts +160 -32
  487. package/src/services/smart-collection.ts +636 -0
  488. package/src/services/telegram.ts +20 -22
  489. package/src/services/upload-session.ts +1 -3
  490. package/src/style-author.css +27 -0
  491. package/src/styles/components-author.css +1622 -0
  492. package/src/styles/components.css +24 -1571
  493. package/src/styles/site-media.css +16 -2
  494. package/src/styles/tokens.css +82 -12
  495. package/src/styles/ui-author.css +7602 -0
  496. package/src/styles/ui.css +1090 -7216
  497. package/src/types/app-context.ts +23 -0
  498. package/src/types/bindings.ts +8 -0
  499. package/src/types/config.ts +94 -15
  500. package/src/types/constants.ts +188 -16
  501. package/src/types/entities.ts +87 -1
  502. package/src/types/operations.ts +74 -2
  503. package/src/types/props.ts +157 -31
  504. package/src/types/views.ts +55 -1
  505. package/src/ui/__tests__/font-themes.test.ts +52 -12
  506. package/src/ui/compose/ComposeDialog.tsx +201 -7
  507. package/src/ui/compose/ComposePrompt.tsx +1 -5
  508. package/src/ui/compose/__tests__/ComposeDialog.test.tsx +125 -0
  509. package/src/ui/dash/appearance/AdvancedContent.tsx +2 -2
  510. package/src/ui/dash/appearance/CodeInjectionContent.tsx +12 -16
  511. package/src/ui/dash/appearance/NavigationContent.tsx +117 -16
  512. package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +33 -11
  513. package/src/ui/dash/appearance/__tests__/system-nav-descriptions.test.ts +44 -0
  514. package/src/ui/dash/appearance/system-nav-descriptions.ts +75 -0
  515. package/src/ui/dash/settings/AccountMenuContent.tsx +2 -2
  516. package/src/ui/dash/settings/ApiTokensContent.tsx +2 -1
  517. package/src/ui/dash/settings/ConfigEditorContent.tsx +35 -16
  518. package/src/ui/dash/settings/GeneralContent.tsx +277 -113
  519. package/src/ui/dash/settings/LanguageContent.tsx +409 -0
  520. package/src/ui/dash/settings/SettingsRootContent.tsx +19 -1
  521. package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +324 -4
  522. package/src/ui/dash/settings/__tests__/LanguageContent.test.tsx +144 -0
  523. package/src/ui/dash/settings/__tests__/discover-copy.test.ts +66 -0
  524. package/src/ui/dash/settings/discover-copy.ts +116 -0
  525. package/src/ui/feed/LinkCard.tsx +55 -45
  526. package/src/ui/feed/NoteCard.tsx +9 -4
  527. package/src/ui/feed/PostStatusBadges.tsx +90 -43
  528. package/src/ui/feed/QuoteCard.tsx +55 -30
  529. package/src/ui/feed/ThreadPreview.tsx +6 -2
  530. package/src/ui/feed/__tests__/thread-preview.test.ts +13 -16
  531. package/src/ui/feed/__tests__/timeline-cards.test.ts +314 -49
  532. package/src/ui/feed/thread-preview-state.ts +20 -6
  533. package/src/ui/font-themes.ts +39 -29
  534. package/src/ui/layouts/BaseLayout.tsx +155 -10
  535. package/src/ui/layouts/SiteLayout.tsx +192 -34
  536. package/src/ui/layouts/__tests__/BaseLayout.test.tsx +196 -17
  537. package/src/ui/layouts/__tests__/SiteLayout.test.tsx +109 -85
  538. package/src/ui/pages/ArchivePage.tsx +282 -289
  539. package/src/ui/pages/CollectionPage.tsx +86 -76
  540. package/src/ui/pages/CollectionsPage.tsx +5 -0
  541. package/src/ui/pages/ComposePage.tsx +8 -0
  542. package/src/ui/pages/PostPage.tsx +47 -0
  543. package/src/ui/pages/SearchPage.tsx +3 -3
  544. package/src/ui/pages/SmartCollectionPage.tsx +434 -0
  545. package/src/ui/pages/SubscribePage.tsx +236 -0
  546. package/src/ui/pages/__tests__/ArchivePage.test.tsx +65 -9
  547. package/src/ui/pages/__tests__/collections-directory-rows.test.tsx +92 -0
  548. package/src/ui/pages/__tests__/collections-view-links.test.tsx +81 -0
  549. package/src/ui/shared/CollectionDirectory.tsx +178 -3
  550. package/src/ui/shared/CollectionsManager.tsx +70 -17
  551. package/src/ui/shared/CopyField.tsx +127 -0
  552. package/src/ui/shared/EmptyPostContent.tsx +23 -0
  553. package/src/ui/shared/MediaGallery.tsx +125 -46
  554. package/src/ui/shared/Pagination.tsx +20 -5
  555. package/src/ui/shared/PostFooter.tsx +20 -11
  556. package/src/ui/shared/__tests__/archive-labels.test.ts +100 -0
  557. package/src/ui/shared/__tests__/media-gallery.test.ts +210 -2
  558. package/src/ui/shared/__tests__/navigation-labels.test.ts +16 -22
  559. package/src/ui/shared/__tests__/pagination.test.ts +38 -24
  560. package/src/ui/shared/__tests__/post-footer.test.ts +32 -0
  561. package/src/ui/shared/archive-labels.ts +98 -0
  562. package/src/ui/shared/collection-management-labels.ts +94 -16
  563. package/src/ui/shared/navigation-labels.ts +45 -71
  564. package/src/ui/shared/post-article-attributes.ts +2 -0
  565. package/src/ui/shared/smart-collection-labels.ts +343 -0
  566. package/dist/app-C75Zc8dD.js +0 -6
  567. package/dist/client/_assets/chunks/heic-to-DUUaO23q.js +0 -1
  568. package/dist/client/_assets/chunks/module-DcsAZQZ_.js +0 -716
  569. package/dist/client/_assets/chunks/url-CU9pEbk5.js +0 -1
  570. package/dist/client/_assets/client-Bq0vre8Y.js +0 -296
  571. package/dist/client/_assets/client-D3VZyG4L.css +0 -2
  572. package/dist/client/_assets/client-auth-F2rb9WfF.js +0 -5571
  573. package/dist/env-BPYViDJJ.js +0 -281
  574. package/dist/export-Dl5KCiEs.js +0 -5562
  575. package/dist/github-api-BNF35Lkx.js +0 -176
  576. package/dist/github-app-DdiD-bC4.js +0 -275
  577. package/dist/github-sync-Bqg62IvV.js +0 -4
  578. package/dist/github-sync-HdK2EgvJ.js +0 -431
  579. package/dist/url-CbLAtlZe.js +0 -900
  580. package/src/client/__tests__/collection-created-notice.test.ts +0 -36
  581. package/src/client/__tests__/collection-form-bridge.test.ts +0 -216
  582. package/src/client/__tests__/post-form-bridge.test.ts +0 -82
  583. package/src/client/collection-created-notice.ts +0 -66
  584. package/src/client/collection-form-bridge.ts +0 -191
  585. package/src/client/components/__tests__/jant-post-form.test.ts +0 -193
  586. package/src/client/components/jant-post-form.ts +0 -422
  587. package/src/client/components/post-form-template.ts +0 -352
  588. package/src/client/components/post-form-types.ts +0 -96
  589. package/src/client/media-metadata.ts +0 -247
  590. package/src/client/post-form-bridge.ts +0 -193
  591. package/src/client/tiptap/exitable-marks.ts +0 -73
  592. package/src/ui/pages/CollectionEditorPage.tsx +0 -175
@@ -1,3 +1,13 @@
1
+ {{- /*
2
+ Alias page. Hugo renders one of these for every entry in a post's
3
+ `aliases:` front matter.
4
+
5
+ `.Permalink` is the alias target. It is empty whenever the page the alias
6
+ points at was not built — an empty `content=0; url=` is read by browsers as
7
+ "reload this page", which turns the alias into an infinite refresh loop. So
8
+ the redirect markup is only emitted when there is somewhere to redirect to.
9
+ */ -}}
10
+ {{- if .Permalink -}}
1
11
  <!doctype html>
2
12
  <html lang="{{ .Site.LanguageCode | default "en" }}">
3
13
  <head>
@@ -9,7 +19,10 @@
9
19
  <script>
10
20
  (function () {
11
21
  var target = {{ .Permalink }};
12
- var rootAliases = {{ with .Params.root_aliases }}{{ . | jsonify }}{{ else }}[]{{ end }};
22
+ // `safeJS` keeps this an array literal. Without it Hugo's contextual
23
+ // escaping emits a quoted JSON string, and the loop below compares
24
+ // single characters instead of paths.
25
+ var rootAliases = {{ with .Params.root_aliases }}{{ . | jsonify | safeJS }}{{ else }}[]{{ end }};
13
26
  try {
14
27
  var path = window.location.pathname || "";
15
28
  var normalized = "/" + path.replace(/^\/+|\/+$/g, "") + "/";
@@ -47,3 +60,16 @@
47
60
  <p>Redirecting to <a href="{{ .Permalink }}">{{ .Permalink }}</a>&hellip;</p>
48
61
  </body>
49
62
  </html>
63
+ {{- else -}}
64
+ <!doctype html>
65
+ <html lang="{{ .Site.LanguageCode | default "en" }}">
66
+ <head>
67
+ <meta charset="utf-8">
68
+ <title>{{ .Site.Title }}</title>
69
+ <meta name="robots" content="noindex,nofollow">
70
+ </head>
71
+ <body>
72
+ <p>This page is not available.</p>
73
+ </body>
74
+ </html>
75
+ {{- end -}}
@@ -8,45 +8,7 @@
8
8
  fallback for unknown section types.
9
9
  */ -}}
10
10
  {{- if eq .Type "collection" -}}
11
- {{- $collectionSlug := .Params.slug | default .Slug -}}
12
- {{- /* Thread collection membership is stored only on root bundles. */ -}}
13
- {{- $allPosts := where (where .Site.Pages "Type" "post") "Kind" "section" -}}
14
- {{- $members := slice -}}
15
- {{- range $allPosts -}}
16
- {{- $post := . -}}
17
- {{- with $post.Params.collections -}}
18
- {{- range . -}}
19
- {{- if eq .slug $collectionSlug -}}
20
- {{- $members = $members | append (dict "post" $post "entry" .) -}}
21
- {{- end -}}
22
- {{- end -}}
23
- {{- end -}}
24
- {{- end -}}
25
-
26
- {{- /* Split into pinned + unpinned, sort each, then concat. */ -}}
27
- {{- $pinned := slice -}}
28
- {{- $unpinned := slice -}}
29
- {{- range $members -}}
30
- {{- if .entry.pinned_at -}}
31
- {{- $pinned = $pinned | append . -}}
32
- {{- else -}}
33
- {{- $unpinned = $unpinned | append . -}}
34
- {{- end -}}
35
- {{- end -}}
36
- {{- if $pinned -}}
37
- {{- $pinned = sort $pinned "entry.pinned_at" "desc" -}}
38
- {{- end -}}
39
- {{- $sortOrder := .Params.sort_order | default "position" -}}
40
- {{- if $unpinned -}}
41
- {{- if eq $sortOrder "collected_at_desc" -}}
42
- {{- $unpinned = sort $unpinned "entry.collected_at" "desc" -}}
43
- {{- else -}}
44
- {{- $unpinned = sort $unpinned "entry.position" "asc" -}}
45
- {{- end -}}
46
- {{- end -}}
47
- {{- $ordered := $pinned | append $unpinned -}}
48
- {{- $posts := slice -}}
49
- {{- range $ordered -}}{{- $posts = $posts | append .post -}}{{- end -}}
11
+ {{- $posts := partial "collection-members.html" (dict "page" . "feed" false) -}}
50
12
 
51
13
  {{- $pageSize := .Site.Params.page_size | default 10 -}}
52
14
  {{- $paginator := .Paginate $posts $pageSize -}}
@@ -62,30 +24,7 @@
62
24
  {{- end -}}
63
25
  {{ .Content }}
64
26
  </header>
65
- {{- if $paginator.Pages -}}
66
- <div class="post-list">
67
- {{- range $i, $p := $paginator.Pages -}}
68
- {{- if gt $i 0 -}}<hr class="feed-divider" aria-hidden="true" />{{- end -}}
69
- {{- $replies := $p.Pages.ByDate -}}
70
- {{- $hasReplies := gt (len $replies) 0 -}}
71
- <div class="thread thread-full{{ if $hasReplies }} thread-has-replies{{ end }}" data-slug="{{ $p.Params.slug | default $p.Slug }}">
72
- <div class="thread-item thread-item-root">
73
- {{ partial "post-card.html" $p }}
74
- </div>
75
- {{- if $hasReplies -}}
76
- <section class="thread-replies" aria-label="Replies">
77
- {{- range $replies -}}
78
- {{ partial "reply.html" . }}
79
- {{- end -}}
80
- </section>
81
- {{- end -}}
82
- </div>
83
- {{- end -}}
84
- </div>
85
- {{ partial "pagination.html" $paginator }}
86
- {{- else -}}
87
- <p class="empty-state">This collection doesn't contain any threads.</p>
88
- {{- end -}}
27
+ {{ partial "collection-threads.html" (dict "paginator" $paginator "empty" "This collection doesn't contain any threads.") }}
89
28
  </section>
90
29
  {{- else -}}
91
30
  <section class="section">
@@ -2,18 +2,23 @@
2
2
  Atom 2005 feed template — mirrors the main site's `lib/feed.ts`.
3
3
 
4
4
  One template covers every page that opts into the custom `RSS` output
5
- format (home, featured, archive, and each collection section). Root-post
6
- sections do NOT opt in, so no `/{slug}/index.xml` gets emitted.
5
+ format (home, featured, archive, and each collection and smart collection
6
+ section). Root-post sections do NOT opt in, so no `/{slug}/index.xml` gets
7
+ emitted.
7
8
 
8
- Feed filter parity with the corresponding HTML list view:
9
- - home (`.Kind == "home"`): visibility == "public" (drops latest_hidden)
10
- - featured (`.Type == "featured"`): one Root per Thread whose derived
11
- featured projection is non-empty, ordered by selected Post publication
9
+ Each feed carries what the matching Jant feed carries, in its order:
10
+ - home (`.Kind == "home"`): `/latest/feed`, public roots by Thread
11
+ activity, pins ignored (`latest-members`)
12
+ - featured (`.Type == "featured"`): one root per Thread with a featured
13
+ post, by that post's publication (`featured-members`)
12
14
  - archive (`.Type == "archive"`): all published (includes latest_hidden)
13
- - collection (`.Type == "collection"`): Params.collections.slug match,
14
- ordered per `sort_order`; pinning is ignored for RSS
15
+ - collection (`.Type == "collection"`): its Threads by activity, newest
16
+ first, pins and `sort_order` ignored (`collection-members`)
17
+ - smart collection (`.Type == "smart_collection"`): the roots its
18
+ conditions select, in its stored order (`smart-collection-members`)
15
19
 
16
20
  Per-entry payload mirrors `buildSinglePostContent` + `buildFeedContent`:
21
+ - Id: `feed_id` from front matter, the id Jant's feed used; see below
17
22
  - Title: empty for quote posts, else .Title
18
23
  - Link rel=alternate: external URL for `link` format, else permalink;
19
24
  link-format posts also get a <link rel="related"> back to the
@@ -33,7 +38,6 @@
33
38
  {{- $page := . -}}
34
39
  {{- $limit := int (.Site.Params.rss_feed_limit | default 50) -}}
35
40
  {{- $allPosts := where .Site.Pages "Type" "post" -}}
36
- {{- $rootPosts := where $allPosts "Kind" "section" -}}
37
41
 
38
42
  {{- /* ------------------------------------------------------------------
39
43
  Post selection per feed kind
@@ -42,36 +46,15 @@
42
46
  {{- $isFeatured := eq $page.Type "featured" -}}
43
47
 
44
48
  {{- if eq $page.Kind "home" -}}
45
- {{- $public := where $allPosts "Params.visibility" "public" -}}
46
- {{- $posts = $public.ByDate.Reverse -}}
49
+ {{- $posts = partial "latest-members.html" (dict "feed" true) -}}
47
50
  {{- else if $isFeatured -}}
48
- {{- $featured := where $rootPosts "Params.featured_sort_at" "ne" nil -}}
49
- {{- $posts = sort $featured "Params.featured_sort_at" "desc" -}}
51
+ {{- $posts = partial "featured-members.html" . -}}
50
52
  {{- else if eq $page.Type "archive" -}}
51
53
  {{- $posts = $allPosts.ByDate.Reverse -}}
52
54
  {{- else if eq $page.Type "collection" -}}
53
- {{- $collectionSlug := $page.Params.slug | default $page.Slug -}}
54
- {{- $members := slice -}}
55
- {{- range $rootPosts -}}
56
- {{- $post := . -}}
57
- {{- with $post.Params.collections -}}
58
- {{- range . -}}
59
- {{- if eq .slug $collectionSlug -}}
60
- {{- $members = $members | append (dict "post" $post "entry" .) -}}
61
- {{- end -}}
62
- {{- end -}}
63
- {{- end -}}
64
- {{- end -}}
65
- {{- $sortOrder := $page.Params.sort_order | default "position" -}}
66
- {{- $ordered := $members -}}
67
- {{- if $ordered -}}
68
- {{- if eq $sortOrder "collected_at_desc" -}}
69
- {{- $ordered = sort $ordered "entry.collected_at" "desc" -}}
70
- {{- else -}}
71
- {{- $ordered = sort $ordered "entry.position" "asc" -}}
72
- {{- end -}}
73
- {{- end -}}
74
- {{- range $ordered -}}{{- $posts = $posts | append .post -}}{{- end -}}
55
+ {{- $posts = partial "collection-members.html" (dict "page" $page "feed" true) -}}
56
+ {{- else if eq $page.Type "smart_collection" -}}
57
+ {{- $posts = partial "smart-collection-members.html" (dict "page" $page "feed" true) -}}
75
58
  {{- end -}}
76
59
 
77
60
  {{- if gt (len $posts) $limit -}}
@@ -88,7 +71,7 @@
88
71
  {{- $feedTitle = printf "%s - Featured posts" .Site.Title -}}
89
72
  {{- else if eq $page.Type "archive" -}}
90
73
  {{- $feedTitle = printf "%s - Archive" .Site.Title -}}
91
- {{- else if eq $page.Type "collection" -}}
74
+ {{- else if in (slice "collection" "smart_collection") $page.Type -}}
92
75
  {{- $feedTitle = printf "%s - %s" .Site.Title $page.Title -}}
93
76
  {{- end -}}
94
77
 
@@ -97,7 +80,16 @@
97
80
  {{- $selfUrl := .Permalink -}}
98
81
  {{- $now := now.UTC.Format "2006-01-02T15:04:05Z" -}}
99
82
  <?xml version="1.0" encoding="UTF-8"?>
100
- <feed xmlns="http://www.w3.org/2005/Atom">
83
+ {{- /* Namespaces ride along only when something in them is emitted, the same
84
+ rule `lib/feed.ts` follows. */ -}}
85
+ {{- $anyMedia := false -}}
86
+ {{- range $posts -}}
87
+ {{- if .Params.media -}}{{- $anyMedia = true -}}{{- end -}}
88
+ {{- range .Pages -}}{{- if .Params.media -}}{{- $anyMedia = true -}}{{- end -}}{{- end -}}
89
+ {{- end -}}
90
+ {{- $jantNs := cond (gt (len $posts) 0) ` xmlns:jant="https://jant.me/ns"` "" -}}
91
+ {{- $mediaNs := cond $anyMedia ` xmlns:media="http://search.yahoo.com/mrss/"` "" -}}
92
+ <feed xmlns="http://www.w3.org/2005/Atom"{{ $jantNs | safeHTMLAttr }}{{ $mediaNs | safeHTMLAttr }}>
101
93
  <title>{{ $feedTitle | transform.XMLEscape }}</title>
102
94
  <subtitle>{{ $siteDescription | transform.XMLEscape }}</subtitle>
103
95
  <link href="{{ $siteBase | transform.XMLEscape }}" rel="alternate"/>
@@ -108,6 +100,12 @@
108
100
  {{- $post := . -}}
109
101
  {{- $format := .Params.format | default "note" -}}
110
102
  {{- $permalink := .Permalink -}}
103
+ {{- /* <id>: the string Jant's feeds gave this entry, which the exporter
104
+ writes as `feed_id`. Readers recognise an entry by it alone, and
105
+ `.Permalink` differs from it (trailing slash; slug in place of a custom
106
+ path), so using the page URL would show every post again after a move.
107
+ A post written in Hugo has no `feed_id` and takes its page URL. */ -}}
108
+ {{- $entryId := string (.Params.feed_id | default $permalink) -}}
111
109
  {{- $isLink := and (eq $format "link") .Params.link_url -}}
112
110
  {{- $alternate := cond $isLink (string .Params.link_url) $permalink -}}
113
111
  {{- /* Atom <title>: empty for quotes (matches main site's getAtomTitle). */ -}}
@@ -129,15 +127,82 @@
129
127
  {{- $summaryText = or (string (.Params.summary_text | default "")) (.Title | default "") (string (.Params.link_url | default "")) -}}
130
128
  {{- end -}}
131
129
  {{- if eq $summaryText "" -}}{{- $summaryText = printf "Post %s" (string (.Params.id | default "")) -}}{{- end -}}
130
+
131
+ {{- /* Whether the timeline cut the text of any post the entry's card shows.
132
+ Per post it is written into front matter by the exporter, which shares
133
+ the boundary with the served feed; Hugo's own summary rule would cut
134
+ somewhere else. Per entry the rule is `isEntryTruncated` in
135
+ `lib/feed.ts`: the root plus every reply the fold keeps — the first
136
+ THREAD_LEADING_REPLIES (2) and the last THREAD_TRAILING_REPLIES (3),
137
+ newest included — as `lib/thread-fold.ts` defines it. A thread of five
138
+ replies or fewer is shown whole; `first` and `last` clamp to the slice,
139
+ so the two windows simply overlap there. */ -}}
140
+ {{- $replies := (where $post.Pages "Params.visibility" "public").ByWeight -}}
141
+ {{- $truncated := $post.Params.truncated | default false -}}
142
+ {{- range first 2 $replies -}}
143
+ {{- if .Params.truncated -}}{{- $truncated = true -}}{{- end -}}
144
+ {{- end -}}
145
+ {{- range last 3 $replies -}}
146
+ {{- if .Params.truncated -}}{{- $truncated = true -}}{{- end -}}
147
+ {{- end -}}
148
+
149
+ {{- /* Every attachment in the thread, each paired with the post that carries
150
+ it — a text attachment's page lives under its own post. */ -}}
151
+ {{- $entryMedia := slice -}}
152
+ {{- range ($post.Params.media | default slice) -}}
153
+ {{- $entryMedia = $entryMedia | append (dict "m" . "page" $permalink) -}}
154
+ {{- end -}}
155
+ {{- range $replies -}}
156
+ {{- $replyPermalink := .Permalink -}}
157
+ {{- range (.Params.media | default slice) -}}
158
+ {{- $entryMedia = $entryMedia | append (dict "m" . "page" $replyPermalink) -}}
159
+ {{- end -}}
160
+ {{- end -}}
132
161
  <entry>
133
162
  <title>{{ $entryTitle | transform.XMLEscape }}</title>
134
163
  <link href="{{ $alternate | transform.XMLEscape }}" rel="alternate"/>
135
164
  {{- if $isLink }}
136
165
  <link href="{{ $permalink | transform.XMLEscape }}" rel="related"/>
137
166
  {{- end }}
138
- <id>{{ $permalink | transform.XMLEscape }}</id>
167
+ {{- /* Images carry no enclosure: the content already shows them full size
168
+ inside a link to the original, so one would only ask an attachment
169
+ shelf to list the picture again. */ -}}
170
+ {{- range $entryMedia }}
171
+ {{- $m := .m }}
172
+ {{- if ne (string ($m.kind | default "document")) "image" }}
173
+ <link rel="enclosure" type="{{ (string ($m.mime_type | default "application/octet-stream")) | transform.XMLEscape }}" href="{{ (string $m.src) | absURL | transform.XMLEscape }}"{{ with $m.size }} length="{{ . }}"{{ end }}{{ with $m.original_name }} title="{{ . | transform.XMLEscape }}"{{ end }}/>
174
+ {{- end }}
175
+ {{- end }}
176
+ <id>{{ $entryId | transform.XMLEscape }}</id>
139
177
  <published>{{ $published }}</published>
140
178
  <updated>{{ $updated }}</updated>
179
+ <jant:format>{{ $format | transform.XMLEscape }}</jant:format>
180
+ {{- if $truncated }}
181
+ <jant:truncated/>
182
+ {{- end }}
183
+ {{- /* A collection is a label the author chose, which is what <category>
184
+ is for. `jant:page` carries the URL because a single collection lives
185
+ in the root URL namespace. */ -}}
186
+ {{- range ($post.Params.collections | default slice) }}
187
+ <category term="{{ (string .slug) | transform.XMLEscape }}" label="{{ (string (.title | default .slug)) | transform.XMLEscape }}" jant:page="{{ (printf "/%s/" (string .slug)) | absURL | transform.XMLEscape }}"/>
188
+ {{- end }}
189
+ {{- range $entryMedia }}
190
+ {{- $m := .m }}
191
+ {{- $kind := string ($m.kind | default "document") }}
192
+ {{- $medium := cond (or (eq $kind "image") (or (eq $kind "video") (eq $kind "audio"))) $kind "document" }}
193
+ {{- $fileUrl := (string $m.src) | absURL }}
194
+ {{- /* Where a click should land. Only emitted where it is not the file:
195
+ a text attachment's markdown would download or dump unstyled. */ -}}
196
+ {{- $pageUrl := $fileUrl }}
197
+ {{- if eq $kind "text" }}{{ $pageUrl = printf "%s/text/%s" (strings.TrimSuffix "/" .page) (string $m.id) }}{{ end }}
198
+ {{- $desc := string ($m.alt | default ($m.summary | default "")) }}
199
+ {{- $poster := string ($m.poster | default "") }}
200
+ <media:content url="{{ $fileUrl | transform.XMLEscape }}" type="{{ (string ($m.mime_type | default "application/octet-stream")) | transform.XMLEscape }}" medium="{{ $medium }}"{{ with $m.size }} fileSize="{{ . }}"{{ end }}{{ with $m.width }} width="{{ . }}"{{ end }}{{ with $m.height }} height="{{ . }}"{{ end }}{{ with $m.duration_seconds }} duration="{{ math.Round . }}"{{ end }}{{ if ne $pageUrl $fileUrl }} jant:page="{{ $pageUrl | transform.XMLEscape }}"{{ end }}>
201
+ {{- with $m.original_name }}<media:title type="plain">{{ . | transform.XMLEscape }}</media:title>{{ end }}
202
+ {{- if ne $desc "" }}<media:description type="plain">{{ $desc | transform.XMLEscape }}</media:description>{{ end }}
203
+ {{- if ne $poster "" }}<media:thumbnail url="{{ $poster | absURL | transform.XMLEscape }}"/>{{ end -}}
204
+ </media:content>
205
+ {{- end }}
141
206
  <summary type="text">{{ $summaryText | transform.XMLEscape }}</summary>
142
207
  <content type="html"><![CDATA[
143
208
  {{- partial "feed-post-content.xml" (dict "post" $post "permalink" $permalink "includeReplies" true) -}}
@@ -1,9 +1,9 @@
1
1
  {{ define "main" }}
2
2
  {{- $items := slice -}}
3
- {{- with hugo.Data.jant -}}{{- with .directory -}}{{- $items = . -}}{{- end -}}{{- end -}}
3
+ {{- with partial "jant-data.html" . -}}{{- with .directory -}}{{- $items = . -}}{{- end -}}{{- end -}}
4
4
  {{- $collectionCount := 0 -}}
5
5
  {{- range $items -}}
6
- {{- if eq .type "collection" -}}{{- $collectionCount = add $collectionCount 1 -}}{{- end -}}
6
+ {{- if in (slice "collection" "smart_collection") .type -}}{{- $collectionCount = add $collectionCount 1 -}}{{- end -}}
7
7
  {{- end -}}
8
8
 
9
9
  <div class="section section-collections" data-page="collections">
@@ -68,7 +68,7 @@
68
68
  {{- end -}}
69
69
  </div>
70
70
  </div>
71
- {{- else if eq .type "collection" -}}
71
+ {{- else if in (slice "collection" "smart_collection") .type -}}
72
72
  {{- $entryCount := .entry_count -}}
73
73
  {{- $activityLabel := .recent_activity_label -}}
74
74
  {{- $activityIso := .recent_activity_iso -}}
@@ -1,9 +1,6 @@
1
1
  {{ define "main" }}
2
2
  {{- /* Root bundles carry a derived projection of every Featured Post in the Thread. */ -}}
3
- {{- $posts := where .Site.Pages "Type" "post" -}}
4
- {{- $roots := where $posts "Kind" "section" -}}
5
- {{- $featured := where $roots "Params.featured_sort_at" "ne" nil -}}
6
- {{- $featured = sort $featured "Params.featured_sort_at" "desc" -}}
3
+ {{- $featured := partial "featured-members.html" . -}}
7
4
  {{- $pageSize := .Site.Params.page_size | default 10 -}}
8
5
  {{- $paginator := .Paginate $featured $pageSize -}}
9
6
 
@@ -1,28 +1,42 @@
1
- {{ define "main" }} {{- $pageSize := .Site.Params.page_size | default 10 -}} {{-
2
- /* Root posts are branch bundles (kind=section), so we iterate .Site.Pages, not
3
- .Site.RegularPages. */ -}} {{- $allPosts := where .Site.Pages "Type" "post" -}}
4
- {{- $pinned := where $allPosts "Params.pinned_at" "ne" nil -}} {{- $pinned =
5
- $pinned.ByDate.Reverse -}} {{- $public := where $allPosts "Params.visibility"
6
- "public" -}} {{- $public = where $public "Params.pinned_at" nil -}} {{- $main :=
7
- $public.ByDate.Reverse -}} {{- $paginator := .Paginate $main $pageSize -}}
8
-
9
- <section class="home">
10
- {{- if and (eq $paginator.PageNumber 1) $pinned -}}
11
- <div class="post-list post-list-pinned" aria-label="Pinned posts">
12
- {{- range $i, $p := $pinned -}} {{- if gt $i 0 -}}
13
- <hr class="feed-divider" aria-hidden="true" />
14
- {{- end -}} {{ partial "thread-preview.html" $p }} {{- end -}}
15
- </div>
16
- {{- end -}} {{- if $paginator.Pages -}}
17
- <div class="post-list post-list-main">
18
- {{- range $i, $p := $paginator.Pages -}} {{- if gt $i 0 -}}
19
- <hr class="feed-divider" aria-hidden="true" />
20
- {{- end -}} {{ partial "thread-preview.html" $p }} {{- end -}}
21
- </div>
22
- {{ partial "pagination.html" $paginator }} {{- else if not $pinned -}}
23
- <p class="empty-state">
24
- Nothing published yet. Write your first post to get started.
25
- </p>
1
+ {{ define "main" }}
2
+ {{- /*
3
+ The home page lists Latest the way Jant's does: one list, pinned roots
4
+ first, paginated as a whole (`latest-members`). The pinned roots on a
5
+ page, which only lead the list, keep a block of their own.
6
+ */ -}}
7
+ {{- $pageSize := .Site.Params.page_size | default 10 -}}
8
+ {{- $paginator := .Paginate (partial "latest-members.html" (dict "feed" false)) $pageSize -}}
9
+ {{- $pinned := slice -}}
10
+ {{- $main := slice -}}
11
+ {{- range $paginator.Pages -}}
12
+ {{- if .Params.pinned_at -}}
13
+ {{- $pinned = $pinned | append . -}}
14
+ {{- else -}}
15
+ {{- $main = $main | append . -}}
16
+ {{- end -}}
26
17
  {{- end -}}
27
- </section>
18
+
19
+ <section class="home">
20
+ {{- if $pinned -}}
21
+ <div class="post-list post-list-pinned" aria-label="Pinned posts">
22
+ {{- range $i, $p := $pinned -}}
23
+ {{- if gt $i 0 -}}<hr class="feed-divider" aria-hidden="true" />{{- end -}}
24
+ {{ partial "thread-preview.html" $p }}
25
+ {{- end -}}
26
+ </div>
27
+ {{- end -}}
28
+ {{- if $main -}}
29
+ <div class="post-list post-list-main">
30
+ {{- range $i, $p := $main -}}
31
+ {{- if gt $i 0 -}}<hr class="feed-divider" aria-hidden="true" />{{- end -}}
32
+ {{ partial "thread-preview.html" $p }}
33
+ {{- end -}}
34
+ </div>
35
+ {{- end -}}
36
+ {{- if $paginator.Pages -}}
37
+ {{ partial "pagination.html" $paginator }}
38
+ {{- else -}}
39
+ <p class="empty-state">Nothing published yet. Write your first post to get started.</p>
40
+ {{- end -}}
41
+ </section>
28
42
  {{ end }}
@@ -0,0 +1,100 @@
1
+ {{- /*
2
+ The Thread roots in a collection, in the order Jant lists them: the page
3
+ (`listCollectionThreadRootIdsForCollections` in `services/post.ts`) or the
4
+ feed (`listCollectionFeedEntriesForCollections`).
5
+
6
+ Takes a dict: `page`, the collection's section page, and `feed`, true for
7
+ its Atom feed.
8
+
9
+ A Thread belongs when its root lists the collection in `collections`.
10
+ Drafts and private roots are exported as Hugo drafts and never reach here;
11
+ they are checked again so a `--buildDrafts` preview agrees with Jant.
12
+
13
+ The page puts Threads pinned in this collection first, most recently
14
+ pinned first, then orders by `sort_order`, each order ending on the root
15
+ ID so no two Threads tie:
16
+ newest Thread activity (`last_activity_at`, else `date`), newest first
17
+ oldest the Thread's earliest published post, oldest first
18
+ rating_desc Threads with a rating before those without, by the highest
19
+ rating on any published post in the Thread, then as newest.
20
+ With fewer than two rated Threads it falls back to newest,
21
+ as Jant's page does.
22
+ The feed ignores pins and `sort_order`: Thread activity, newest first.
23
+
24
+ `src/__tests__/export-collection-order.test.ts` builds a site with Hugo
25
+ and compares the page and the feed with Jant's.
26
+ */ -}}
27
+ {{- $page := .page -}}
28
+ {{- $feed := .feed -}}
29
+ {{- $slug := $page.Params.slug | default $page.Slug -}}
30
+ {{- $roots := where (where site.Pages "Type" "post") "Kind" "section" -}}
31
+ {{- $members := slice -}}
32
+ {{- range $roots -}}
33
+ {{- $post := . -}}
34
+ {{- $params := .Params -}}
35
+ {{- $entry := false -}}
36
+ {{- range $params.collections -}}
37
+ {{- if eq .slug $slug -}}{{- $entry = . -}}{{- end -}}
38
+ {{- end -}}
39
+ {{- $visible := and (eq ($params.status | default "published") "published") (ne $params.visibility "private") -}}
40
+ {{- if and $entry $visible -}}
41
+ {{- $pinned := 0 -}}
42
+ {{- with $entry.pinned_at -}}{{- $pinned = (time.AsTime .).Unix -}}{{- end -}}
43
+ {{- $activity := $post.Date.Unix -}}
44
+ {{- with $params.last_activity_at -}}{{- $activity = (time.AsTime .).Unix -}}{{- end -}}
45
+ {{- $first := $post.Date.Unix -}}
46
+ {{- $rated := ne $params.rating nil -}}
47
+ {{- $rating := 0 -}}
48
+ {{- if $rated -}}{{- $rating = int $params.rating -}}{{- end -}}
49
+ {{- range $post.Pages -}}
50
+ {{- if eq (.Params.status | default "published") "published" -}}
51
+ {{- if lt .Date.Unix $first -}}{{- $first = .Date.Unix -}}{{- end -}}
52
+ {{- if ne .Params.rating nil -}}
53
+ {{- $rated = true -}}
54
+ {{- if gt (int .Params.rating) $rating -}}{{- $rating = int .Params.rating -}}{{- end -}}
55
+ {{- end -}}
56
+ {{- end -}}
57
+ {{- end -}}
58
+ {{- $members = $members | append (dict
59
+ "post" $post
60
+ "id" $params.id
61
+ "pinned" (cond $feed 0 $pinned)
62
+ "first" $first
63
+ "activity" $activity
64
+ "rated" $rated
65
+ "rating" $rating
66
+ ) -}}
67
+ {{- end -}}
68
+ {{- end -}}
69
+
70
+ {{- $order := "newest" -}}
71
+ {{- if not $feed -}}
72
+ {{- $order = $page.Params.sort_order | default "newest" -}}
73
+ {{- if and (eq $order "rating_desc") (le (len (where $members "rated" true)) 1) -}}
74
+ {{- $order = "newest" -}}
75
+ {{- end -}}
76
+ {{- end -}}
77
+
78
+ {{- /* One string key per Thread, fixed-width numbers first, so a single sort
79
+ orders by every key in turn. `oldest` sorts ascending, so its pin rank
80
+ counts down: the most recently pinned Thread gets the smallest. */ -}}
81
+ {{- $unpinnedRank := 99999999999 -}}
82
+ {{- $keyed := slice -}}
83
+ {{- range $members -}}
84
+ {{- $key := printf "%020d %020d %s" .pinned .activity .id -}}
85
+ {{- if eq $order "oldest" -}}
86
+ {{- $pinRank := cond (gt .pinned 0) (sub $unpinnedRank .pinned) $unpinnedRank -}}
87
+ {{- $key = printf "%020d %020d %s" $pinRank .first .id -}}
88
+ {{- else if eq $order "rating_desc" -}}
89
+ {{- $key = printf "%020d %d %02d %020d %s" .pinned (cond .rated 1 0) .rating .activity .id -}}
90
+ {{- end -}}
91
+ {{- $keyed = $keyed | append (dict "key" $key "post" .post) -}}
92
+ {{- end -}}
93
+
94
+ {{- $ordered := slice -}}
95
+ {{- if $keyed -}}
96
+ {{- range sort $keyed "key" (cond (eq $order "oldest") "asc" "desc") -}}
97
+ {{- $ordered = $ordered | append .post -}}
98
+ {{- end -}}
99
+ {{- end -}}
100
+ {{- return $ordered -}}
@@ -0,0 +1,33 @@
1
+ {{- /*
2
+ A collection page's Threads, each root with its replies in full, then the
3
+ pagination. Shared by manual and smart collections, which look the same to
4
+ a reader.
5
+
6
+ Takes a dict: `paginator`, and `empty`, the text shown when there are no
7
+ Threads.
8
+ */ -}}
9
+ {{- $paginator := .paginator -}}
10
+ {{- if $paginator.Pages -}}
11
+ <div class="post-list">
12
+ {{- range $i, $p := $paginator.Pages -}}
13
+ {{- if gt $i 0 -}}<hr class="feed-divider" aria-hidden="true" />{{- end -}}
14
+ {{- $replies := $p.Pages.ByWeight -}}
15
+ {{- $hasReplies := gt (len $replies) 0 -}}
16
+ <div class="thread thread-full{{ if $hasReplies }} thread-has-replies{{ end }}" data-slug="{{ $p.Params.slug | default $p.Slug }}">
17
+ <div class="thread-item thread-item-root">
18
+ {{ partial "post-card.html" $p }}
19
+ </div>
20
+ {{- if $hasReplies -}}
21
+ <section class="thread-replies" aria-label="Replies">
22
+ {{- range $replies -}}
23
+ {{ partial "reply.html" . }}
24
+ {{- end -}}
25
+ </section>
26
+ {{- end -}}
27
+ </div>
28
+ {{- end -}}
29
+ </div>
30
+ {{ partial "pagination.html" $paginator }}
31
+ {{- else -}}
32
+ <p class="empty-state">{{ .empty }}</p>
33
+ {{- end -}}
@@ -0,0 +1,35 @@
1
+ {{- /*
2
+ The Threads on Featured, in the order Jant lists them on the Featured page
3
+ and in `/featured/feed` (`listFeaturedThreadRootIds`): a Thread belongs
4
+ when any published post in it is featured and it is not private. Hidden
5
+ from Latest is no bar here.
6
+
7
+ Order: the newest publication among the Thread's featured posts
8
+ (`featured_sort_at`, which the export derives exactly that way), newest
9
+ first, then the root ID, newest first, so Threads whose featured posts
10
+ share a second don't tie.
11
+
12
+ `src/__tests__/export-feed-order.test.ts` builds a site with Hugo and
13
+ compares the page and the feed with Jant's.
14
+ */ -}}
15
+ {{- $roots := where (where site.Pages "Type" "post") "Kind" "section" -}}
16
+ {{- $keyed := slice -}}
17
+ {{- range $roots -}}
18
+ {{- $post := . -}}
19
+ {{- $params := .Params -}}
20
+ {{- $visible := and (eq ($params.status | default "published") "published") (ne $params.visibility "private") -}}
21
+ {{- if and $visible $params.featured_sort_at -}}
22
+ {{- $keyed = $keyed | append (dict
23
+ "key" (printf "%020d %s" (time.AsTime $params.featured_sort_at).Unix $params.id)
24
+ "post" $post
25
+ ) -}}
26
+ {{- end -}}
27
+ {{- end -}}
28
+
29
+ {{- $ordered := slice -}}
30
+ {{- if $keyed -}}
31
+ {{- range sort $keyed "key" "desc" -}}
32
+ {{- $ordered = $ordered | append .post -}}
33
+ {{- end -}}
34
+ {{- end -}}
35
+ {{- return $ordered -}}
@@ -8,7 +8,7 @@
8
8
  {{- $root := . -}}
9
9
  {{- $featuredIds := $root.Params.featured_post_ids | default slice -}}
10
10
  {{- $posts := slice $root -}}
11
- {{- range $root.Pages.ByDate -}}
11
+ {{- range $root.Pages.ByWeight -}}
12
12
  {{- $posts = $posts | append . -}}
13
13
  {{- end -}}
14
14
  {{- $lastIndex := sub (len $posts) 1 -}}