@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
@@ -13,12 +13,27 @@ import type { FC } from "hono/jsx";
13
13
  import { MAX_THREAD_POSTS, type Collection } from "../../types.js";
14
14
  import { useLingui } from "../../i18n/context.js";
15
15
  import { getCollectionFormLabels } from "../shared/collection-management-labels.js";
16
- import type { ComposeLabels } from "../../client/components/compose-types.js";
16
+ import type {
17
+ ComposeLabels,
18
+ ComposeLanguage,
19
+ } from "../../client/components/compose-types.js";
17
20
 
18
21
  export interface ComposeDialogProps {
19
22
  collections?: Collection[];
20
23
  uploadMaxFileSize?: number;
21
24
  slashCommandDiscovered?: boolean;
25
+ /**
26
+ * Languages the site publishes. Empty on a single-language site, and the
27
+ * composer then shows no language UI at all — an author who never turned
28
+ * multilingual content on should not meet it.
29
+ */
30
+ languages?: ComposeLanguage[];
31
+ /**
32
+ * Content language of the page the composer opens from. A post left on
33
+ * automatic publishes in this language until detection reads the writing as
34
+ * another one, which the composer says on the button beside Post.
35
+ */
36
+ contextLanguage?: string | null;
22
37
  }
23
38
 
24
39
  export interface ComposeFormProps extends ComposeDialogProps {
@@ -31,6 +46,8 @@ export const ComposeForm: FC<ComposeFormProps> = ({
31
46
  collections,
32
47
  uploadMaxFileSize,
33
48
  slashCommandDiscovered = false,
49
+ languages,
50
+ contextLanguage,
34
51
  pageMode = false,
35
52
  closeHref,
36
53
  autoRestoreDraft = false,
@@ -41,7 +58,15 @@ export const ComposeForm: FC<ComposeFormProps> = ({
41
58
  cancel: i18n._(
42
59
  msg({
43
60
  message: "Cancel",
44
- comment: "@context: Close compose dialog",
61
+ comment:
62
+ "@context: Back out of the attached-text sub-editor, paired with Done. Leaving the composer itself says Close instead — it may keep the work as a draft, so nothing is being cancelled.",
63
+ }),
64
+ ),
65
+ close: i18n._(
66
+ msg({
67
+ message: "Close",
68
+ comment:
69
+ "@context: Leaves the composer — the × on a single post, and the button in a thread. Prompts to save a draft first if there is unsaved work.",
45
70
  }),
46
71
  ),
47
72
  imageNotRehosted: i18n._(
@@ -322,12 +347,39 @@ export const ComposeForm: FC<ComposeFormProps> = ({
322
347
  comment: "@context: Button to remove an uploaded attachment in compose",
323
348
  }),
324
349
  ),
350
+ moveAttachmentEarlier: i18n._(
351
+ msg({
352
+ message: "Move attachment earlier",
353
+ comment:
354
+ "@context: Button that moves an attachment one place toward the start in compose",
355
+ }),
356
+ ),
357
+ moveAttachmentLater: i18n._(
358
+ msg({
359
+ message: "Move attachment later",
360
+ comment:
361
+ "@context: Button that moves an attachment one place toward the end in compose",
362
+ }),
363
+ ),
325
364
  uploading: i18n._(
326
365
  msg({
327
366
  message: "Uploading...",
328
367
  comment: "@context: Toast shown during background upload",
329
368
  }),
330
369
  ),
370
+ publishing: i18n._(
371
+ msg({
372
+ message: "Publishing...",
373
+ comment: "@context: Toast shown while a post is being published",
374
+ }),
375
+ ),
376
+ saving: i18n._(
377
+ msg({
378
+ message: "Saving...",
379
+ comment:
380
+ "@context: Toast shown while a draft or an edited post is being saved",
381
+ }),
382
+ ),
331
383
  loadingPost: i18n._(
332
384
  msg({
333
385
  message: "Loading post...",
@@ -391,9 +443,9 @@ export const ComposeForm: FC<ComposeFormProps> = ({
391
443
  ),
392
444
  confirmCloseCancel: i18n._(
393
445
  msg({
394
- message: "Cancel",
446
+ message: "Keep editing",
395
447
  comment:
396
- "@context: Confirm close action sheet - cancel and return to editor",
448
+ "@context: Confirm close action sheet - dismiss the sheet and return to the editor. Says what it does rather than 'Cancel', which the composer already spends on leaving.",
397
449
  }),
398
450
  ),
399
451
  confirmCloseDiscard: i18n._(
@@ -510,6 +562,20 @@ export const ComposeForm: FC<ComposeFormProps> = ({
510
562
  "@context: Toast when publish fails and post is auto-saved as draft",
511
563
  }),
512
564
  ),
565
+ publishFailed: i18n._(
566
+ msg({
567
+ message: "Couldn't publish. Try again.",
568
+ comment:
569
+ "@context: Toast when publish fails and the server gave no reason",
570
+ }),
571
+ ),
572
+ publishFailedOffline: i18n._(
573
+ msg({
574
+ message: "Couldn't reach the server. Try again.",
575
+ comment:
576
+ "@context: Toast when a publish request never reached the server",
577
+ }),
578
+ ),
513
579
  uploadFailedDraft: i18n._(
514
580
  msg({
515
581
  message: "Some uploads failed. Saved as draft.",
@@ -795,11 +861,11 @@ export const ComposeForm: FC<ComposeFormProps> = ({
795
861
  "@context: Toast shown when a local draft is restored on compose open",
796
862
  }),
797
863
  ),
798
- closeCompose: i18n._(
864
+ draftStoreFailed: i18n._(
799
865
  msg({
800
- message: "Close compose",
866
+ message: "This browser can't keep a local copy. Save as a draft.",
801
867
  comment:
802
- "@context: Post options row that leaves the composer; prompts to save a draft first if there is unsaved work",
868
+ "@context: Toast shown when the composer cannot write its local draft to browser storage",
803
869
  }),
804
870
  ),
805
871
  editing: i18n._(
@@ -897,10 +963,132 @@ export const ComposeForm: FC<ComposeFormProps> = ({
897
963
  }),
898
964
  ),
899
965
  },
966
+ languageLabel: i18n._(
967
+ msg({
968
+ message: "Language",
969
+ comment: "@context: Compose field for the post's content language",
970
+ }),
971
+ ),
972
+ languageAuto: i18n._(
973
+ msg({
974
+ message: "Detect",
975
+ comment:
976
+ "@context: Compose language option that leaves detection to Jant",
977
+ }),
978
+ ),
979
+ languageAutoHint: i18n._(
980
+ msg({
981
+ message: "Read from what you write",
982
+ comment: "@context: Help text under the compose language field",
983
+ }),
984
+ ),
985
+ languageAutoDetected: i18n._(
986
+ msg({
987
+ message: "Read from what you write — looks like {language}",
988
+ comment:
989
+ "@context: Help text under the compose language field once a language has been detected",
990
+ }),
991
+ // Detected in the browser from the current text; see `translationOf`.
992
+ { language: "{language}" },
993
+ ),
994
+ languageAutoPending: i18n._(
995
+ msg({
996
+ message: "Not enough text to tell yet — publishes in {language}",
997
+ comment:
998
+ "@context: Help text under the compose language field before there is enough writing to read a language out of; {language} is the one it would publish in meanwhile, which is the language of the page the composer was opened from",
999
+ }),
1000
+ // Only known in the browser; see `translationOf`.
1001
+ { language: "{language}" },
1002
+ ),
1003
+ languageTriggerLabel: i18n._(
1004
+ msg({
1005
+ message: "Language: {language}",
1006
+ comment:
1007
+ "@context: Accessible name of the composer's language button, which sits beside Post and names the language this post would publish in",
1008
+ }),
1009
+ // Filled in the browser with whichever language that is; see
1010
+ // `translationOf`.
1011
+ { language: "{language}" },
1012
+ ),
1013
+ translationOf: i18n._(
1014
+ msg({
1015
+ message: "Translation of “{title}”",
1016
+ comment:
1017
+ "@context: Compose banner shown while writing a translation of an existing post",
1018
+ }),
1019
+ // The post being translated is only known in the browser, and Lingui
1020
+ // resolves an ICU message completely at `i18n._()` time — formatted with
1021
+ // no value, this slot would come out empty. Passing the placeholder back
1022
+ // as the value leaves it intact for the component to fill.
1023
+ { title: "{title}" },
1024
+ ),
1025
+ translationContext: i18n._(
1026
+ msg({
1027
+ message: "Translating",
1028
+ comment:
1029
+ "@context: Divider between the original post and the editor writing its translation, when the target language is not known yet",
1030
+ }),
1031
+ ),
1032
+ translationContextInLanguage: i18n._(
1033
+ msg({
1034
+ message: "Translating into {language}",
1035
+ comment:
1036
+ "@context: Divider between the original post above and the editor below, naming the language being written",
1037
+ }),
1038
+ // Filled in the browser from the chosen language; see `translationOf`.
1039
+ { language: "{language}" },
1040
+ ),
1041
+ translationContextOpen: i18n._(
1042
+ msg({
1043
+ message: "Open the original in a new tab",
1044
+ comment:
1045
+ "@context: Tooltip on the source post link above the compose editor while writing a translation",
1046
+ }),
1047
+ ),
1048
+ translationContextOriginal: i18n._(
1049
+ msg({
1050
+ message: "The original",
1051
+ comment:
1052
+ "@context: Accessible name of the scrollable panel holding the post being translated",
1053
+ }),
1054
+ ),
1055
+ translationContextHide: i18n._(
1056
+ msg({
1057
+ message: "Hide",
1058
+ comment:
1059
+ "@context: Button that folds away the original while writing its translation. Sits right after the words “Translating into …”, which is what makes it clear what gets hidden — the accessible name spells it out",
1060
+ }),
1061
+ ),
1062
+ translationContextHideLong: i18n._(
1063
+ msg({
1064
+ message: "Hide the original",
1065
+ comment:
1066
+ "@context: Accessible name of the button that folds away the post being translated",
1067
+ }),
1068
+ ),
1069
+ translationContextShow: i18n._(
1070
+ msg({
1071
+ message: "Show",
1072
+ comment:
1073
+ "@context: Button that brings back the folded-away original while writing its translation",
1074
+ }),
1075
+ ),
1076
+ translationContextShowLong: i18n._(
1077
+ msg({
1078
+ message: "Show the original",
1079
+ comment:
1080
+ "@context: Accessible name of the button that brings back the folded-away post being translated",
1081
+ }),
1082
+ ),
900
1083
  collectionFormLabels: getCollectionFormLabels(i18n),
901
1084
  } satisfies ComposeLabels;
902
1085
  const labels = JSON.stringify(labelsObject).replace(/</g, "\\u003c");
903
1086
 
1087
+ const languagesJson = JSON.stringify(languages ?? []).replace(
1088
+ /</g,
1089
+ "\\u003c",
1090
+ );
1091
+
904
1092
  const collectionsJson = JSON.stringify(
905
1093
  (collections ?? []).map((c) => ({
906
1094
  id: c.id,
@@ -912,6 +1100,8 @@ export const ComposeForm: FC<ComposeFormProps> = ({
912
1100
  return (
913
1101
  <jant-compose-dialog
914
1102
  collections={collectionsJson}
1103
+ languages={languagesJson}
1104
+ {...(contextLanguage ? { "context-language": contextLanguage } : {})}
915
1105
  labels={labels}
916
1106
  upload-max-file-size={uploadMaxFileSize ?? 500}
917
1107
  {...(pageMode ? { "page-mode": "" } : {})}
@@ -931,6 +1121,8 @@ export const ComposeDialog: FC<ComposeDialogProps> = ({
931
1121
  collections,
932
1122
  uploadMaxFileSize,
933
1123
  slashCommandDiscovered = false,
1124
+ languages,
1125
+ contextLanguage,
934
1126
  }) => {
935
1127
  return (
936
1128
  <dialog id="compose-dialog" class="compose-dialog">
@@ -938,6 +1130,8 @@ export const ComposeDialog: FC<ComposeDialogProps> = ({
938
1130
  collections={collections}
939
1131
  uploadMaxFileSize={uploadMaxFileSize}
940
1132
  slashCommandDiscovered={slashCommandDiscovered}
1133
+ languages={languages}
1134
+ contextLanguage={contextLanguage}
941
1135
  />
942
1136
  </dialog>
943
1137
  );
@@ -25,11 +25,7 @@ export const ComposePrompt: FC<ComposePromptProps> = ({
25
25
  composeOpenShortcutDiscovered ? "true" : "false"
26
26
  }
27
27
  >
28
- <button
29
- type="button"
30
- class="compose-prompt-trigger"
31
- data-on:click="document.getElementById('compose-dialog')?.querySelector('jant-compose-dialog')?.openNew()"
32
- >
28
+ <button type="button" class="compose-prompt-trigger" data-compose-open>
33
29
  <span class="compose-prompt-avatar">
34
30
  <svg
35
31
  xmlns="http://www.w3.org/2000/svg"
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Compose dialog labels.
3
+ *
4
+ * The composer fills two labels in the browser, from values only it knows: the
5
+ * post being translated and the detected language. Lingui resolves an ICU
6
+ * message completely at `i18n._()` time, so those slots have to be carried
7
+ * through deliberately — a label formatted with no value renders as an empty
8
+ * string, which reads as a missing title rather than as a bug.
9
+ */
10
+
11
+ import type { Context } from "hono";
12
+ import { renderToString } from "hono/jsx/dom/server";
13
+ import { describe, expect, it } from "vitest";
14
+ import { I18nProvider } from "../../../i18n/context.js";
15
+ import { createI18n } from "../../../i18n/i18n.js";
16
+ import { ComposeForm } from "../ComposeDialog.js";
17
+
18
+ function renderComposeLabels(): Record<string, string> {
19
+ const i18n = createI18n("en");
20
+ const c = {
21
+ get(key: string) {
22
+ return key === "i18n" ? i18n : undefined;
23
+ },
24
+ } as unknown as Context;
25
+ I18nProvider({ c, children: "" });
26
+
27
+ const html = renderToString(
28
+ ComposeForm({
29
+ languages: [
30
+ { tag: "zh-Hans", label: "简体中文" },
31
+ { tag: "en", label: "English" },
32
+ ],
33
+ }),
34
+ );
35
+
36
+ const match = /labels="([^"]*)"/.exec(html);
37
+ if (!match?.[1]) throw new Error("expected a labels attribute");
38
+ const decoded = match[1]
39
+ .replace(/&quot;/g, '"')
40
+ .replace(/&amp;/g, "&")
41
+ .replace(/&lt;/g, "<")
42
+ .replace(/&gt;/g, ">")
43
+ .replace(/&#39;/g, "'");
44
+ return JSON.parse(decoded) as Record<string, string>;
45
+ }
46
+
47
+ describe("ComposeForm labels", () => {
48
+ it("leaves the slots the browser fills", () => {
49
+ const labels = renderComposeLabels();
50
+
51
+ expect(labels.translationOf).toContain("{title}");
52
+ expect(labels.translationContextInLanguage).toContain("{language}");
53
+ expect(labels.languageAutoDetected).toContain("{language}");
54
+ expect(labels.languageAutoPending).toContain("{language}");
55
+ expect(labels.languageTriggerLabel).toContain("{language}");
56
+ });
57
+
58
+ it("never ships an unresolved ICU construct", () => {
59
+ for (const [key, value] of Object.entries(renderComposeLabels())) {
60
+ if (typeof value !== "string") continue;
61
+ expect(value, key).not.toContain("plural,");
62
+ expect(value, key).not.toContain("NaN");
63
+ }
64
+ });
65
+
66
+ it("passes the site's languages to the component", () => {
67
+ const i18n = createI18n("en");
68
+ const c = {
69
+ get(key: string) {
70
+ return key === "i18n" ? i18n : undefined;
71
+ },
72
+ } as unknown as Context;
73
+ I18nProvider({ c, children: "" });
74
+
75
+ const html = renderToString(
76
+ ComposeForm({
77
+ languages: [{ tag: "en", label: "English" }],
78
+ }),
79
+ );
80
+ expect(html).toContain("languages=");
81
+ expect(html).toContain("English");
82
+ });
83
+
84
+ it("hands the component the page's language as the automatic default", () => {
85
+ const i18n = createI18n("en");
86
+ const c = {
87
+ get(key: string) {
88
+ return key === "i18n" ? i18n : undefined;
89
+ },
90
+ } as unknown as Context;
91
+ I18nProvider({ c, children: "" });
92
+
93
+ const html = renderToString(
94
+ ComposeForm({
95
+ languages: [
96
+ { tag: "zh-Hans", label: "简体中文" },
97
+ { tag: "ja", label: "日本語" },
98
+ ],
99
+ contextLanguage: "ja",
100
+ }),
101
+ );
102
+ expect(html).toContain('context-language="ja"');
103
+
104
+ // Without a page language the attribute stays off, and the component
105
+ // falls back to the primary language on its own.
106
+ expect(
107
+ renderToString(
108
+ ComposeForm({ languages: [{ tag: "en", label: "English" }] }),
109
+ ),
110
+ ).not.toContain("context-language");
111
+ });
112
+
113
+ it("sends an empty list on a single-language site", () => {
114
+ const i18n = createI18n("en");
115
+ const c = {
116
+ get(key: string) {
117
+ return key === "i18n" ? i18n : undefined;
118
+ },
119
+ } as unknown as Context;
120
+ I18nProvider({ c, children: "" });
121
+
122
+ // The composer renders no language UI at all in that case.
123
+ expect(renderToString(ComposeForm({}))).toContain('languages="[]"');
124
+ });
125
+ });
@@ -4,10 +4,10 @@
4
4
 
5
5
  import { msg } from "@lingui/core/macro";
6
6
  import { useLingui } from "../../../i18n/context.js";
7
+ import { getJantDocsUrl } from "../../../lib/jant-docs.js";
7
8
  import { toPublicPath } from "../../../lib/url.js";
8
9
 
9
- const THEMING_DOCS_URL =
10
- "https://github.com/jant-me/jant/blob/main/docs/theming.md";
10
+ const THEMING_DOCS_URL = getJantDocsUrl("theming");
11
11
 
12
12
  export function AdvancedContent({
13
13
  customCSS,
@@ -33,11 +33,7 @@ export function CodeInjectionContent({
33
33
  data-indicator="_codeLoading"
34
34
  class="max-w-form"
35
35
  >
36
- <div
37
- role="note"
38
- class="alert alert-destructive mb-6"
39
- style="display:flex;gap:.75rem;align-items:flex-start"
40
- >
36
+ <div role="note" class="alert-destructive mb-6">
41
37
  <svg
42
38
  xmlns="http://www.w3.org/2000/svg"
43
39
  width="20"
@@ -54,16 +50,16 @@ export function CodeInjectionContent({
54
50
  <path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
55
51
  <path d="M12 17h.01" />
56
52
  </svg>
57
- <div>
58
- <p class="font-medium">
59
- {i18n._(
60
- msg({
61
- message: "This code runs on every page of your site.",
62
- comment: "@context: Code Injection security warning headline",
63
- }),
64
- )}
65
- </p>
66
- <p class="text-sm" style="margin-top:.25rem">
53
+ <strong>
54
+ {i18n._(
55
+ msg({
56
+ message: "This code runs on every page of your site.",
57
+ comment: "@context: Code Injection security warning headline",
58
+ }),
59
+ )}
60
+ </strong>
61
+ <section>
62
+ <p>
67
63
  {i18n._(
68
64
  msg({
69
65
  message:
@@ -72,7 +68,7 @@ export function CodeInjectionContent({
72
68
  }),
73
69
  )}
74
70
  </p>
75
- </div>
71
+ </section>
76
72
  </div>
77
73
 
78
74
  <fieldset class="mb-6">