@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
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The repository name Jant proposes for a site's GitHub Sync mirror.
3
+ *
4
+ * The settings page prefills it on github.com/new, and a site export with no
5
+ * repository behind it uses it as the Worker name in `wrangler.jsonc`.
6
+ * Cloudflare names a Worker imported from a repository after the repository,
7
+ * so an export and a repository created with the default name agree without
8
+ * anyone editing either.
9
+ */
10
+
11
+ /** Used when the site URL yields no host label. */
12
+ const FALLBACK_REPO_NAME = "jant-site-sync";
13
+
14
+ /**
15
+ * Derive the default repository name for a site's sync mirror.
16
+ *
17
+ * Uses the first DNS label of the site's host — a stable, URL-safe
18
+ * identifier tied to this Jant instance. A leading `www` is skipped: it
19
+ * names nothing about the site, and every `www.` site would otherwise
20
+ * propose the same `www-jant-sync`. It is kept when only a top-level domain
21
+ * follows it, since that label would say even less. The `-jant-sync` suffix
22
+ * tells the sync mirror apart from a user's own `{slug}-jant` source repo.
23
+ *
24
+ * @param siteUrl - The site's public URL.
25
+ * @returns A GitHub-safe repository name; `jant-site-sync` when the URL has
26
+ * no usable host.
27
+ * @example
28
+ * suggestSyncRepoName("https://notes.example.com"); // "notes-jant-sync"
29
+ * suggestSyncRepoName("https://www.example.com"); // "example-jant-sync"
30
+ */
31
+ export function suggestSyncRepoName(siteUrl: string): string {
32
+ let labels: string[] = [];
33
+ try {
34
+ labels = new URL(siteUrl).host.split(".");
35
+ } catch {
36
+ /* fall through */
37
+ }
38
+ const [first = "", second = "", ...more] = labels;
39
+ const label = first === "www" && more.length > 0 ? second : first;
40
+ const slug = label
41
+ .toLowerCase()
42
+ .replace(/[^a-z0-9-_]+/g, "-")
43
+ .replace(/^-+|-+$/g, "");
44
+ return slug ? `${slug}-jant-sync` : FALLBACK_REPO_NAME;
45
+ }
@@ -29,10 +29,13 @@ export async function buildSyncSiteConfig(c: {
29
29
  siteUrl: appConfig.siteUrl,
30
30
  siteDescription: appConfig.siteDescription,
31
31
  siteLanguage: appConfig.siteLanguage,
32
+ multilingualEnabled: appConfig.multilingualEnabled,
33
+ additionalLanguages: appConfig.additionalLanguages,
32
34
  showJantBrandingOnHome: appConfig.showJantBrandingOnHome,
33
35
  publicApiEnabled: appConfig.publicApiEnabled,
34
36
  rssFeedsEnabled: appConfig.rssFeedsEnabled,
35
37
  mainRssFeed: appConfig.mainRssFeed,
38
+ archiveDefaultLayout: appConfig.archiveDefaultLayout,
36
39
  siteFooter: appConfig.siteFooter,
37
40
  showHeaderAvatar: appConfig.showHeaderAvatar,
38
41
  siteAvatarUrl: appConfig.siteAvatarUrl,
@@ -60,7 +60,7 @@ export function getHostedControlPlaneResetUrl(
60
60
  export function getHostedControlPlaneDashboardUrl(
61
61
  env: object | undefined | null,
62
62
  ): string | null {
63
- return buildHostedControlPlaneUrl(env, "/sites");
63
+ return buildHostedControlPlaneUrl(env, "/app");
64
64
  }
65
65
 
66
66
  export function getHostedControlPlaneAccountUrl(
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Conditional-request helpers.
3
+ *
4
+ * Transport-level only: deriving an entity tag for a response body, and
5
+ * deciding whether the request already holds it. Nothing here knows what the
6
+ * body represents.
7
+ */
8
+
9
+ const textEncoder = new TextEncoder();
10
+
11
+ /**
12
+ * One entity tag inside an `If-None-Match` list, weak prefix included.
13
+ *
14
+ * Matching quoted runs rather than splitting on commas is deliberate: RFC 9110
15
+ * allows a comma inside an entity tag's opaque part, so a naive split can tear
16
+ * one tag into two that match nothing.
17
+ */
18
+ const ENTITY_TAG = /(?:W\/)?"[^"]*"/g;
19
+
20
+ /** Drop the weak prefix, which the weak comparison in RFC 9110 ignores. */
21
+ function opaqueTag(tag: string): string {
22
+ return tag.startsWith("W/") ? tag.slice(2) : tag;
23
+ }
24
+
25
+ /**
26
+ * Derive a strong entity tag from a response body.
27
+ *
28
+ * The tag is a truncated SHA-256 of the exact bytes that would be sent, so
29
+ * two equal tags do mean two identical responses — which is what makes the
30
+ * tag strong rather than `W/`-weak. 128 bits is far past the point where a
31
+ * collision could plausibly pin a stale body in a cache.
32
+ *
33
+ * @param body - Serialized response body
34
+ * @returns Quoted entity tag, ready for an `ETag` header
35
+ * @example
36
+ * ```ts
37
+ * const etag = await strongETag(xml); // '"9f86d081884c7d65..."'
38
+ * ```
39
+ */
40
+ export async function strongETag(body: string): Promise<string> {
41
+ const digest = await crypto.subtle.digest(
42
+ "SHA-256",
43
+ textEncoder.encode(body),
44
+ );
45
+
46
+ let hex = "";
47
+ for (const byte of new Uint8Array(digest, 0, 16)) {
48
+ hex += byte.toString(16).padStart(2, "0");
49
+ }
50
+
51
+ return `"${hex}"`;
52
+ }
53
+
54
+ /**
55
+ * Whether a request's `If-None-Match` already holds `etag`.
56
+ *
57
+ * Comparison is the weak one RFC 9110 requires for `If-None-Match`: the weak
58
+ * prefix is ignored on both sides, and any tag in the list can match. `*`
59
+ * matches whenever the resource exists, which it does by the time a response
60
+ * body has been rendered.
61
+ *
62
+ * @param header - Raw `If-None-Match` header value, absent when not sent
63
+ * @param etag - Entity tag of the response about to be sent
64
+ * @returns `true` when the response can be answered with `304`
65
+ * @example
66
+ * ```ts
67
+ * matchesIfNoneMatch('W/"abc", "def"', '"def"'); // true
68
+ * ```
69
+ */
70
+ export function matchesIfNoneMatch(
71
+ header: string | null | undefined,
72
+ etag: string,
73
+ ): boolean {
74
+ if (!header) return false;
75
+ if (header.trim() === "*") return true;
76
+
77
+ const target = opaqueTag(etag);
78
+ for (const candidate of header.match(ENTITY_TAG) ?? []) {
79
+ if (opaqueTag(candidate) === target) return true;
80
+ }
81
+
82
+ return false;
83
+ }
84
+
85
+ /**
86
+ * Headers a `304` carries over from the response it stands in for.
87
+ *
88
+ * RFC 9110 asks for the fields a cache would otherwise have to invent or
89
+ * discard: the validator itself, the freshness the entry is stored under, and
90
+ * the identity of what was validated.
91
+ */
92
+ const NOT_MODIFIED_HEADERS = [
93
+ "Cache-Control",
94
+ "Content-Location",
95
+ "Date",
96
+ "ETag",
97
+ "Expires",
98
+ "Vary",
99
+ ] as const;
100
+
101
+ /**
102
+ * Answer a conditional request with `304` when the response is one the client
103
+ * already holds.
104
+ *
105
+ * This is the only place in the app that turns a validator into a `304`, and
106
+ * it runs at the outer edge of the request — outside the Worker response
107
+ * cache. Both halves of that matter: a cache hit is revalidated without
108
+ * reaching a route, and a cache miss still renders and stores the whole
109
+ * document before the body is dropped, so the next reader gets a cache entry
110
+ * rather than a `304` that cannot be stored.
111
+ *
112
+ * Anything without an entity tag passes through untouched, so a route opts in
113
+ * simply by emitting one.
114
+ *
115
+ * @param request - Incoming request, read for `If-None-Match`
116
+ * @param response - Response the app produced
117
+ * @returns A bare `304`, or the response unchanged
118
+ * @example
119
+ * ```ts
120
+ * return withConditionalResponse(request, await handle(request));
121
+ * ```
122
+ */
123
+ export function withConditionalResponse(
124
+ request: Request,
125
+ response: Response,
126
+ ): Response {
127
+ if (request.method !== "GET" && request.method !== "HEAD") return response;
128
+ if (response.status !== 200) return response;
129
+
130
+ // A 304 drops the body, and with it any header that only makes sense
131
+ // attached to one. A response setting a cookie is left alone rather than
132
+ // stripped down to a validator.
133
+ if (response.headers.has("Set-Cookie")) return response;
134
+
135
+ const etag = response.headers.get("ETag");
136
+ if (!etag) return response;
137
+ if (!matchesIfNoneMatch(request.headers.get("If-None-Match"), etag)) {
138
+ return response;
139
+ }
140
+
141
+ const headers = new Headers();
142
+ for (const name of NOT_MODIFIED_HEADERS) {
143
+ const value = response.headers.get(name);
144
+ if (value !== null) headers.set(name, value);
145
+ }
146
+
147
+ return new Response(null, { status: 304, headers });
148
+ }
@@ -34,6 +34,24 @@ export interface HugoCollectionRef {
34
34
  pinned_at?: string | null;
35
35
  }
36
36
 
37
+ /**
38
+ * A smart collection's conditions on its section page (`type:
39
+ * smart_collection`). The stored selection, with two changes of spelling:
40
+ * `collection` is one collection's slug rather than a list of IDs, and
41
+ * `media` is `any`, `none`, or a list of kinds. The theme's
42
+ * `smart-collection-members` partial evaluates it; an import recreates the
43
+ * smart collection from it.
44
+ */
45
+ export interface HugoSmartCollectionSelection {
46
+ collection?: string;
47
+ format?: string;
48
+ title?: boolean;
49
+ year?: number;
50
+ media?: "any" | "none" | string[];
51
+ replies?: boolean;
52
+ visibility?: string;
53
+ }
54
+
37
55
  /**
38
56
  * Jant media attachment descriptor. Stored flat inside the `media:` front-
39
57
  * matter array. `src` is either:
@@ -90,6 +108,12 @@ export interface HugoFrontMatter {
90
108
  id?: string;
91
109
  title?: string;
92
110
  date?: string;
111
+ /**
112
+ * When the post was created, written only when it differs from `date` (a
113
+ * published post dated before or after it was written). Restored on import
114
+ * so thread order and "last edited" survive a move.
115
+ */
116
+ created?: string;
93
117
  updated?: string;
94
118
  slug?: string;
95
119
  type?: string;
@@ -97,13 +121,31 @@ export interface HugoFrontMatter {
97
121
 
98
122
  // Hugo routing
99
123
  aliases?: string[];
124
+ /**
125
+ * Published root bundles only: the `<id>` Jant's feeds gave the Thread's
126
+ * entry — its permalink, absolute, with no trailing slash. The theme's feed
127
+ * writes it as the entry's `<id>` so readers don't show the post again after
128
+ * a move. Written by the export, never read by the import.
129
+ */
130
+ feed_id?: string;
100
131
  build?: HugoBuildOptions;
132
+ /**
133
+ * Reply bundles only: 1-based position in the Thread. Jant orders a Thread
134
+ * by creation time, then ID; replies written in the same second tie on
135
+ * `date`, so the theme and the importer order by this instead.
136
+ */
137
+ weight?: number;
101
138
 
102
139
  // Jant post payload (flat — no `extra` nesting)
103
140
  format?: string;
104
141
  status?: string;
105
142
  visibility?: string;
106
143
  summary_text?: string;
144
+ /**
145
+ * The timeline cuts this post's text short. Written only when true, so a
146
+ * consumer reads its presence rather than its value.
147
+ */
148
+ truncated?: boolean;
107
149
  link_url?: string;
108
150
  source_name?: string;
109
151
  source_url?: string;
@@ -111,6 +153,16 @@ export interface HugoFrontMatter {
111
153
  rating?: number | null;
112
154
  featured_at?: string | null;
113
155
  pinned_at?: string | null;
156
+ /**
157
+ * BCP 47 content language. Root bundles only — replies take the root's.
158
+ */
159
+ language?: string;
160
+ /**
161
+ * Opaque shared key linking posts that are translations of one another. Root
162
+ * bundles only. Meaningful within one export; carried across so a full-site
163
+ * restore keeps translated posts linked.
164
+ */
165
+ translation_group?: string;
114
166
  /**
115
167
  * Reply bundles only: the reply was published without announcing the Thread
116
168
  * on Latest. Round-tripped so a restore rebuilds the same ordering.
@@ -128,6 +180,14 @@ export interface HugoFrontMatter {
128
180
  collections?: HugoCollectionRef[];
129
181
  media?: JantMedia[];
130
182
 
183
+ // Smart collection section pages
184
+ selection?: HugoSmartCollectionSelection;
185
+ /**
186
+ * `list` or `grid`; absent when the smart collection follows the site's
187
+ * archive layout. Not `layout`, which Hugo reads as a template name.
188
+ */
189
+ display_layout?: string;
190
+
131
191
  // Escape hatch for page-specific export metadata.
132
192
  [key: string]: unknown;
133
193
  }
@@ -198,6 +258,7 @@ const FRONT_MATTER_KEY_ORDER: readonly string[] = [
198
258
  "type",
199
259
  "draft",
200
260
  "aliases",
261
+ "feed_id",
201
262
  "build",
202
263
 
203
264
  // Post payload
@@ -205,6 +266,7 @@ const FRONT_MATTER_KEY_ORDER: readonly string[] = [
205
266
  "status",
206
267
  "visibility",
207
268
  "summary_text",
269
+ "truncated",
208
270
  "link_url",
209
271
  "source_name",
210
272
  "source_url",
package/src/lib/icons.ts CHANGED
@@ -24,17 +24,24 @@ function toPascalCase(name: string): string {
24
24
  * Get SVG markup for a Lucide icon by kebab-case name.
25
25
  *
26
26
  * @param name - Kebab-case icon name
27
+ * @param className - Extra classes for the `<svg>`, appended to Lucide's own.
28
+ * `icon-fine` is the project's lighter stroke.
27
29
  * @returns SVG string or null when the icon is unknown
28
30
  *
29
31
  * @example
30
32
  * ```ts
31
33
  * getIconSvg("book-open");
34
+ * getIconSvg("funnel", "icon-fine");
32
35
  * ```
33
36
  */
34
- export function getIconSvg(name: string): string | null {
37
+ export function getIconSvg(name: string, className?: string): string | null {
35
38
  const pascalName = toPascalCase(name);
36
39
  const svg = (lucideIcons as Record<string, string>)[pascalName];
37
- return typeof svg === "string" ? svg : null;
40
+ if (typeof svg !== "string") return null;
41
+ if (!className) return svg;
42
+ // Lucide always ships a `class="lucide lucide-…"`, so extend it rather than
43
+ // adding a second class attribute the parser would drop.
44
+ return svg.replace(/class="([^"]*)"/, `class="$1 ${className}"`);
38
45
  }
39
46
 
40
47
  /**
package/src/lib/ids.ts CHANGED
@@ -5,9 +5,12 @@ export const ID_PREFIX = {
5
5
  site: "sit",
6
6
  siteDomain: "sdm",
7
7
  post: "pst",
8
+ /** Shared key linking Posts that are translations of one another. */
9
+ translationGroup: "tgr",
8
10
  media: "med",
9
11
  uploadSession: "upl",
10
12
  collection: "col",
13
+ smartCollection: "smc",
11
14
  path: "pth",
12
15
  collectionDirectoryItem: "cdi",
13
16
  navItem: "nav",
package/src/lib/image.ts CHANGED
@@ -34,7 +34,11 @@ export interface ImageOptions {
34
34
  * - Cloudinary
35
35
  * - Any service with similar URL-based transformation API
36
36
  *
37
- * @param originalUrl - The original image URL
37
+ * A root-relative source is written without its leading slash, the form the
38
+ * transformation service resolves against its own host. Kept, it would produce
39
+ * `…/width=200//media/abc123`, which Cloudflare cannot fetch (`err=9404`).
40
+ *
41
+ * @param originalUrl - The original image URL, absolute or root-relative
38
42
  * @param transformUrl - The base URL for transformations (e.g., `https://example.com/cdn-cgi/image`)
39
43
  * @param options - Transformation options (width, height, quality, format, fit)
40
44
  * @returns The transformed URL or original URL if transformations are not configured
@@ -45,9 +49,13 @@ export interface ImageOptions {
45
49
  * getImageUrl("/media/abc123", undefined, { width: 200 });
46
50
  * // Returns: "/media/abc123"
47
51
  *
48
- * // With transform URL - returns transformed
52
+ * // With transform URL and a root-relative source
49
53
  * getImageUrl("/media/abc123", "https://example.com/cdn-cgi/image", { width: 200, quality: 80 });
50
- * // Returns: "https://example.com/cdn-cgi/image/width=200,quality=80/https://example.com/media/abc123"
54
+ * // Returns: "https://example.com/cdn-cgi/image/width=200,quality=80/media/abc123"
55
+ *
56
+ * // With transform URL and an absolute source
57
+ * getImageUrl("https://cdn.example.com/media/abc123", "https://example.com/cdn-cgi/image", { width: 200 });
58
+ * // Returns: "https://example.com/cdn-cgi/image/width=200/https://cdn.example.com/media/abc123"
51
59
  * ```
52
60
  */
53
61
  export function getImageUrl(
@@ -70,7 +78,12 @@ export function getImageUrl(
70
78
  return originalUrl;
71
79
  }
72
80
 
73
- return `${transformUrl}/${params.join(",")}/${originalUrl}`;
81
+ const base = transformUrl.replace(/\/+$/, "");
82
+ const source =
83
+ originalUrl.startsWith("/") && !originalUrl.startsWith("//")
84
+ ? originalUrl.slice(1)
85
+ : originalUrl;
86
+ return `${base}/${params.join(",")}/${source}`;
74
87
  }
75
88
 
76
89
  /**
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Links into the published documentation
3
+ *
4
+ * Everything under `docs/` is published on the Jant site: `docs/multilingual.md`
5
+ * is served at `/docs/multilingual`, lowercased and without the extension.
6
+ *
7
+ * The host is resolved once, here, so no call site writes a domain. Development
8
+ * builds point at the control plane running locally, which serves the docs
9
+ * being edited rather than the released ones.
10
+ */
11
+
12
+ import { IS_VITE_DEV } from "./build-env.js";
13
+
14
+ /** Docs host for released builds. */
15
+ const DOCS_ORIGIN = "https://jant.me";
16
+
17
+ /** Docs host during development. */
18
+ const DEV_DOCS_ORIGIN = "https://jant-cloud.localtest.me";
19
+
20
+ /** Documentation index for the current environment. */
21
+ export const JANT_DOCS_BASE_URL = `${
22
+ IS_VITE_DEV ? DEV_DOCS_ORIGIN : DOCS_ORIGIN
23
+ }/docs`;
24
+
25
+ /**
26
+ * Build a link to one documentation page.
27
+ *
28
+ * @param page - Published page slug, optionally with a `#section` anchor. It is
29
+ * the file name under `docs/`, lowercased and without `.md`. Omit it to link
30
+ * the documentation index.
31
+ * @returns Absolute URL to that page on the current environment's docs site
32
+ * @example
33
+ * ```ts
34
+ * getJantDocsUrl("multilingual"); // "https://jant.me/docs/multilingual"
35
+ * getJantDocsUrl("configuration#required");
36
+ * // "https://jant.me/docs/configuration#required"
37
+ * getJantDocsUrl(); // "https://jant.me/docs"
38
+ * ```
39
+ */
40
+ export function getJantDocsUrl(page = ""): string {
41
+ return page ? `${JANT_DOCS_BASE_URL}/${page}` : JANT_DOCS_BASE_URL;
42
+ }