@jant/core 0.6.16 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (592) hide show
  1. package/README.md +11 -11
  2. package/bin/commands/assets/upload.js +192 -15
  3. package/bin/commands/export.js +3 -1
  4. package/bin/commands/import-site.js +749 -220
  5. package/bin/commands/reset-password.js +21 -8
  6. package/bin/commands/setup.js +136 -0
  7. package/bin/commands/site/export.js +71 -34
  8. package/bin/commands/site/pull-media.js +2 -6
  9. package/bin/commands/site/snapshot/export.js +19 -61
  10. package/bin/commands/site/snapshot/import.js +23 -24
  11. package/bin/lib/d1-query.js +92 -18
  12. package/bin/lib/hugo-markdown.js +4 -0
  13. package/bin/lib/node-env.js +40 -4
  14. package/bin/lib/site-pull-media.js +21 -28
  15. package/bin/lib/site-selection.js +94 -29
  16. package/bin/lib/site-snapshot.js +338 -3
  17. package/bin/lib/sql-export.js +68 -5
  18. package/bin/lib/wrangler-cli.js +37 -2
  19. package/bin/lib/wrangler-json.js +35 -0
  20. package/bin/lib/zip-archive.js +187 -0
  21. package/dist/{app-C7_4oab9.js → app-ZcaI1kPN.js} +53190 -13529
  22. package/dist/client/.vite/manifest.json +255 -90
  23. package/dist/client/_assets/chunks/confirm-D7GrwsCN.js +45 -0
  24. package/dist/client/_assets/chunks/create-editor-B-m7X7S5.js +261 -0
  25. package/dist/client/_assets/chunks/{native-DpcrFAPh.js → emoji-mart-C4RpRKcU.js} +716 -1
  26. package/dist/client/_assets/chunks/footnote-rail-CmeoVtnU.js +1 -0
  27. package/dist/client/_assets/chunks/heic-to-CuP2Qlsw.js +1 -0
  28. package/dist/client/_assets/chunks/jant-locale-picker-DL6mIOT5.js +69 -0
  29. package/dist/client/_assets/chunks/lit-DW8VmJAT.js +2 -0
  30. package/dist/client/_assets/chunks/mediabunny-DRaCQ1r-.js +31 -0
  31. package/dist/client/_assets/chunks/rolldown-runtime-hePW80VL.js +1 -0
  32. package/dist/client/_assets/chunks/settings-paths-BcdySNx6.js +84 -0
  33. package/dist/client/_assets/chunks/site-header-nav-DzXsFk05.js +1 -0
  34. package/dist/client/_assets/chunks/slug-format-CRXO1AL5.js +1 -0
  35. package/dist/client/_assets/chunks/slugify-373citbs.js +1 -0
  36. package/dist/client/_assets/chunks/sortable-list-BJyd-LXE.js +2 -0
  37. package/dist/client/_assets/chunks/types-D5iF3H1a.js +1 -0
  38. package/dist/client/_assets/chunks/unsafe-html-B5ZEXaxg.js +1 -0
  39. package/dist/client/_assets/chunks/unsafe-svg-BkscJx69.js +2 -0
  40. package/dist/client/_assets/client-DAhoqPdr.css +2 -0
  41. package/dist/client/_assets/client-auth-CAMfTrKW.js +828 -0
  42. package/dist/client/_assets/client-author-DQ8R-8KR.css +2 -0
  43. package/dist/client/_assets/client-compose-D6rExKVK.js +2004 -0
  44. package/dist/client/_assets/client-knSEyUJO.js +295 -0
  45. package/dist/client/_assets/client-manage-sVOkqVbH.js +2170 -0
  46. package/dist/client/_assets/client-settings-CGR_nZ78.js +1112 -0
  47. package/dist/github-sync-Gw4orAHk.js +8398 -0
  48. package/dist/index.js +2 -5
  49. package/dist/node.js +11 -9
  50. package/package.json +15 -5
  51. package/src/__tests__/auth-session-freshness.test.ts +57 -0
  52. package/src/__tests__/basecoat-variants.test.ts +121 -0
  53. package/src/__tests__/bin/assets-upload.test.ts +68 -0
  54. package/src/__tests__/dev-scripts.test.ts +203 -0
  55. package/src/__tests__/export-collection-order.test.ts +276 -0
  56. package/src/__tests__/export-feed-ids.test.ts +184 -0
  57. package/src/__tests__/export-feed-order.test.ts +294 -0
  58. package/src/__tests__/export-hugo-build.test.ts +390 -0
  59. package/src/__tests__/export-import-roundtrip.test.ts +94 -0
  60. package/src/__tests__/export-service.test.ts +842 -28
  61. package/src/__tests__/export-smart-collection.test.ts +329 -0
  62. package/src/__tests__/helpers/component-css.ts +91 -0
  63. package/src/__tests__/helpers/db.ts +33 -0
  64. package/src/__tests__/helpers/export-fixtures.ts +3 -0
  65. package/src/__tests__/helpers/hugo-site.ts +146 -0
  66. package/src/__tests__/import-site-command.test.ts +487 -1
  67. package/src/__tests__/mise-config.test.ts +75 -4
  68. package/src/__tests__/node-dev-tasks.test.ts +264 -0
  69. package/src/__tests__/site-export-canonical-import.test.ts +149 -0
  70. package/src/__tests__/snapshot-canonical-replay.test.ts +179 -0
  71. package/src/__tests__/snapshot-settings.test.ts +97 -0
  72. package/src/__tests__/snapshot-tables.test.ts +219 -0
  73. package/src/__tests__/sql-export.test.ts +173 -0
  74. package/src/__tests__/stylesheet-audience.test.ts +159 -0
  75. package/src/__tests__/stylesheet-breakpoints.test.ts +183 -0
  76. package/src/__tests__/zip-archive.test.ts +106 -0
  77. package/src/app.tsx +86 -22
  78. package/src/auth.ts +18 -1
  79. package/src/client/__tests__/collection-page-actions.test.ts +51 -2
  80. package/src/client/__tests__/compose-bridge-image-upload.test.ts +196 -0
  81. package/src/client/__tests__/compose-bridge-video-poster.test.ts +159 -0
  82. package/src/client/__tests__/compose-bridge.test.ts +272 -0
  83. package/src/client/__tests__/compose-launch.test.ts +62 -1
  84. package/src/client/__tests__/compose-shortcuts.test.ts +38 -9
  85. package/src/client/__tests__/compose-triggers.test.ts +90 -0
  86. package/src/client/__tests__/copy-field.test.ts +162 -0
  87. package/src/client/__tests__/custom-url-menu.test.ts +127 -6
  88. package/src/client/__tests__/image-processor.test.ts +324 -2
  89. package/src/client/__tests__/lazy-entries.test.ts +92 -0
  90. package/src/client/__tests__/media-scroll-hint.test.ts +155 -0
  91. package/src/client/__tests__/runtime-paths.test.ts +84 -0
  92. package/src/client/__tests__/smart-collection-page-actions.test.ts +158 -0
  93. package/src/client/__tests__/toast.test.ts +23 -0
  94. package/src/client/__tests__/video-processor-probe.test.ts +175 -0
  95. package/src/client/__tests__/video-processor.test.ts +181 -0
  96. package/src/client/audio-processor.ts +24 -15
  97. package/src/client/audio-waveform.ts +49 -0
  98. package/src/client/collection-dialog-host.ts +72 -0
  99. package/src/client/collection-navigation.ts +51 -16
  100. package/src/client/collection-page-actions.ts +26 -0
  101. package/src/client/components/__tests__/jant-collection-dialog.test.ts +329 -0
  102. package/src/client/components/__tests__/jant-collection-directory.test.ts +274 -10
  103. package/src/client/components/__tests__/jant-collection-form.test.ts +59 -178
  104. package/src/client/components/__tests__/jant-command-palette.test.ts +28 -0
  105. package/src/client/components/__tests__/jant-compose-dialog-draft-storage.test.ts +340 -0
  106. package/src/client/components/__tests__/jant-compose-dialog.test.ts +1250 -53
  107. package/src/client/components/__tests__/jant-compose-editor.test.ts +544 -40
  108. package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +0 -1
  109. package/src/client/components/__tests__/jant-locale-picker.test.ts +207 -0
  110. package/src/client/components/__tests__/jant-nav-manager.test.ts +234 -15
  111. package/src/client/components/__tests__/jant-post-menu.test.ts +413 -2
  112. package/src/client/components/__tests__/jant-post-picker.test.ts +197 -0
  113. package/src/client/components/__tests__/jant-settings-avatar.test.ts +15 -12
  114. package/src/client/components/__tests__/jant-settings-general.test.ts +561 -172
  115. package/src/client/components/__tests__/jant-settings-language.test.ts +851 -0
  116. package/src/client/components/__tests__/jant-smart-collection-dialog.test.ts +665 -0
  117. package/src/client/components/collection-dialog-types.ts +49 -0
  118. package/src/client/components/collection-manager-types.ts +32 -1
  119. package/src/client/components/collection-types.ts +1 -15
  120. package/src/client/components/compose-types.ts +75 -1
  121. package/src/client/components/jant-collection-dialog.ts +722 -0
  122. package/src/client/components/jant-collection-directory.ts +567 -182
  123. package/src/client/components/jant-collection-form.ts +53 -232
  124. package/src/client/components/jant-command-palette.ts +100 -49
  125. package/src/client/components/jant-compose-dialog.ts +1486 -707
  126. package/src/client/components/jant-compose-editor.ts +673 -336
  127. package/src/client/components/jant-compose-fullscreen.ts +51 -43
  128. package/src/client/components/jant-config-editor.ts +65 -53
  129. package/src/client/components/jant-confirm-dialog.ts +13 -11
  130. package/src/client/components/jant-locale-picker.ts +418 -0
  131. package/src/client/components/jant-media-lightbox.ts +146 -136
  132. package/src/client/components/jant-nav-manager.ts +690 -366
  133. package/src/client/components/jant-post-menu.ts +1086 -282
  134. package/src/client/components/jant-post-picker.ts +324 -0
  135. package/src/client/components/jant-repo-picker-types.ts +6 -1
  136. package/src/client/components/jant-repo-picker.ts +137 -108
  137. package/src/client/components/jant-settings-avatar.ts +31 -27
  138. package/src/client/components/jant-settings-general.ts +622 -543
  139. package/src/client/components/jant-settings-language.ts +1087 -0
  140. package/src/client/components/jant-smart-collection-dialog.ts +1021 -0
  141. package/src/client/components/jant-text-preview.ts +94 -86
  142. package/src/client/components/nav-manager-types.ts +62 -1
  143. package/src/client/components/settings-types.ts +14 -29
  144. package/src/client/components/smart-collection-conditions.ts +285 -0
  145. package/src/client/components/smart-collection-dialog-types.ts +68 -0
  146. package/src/client/compose-bridge.ts +214 -141
  147. package/src/client/compose-launch.ts +76 -2
  148. package/src/client/compose-shortcuts.ts +6 -8
  149. package/src/client/compose-triggers.ts +50 -0
  150. package/src/client/copy-field.ts +108 -0
  151. package/src/client/custom-url-menu.ts +60 -4
  152. package/src/client/icons.ts +14 -0
  153. package/src/client/image-plan.ts +165 -0
  154. package/src/client/image-processor.ts +195 -117
  155. package/src/client/image-worker-client.ts +45 -0
  156. package/src/client/image-worker.ts +135 -0
  157. package/src/client/lazy-entries.ts +95 -0
  158. package/src/client/lazy-slugify.ts +2 -2
  159. package/src/client/media-scroll-hint.ts +43 -2
  160. package/src/client/mediabunny.ts +29 -0
  161. package/src/client/post-picker.ts +41 -0
  162. package/src/client/runtime-paths.ts +74 -1
  163. package/src/client/settings-bridge.ts +4 -10
  164. package/src/client/site-header-nav.js +18 -6
  165. package/src/client/smart-collection-dialog-host.ts +116 -0
  166. package/src/client/smart-collection-page-actions.ts +208 -0
  167. package/src/client/tiptap/__tests__/bubble-menu.test.ts +110 -0
  168. package/src/client/tiptap/__tests__/floating-position.test.ts +136 -1
  169. package/src/client/tiptap/__tests__/inline-image-upload.test.ts +37 -0
  170. package/src/client/tiptap/__tests__/list-editing.test.ts +224 -86
  171. package/src/client/tiptap/__tests__/mark-exit.test.ts +1 -2
  172. package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +26 -0
  173. package/src/client/tiptap/bubble-menu.ts +47 -16
  174. package/src/client/tiptap/extensions.ts +0 -3
  175. package/src/client/tiptap/floating-position.ts +216 -2
  176. package/src/client/tiptap/inline-image-upload.ts +7 -3
  177. package/src/client/tiptap/link-toolbar.ts +22 -9
  178. package/src/client/tiptap/structural-keymap.ts +158 -47
  179. package/src/client/toast.ts +13 -0
  180. package/src/client/types/vite-worker.d.ts +12 -0
  181. package/src/client/upload-with-metadata.ts +3 -13
  182. package/src/client/video-processor.ts +351 -107
  183. package/src/client-auth.ts +14 -31
  184. package/src/client-compose.ts +19 -0
  185. package/src/client-manage.ts +13 -0
  186. package/src/client-settings.ts +14 -0
  187. package/src/client-site.ts +10 -3
  188. package/src/client.ts +4 -0
  189. package/src/db/__tests__/backfill-nav-label-mirrors.test.ts +149 -0
  190. package/src/db/__tests__/d1-query.test.ts +56 -1
  191. package/src/db/__tests__/schema-enum-sources.test.ts +33 -0
  192. package/src/db/__tests__/smart-collection-placement.test.ts +402 -0
  193. package/src/db/backfills/0006_clear_mirrored_page_nav_labels.sql +31 -0
  194. package/src/db/migrations/0032_married_tomas.sql +3 -0
  195. package/src/db/migrations/0033_busy_johnny_storm.sql +198 -0
  196. package/src/db/migrations/0034_cheerful_rumiko_fujikawa.sql +35 -0
  197. package/src/db/migrations/meta/0032_snapshot.json +2582 -0
  198. package/src/db/migrations/meta/0033_snapshot.json +2816 -0
  199. package/src/db/migrations/meta/0034_snapshot.json +2816 -0
  200. package/src/db/migrations/meta/_journal.json +21 -0
  201. package/src/db/migrations/pg/0030_abandoned_nighthawk.sql +3 -0
  202. package/src/db/migrations/pg/0031_strong_ultimatum.sql +128 -0
  203. package/src/db/migrations/pg/0032_furry_multiple_man.sql +6 -0
  204. package/src/db/migrations/pg/meta/0030_snapshot.json +3329 -0
  205. package/src/db/migrations/pg/meta/0031_snapshot.json +3660 -0
  206. package/src/db/migrations/pg/meta/0032_snapshot.json +3660 -0
  207. package/src/db/migrations/pg/meta/_journal.json +21 -0
  208. package/src/db/pg/schema.ts +195 -27
  209. package/src/db/post-visibility.ts +117 -0
  210. package/src/db/schema.ts +222 -27
  211. package/src/i18n/__tests__/detect.test.ts +26 -81
  212. package/src/i18n/__tests__/supported-locales.test.ts +83 -0
  213. package/src/i18n/context.tsx +12 -2
  214. package/src/i18n/detect.ts +7 -66
  215. package/src/i18n/locales/glossary.zh-Hans.yml +44 -4
  216. package/src/i18n/locales/glossary.zh-Hant.yml +44 -4
  217. package/src/i18n/locales/public/en.po +490 -158
  218. package/src/i18n/locales/public/en.ts +1 -1
  219. package/src/i18n/locales/public/zh-Hans.po +487 -155
  220. package/src/i18n/locales/public/zh-Hans.ts +1 -1
  221. package/src/i18n/locales/public/zh-Hant.po +487 -155
  222. package/src/i18n/locales/public/zh-Hant.ts +1 -1
  223. package/src/i18n/locales/settings/en.po +555 -73
  224. package/src/i18n/locales/settings/en.ts +1 -1
  225. package/src/i18n/locales/settings/zh-Hans.po +601 -119
  226. package/src/i18n/locales/settings/zh-Hans.ts +1 -1
  227. package/src/i18n/locales/settings/zh-Hant.po +614 -132
  228. package/src/i18n/locales/settings/zh-Hant.ts +1 -1
  229. package/src/i18n/locales.ts +94 -0
  230. package/src/i18n/middleware.ts +12 -2
  231. package/src/i18n/supported-locales.ts +80 -0
  232. package/src/lib/__tests__/api-settings.test.ts +11 -2
  233. package/src/lib/__tests__/asset-path.test.ts +92 -0
  234. package/src/lib/__tests__/deferred.test.ts +90 -0
  235. package/src/lib/__tests__/discover-ping.test.ts +89 -0
  236. package/src/lib/__tests__/discover.test.ts +410 -0
  237. package/src/lib/__tests__/feed-policy.test.ts +26 -0
  238. package/src/lib/__tests__/feed.test.ts +1526 -15
  239. package/src/lib/__tests__/filter-dimensions.test.ts +341 -0
  240. package/src/lib/__tests__/github-sync-repo-name.test.ts +40 -0
  241. package/src/lib/__tests__/hosted-signin.test.ts +1 -1
  242. package/src/lib/__tests__/http-cache.test.ts +162 -0
  243. package/src/lib/__tests__/image.test.ts +27 -1
  244. package/src/lib/__tests__/jant-docs-dev.test.ts +21 -0
  245. package/src/lib/__tests__/jant-docs.test.ts +23 -0
  246. package/src/lib/__tests__/lang-detect.test.ts +231 -0
  247. package/src/lib/__tests__/markdown-to-tiptap.test.ts +105 -0
  248. package/src/lib/__tests__/markdown.test.ts +10 -0
  249. package/src/lib/__tests__/media-helpers.test.ts +81 -0
  250. package/src/lib/__tests__/navigation.test.ts +135 -1
  251. package/src/lib/__tests__/resolve-config.test.ts +131 -3
  252. package/src/lib/__tests__/schemas.test.ts +69 -5
  253. package/src/lib/__tests__/site-unavailable-page.test.ts +25 -0
  254. package/src/lib/__tests__/slug.test.ts +70 -0
  255. package/src/lib/__tests__/slugify.test.ts +85 -0
  256. package/src/lib/__tests__/storage.test.ts +32 -0
  257. package/src/lib/__tests__/thread-fold.test.ts +113 -0
  258. package/src/lib/__tests__/timeline.test.ts +87 -0
  259. package/src/lib/__tests__/tiptap-to-markdown.test.ts +172 -4
  260. package/src/lib/__tests__/upload-sideload.test.ts +14 -0
  261. package/src/lib/__tests__/url-fetch.test.ts +1 -2
  262. package/src/lib/__tests__/url.test.ts +78 -72
  263. package/src/lib/__tests__/view-language.test.ts +413 -0
  264. package/src/lib/__tests__/worker-response-cache.test.ts +90 -0
  265. package/src/lib/address.ts +57 -0
  266. package/src/lib/api-posts.ts +12 -0
  267. package/src/lib/asset-path.ts +98 -1
  268. package/src/lib/build-env.ts +16 -0
  269. package/src/lib/collection-groups.ts +5 -1
  270. package/src/lib/collection-paths.ts +3 -10
  271. package/src/lib/constants.ts +28 -3
  272. package/src/lib/copy-field.ts +36 -0
  273. package/src/lib/crypto.ts +40 -0
  274. package/src/lib/csp-builder.ts +2 -1
  275. package/src/lib/deferred.ts +79 -0
  276. package/src/lib/discover-ping.ts +141 -0
  277. package/src/lib/discover.ts +439 -0
  278. package/src/lib/env.ts +134 -0
  279. package/src/lib/errors.ts +33 -0
  280. package/src/lib/feed-policy.ts +236 -2
  281. package/src/lib/feed.ts +926 -73
  282. package/src/lib/filter-dimensions.ts +1349 -0
  283. package/src/lib/github-sync-repo-name.ts +45 -0
  284. package/src/lib/github-sync-site-config.ts +3 -0
  285. package/src/lib/hosted-signin.ts +1 -1
  286. package/src/lib/http-cache.ts +148 -0
  287. package/src/lib/hugo-markdown.ts +62 -0
  288. package/src/lib/icons.ts +9 -2
  289. package/src/lib/ids.ts +3 -0
  290. package/src/lib/image.ts +17 -4
  291. package/src/lib/jant-docs.ts +42 -0
  292. package/src/lib/lang-detect.ts +308 -0
  293. package/src/lib/markdown-manager.ts +392 -2
  294. package/src/lib/media-helpers.ts +13 -1
  295. package/src/lib/navigation.ts +78 -11
  296. package/src/lib/pagination.ts +28 -24
  297. package/src/lib/per-language-surfaces.ts +67 -0
  298. package/src/lib/post-body-html.ts +11 -4
  299. package/src/lib/post-meta.ts +19 -0
  300. package/src/lib/render.tsx +55 -7
  301. package/src/lib/resolve-config.ts +111 -6
  302. package/src/lib/schemas.ts +251 -15
  303. package/src/lib/site-header-fragment.tsx +2 -1
  304. package/src/lib/site-unavailable-page.ts +47 -0
  305. package/src/lib/slug.ts +51 -7
  306. package/src/lib/slugify.ts +113 -0
  307. package/src/lib/startup-config.ts +4 -6
  308. package/src/lib/storage.ts +7 -0
  309. package/src/lib/summary.ts +50 -0
  310. package/src/lib/thread-fold.ts +161 -0
  311. package/src/lib/time.ts +32 -0
  312. package/src/lib/timeline.ts +43 -2
  313. package/src/lib/tiptap-render.ts +1 -2
  314. package/src/lib/tiptap-to-markdown.ts +13 -7
  315. package/src/lib/upload.ts +16 -13
  316. package/src/lib/url.ts +159 -86
  317. package/src/lib/version.ts +10 -8
  318. package/src/lib/view-language.ts +436 -0
  319. package/src/lib/view.ts +53 -25
  320. package/src/lib/viewer-context.ts +62 -0
  321. package/src/middleware/__tests__/auth.test.ts +70 -2
  322. package/src/middleware/__tests__/cache-control.test.ts +71 -3
  323. package/src/middleware/__tests__/onboarding.test.ts +76 -5
  324. package/src/middleware/__tests__/secure-headers.test.ts +1 -1
  325. package/src/middleware/__tests__/session.test.ts +181 -33
  326. package/src/middleware/auth.ts +50 -31
  327. package/src/middleware/cache-control.ts +75 -10
  328. package/src/middleware/config.ts +6 -6
  329. package/src/middleware/error-handler.ts +12 -0
  330. package/src/middleware/onboarding.ts +32 -10
  331. package/src/middleware/secure-headers.ts +1 -1
  332. package/src/middleware/session.ts +96 -8
  333. package/src/node/__tests__/cli-migrate.test.ts +21 -19
  334. package/src/node/__tests__/cli-reset-password.test.ts +81 -8
  335. package/src/node/__tests__/cli-runtime-target.test.ts +48 -1
  336. package/src/node/__tests__/cli-setup.test.ts +163 -0
  337. package/src/node/__tests__/cli-site-selection.test.ts +128 -38
  338. package/src/node/__tests__/cli-site-snapshot.test.ts +67 -0
  339. package/src/node/__tests__/cli-snapshot-meta.test.ts +20 -0
  340. package/src/node/__tests__/runtime.test.ts +75 -0
  341. package/src/node/index.ts +3 -0
  342. package/src/node/request-handler.ts +3 -1
  343. package/src/preset.css +76 -22
  344. package/src/routes/__tests__/feed-validator.test.ts +156 -0
  345. package/src/routes/api/__tests__/collections.test.ts +114 -0
  346. package/src/routes/api/__tests__/discover.test.ts +222 -0
  347. package/src/routes/api/__tests__/nav-items.test.ts +212 -2
  348. package/src/routes/api/__tests__/post-translations.test.ts +829 -0
  349. package/src/routes/api/__tests__/posts.test.ts +24 -0
  350. package/src/routes/api/__tests__/settings.test.ts +24 -3
  351. package/src/routes/api/__tests__/telegram.test.ts +6 -5
  352. package/src/routes/api/__tests__/upload.test.ts +34 -0
  353. package/src/routes/api/collections.ts +49 -6
  354. package/src/routes/api/custom-urls.ts +0 -1
  355. package/src/routes/api/discover.ts +95 -0
  356. package/src/routes/api/export.ts +6 -3
  357. package/src/routes/api/internal/__tests__/sites.test.ts +42 -2
  358. package/src/routes/api/internal/sites.ts +0 -1
  359. package/src/routes/api/nav-items.ts +55 -8
  360. package/src/routes/api/posts.ts +161 -0
  361. package/src/routes/api/public/__tests__/archive.test.ts +111 -0
  362. package/src/routes/api/public/archive.ts +77 -81
  363. package/src/routes/api/public/posts.ts +44 -7
  364. package/src/routes/api/smart-collections.ts +132 -0
  365. package/src/routes/api/telegram.ts +2 -40
  366. package/src/routes/api/upload.ts +10 -3
  367. package/src/routes/api/uploads.ts +1 -2
  368. package/src/routes/auth/__tests__/setup-auth.test.ts +137 -0
  369. package/src/routes/auth/__tests__/setup-discover.test.ts +204 -0
  370. package/src/routes/auth/__tests__/setup-flow.test.ts +270 -0
  371. package/src/routes/auth/__tests__/setup-page.test.tsx +329 -0
  372. package/src/routes/auth/__tests__/setup-steps.test.ts +310 -0
  373. package/src/routes/auth/__tests__/setup.test.ts +278 -26
  374. package/src/routes/auth/__tests__/signin.test.ts +135 -0
  375. package/src/routes/auth/setup.tsx +934 -186
  376. package/src/routes/auth/signin.tsx +10 -8
  377. package/src/routes/compose.tsx +9 -0
  378. package/src/routes/dash/__tests__/custom-urls-archive.test.ts +44 -0
  379. package/src/routes/dash/__tests__/custom-urls-page.test.ts +49 -0
  380. package/src/routes/dash/__tests__/delete-account.test.ts +106 -0
  381. package/src/routes/dash/__tests__/github-sync-app.test.ts +365 -0
  382. package/src/routes/dash/__tests__/settings-discover.test.ts +213 -0
  383. package/src/routes/dash/custom-urls.tsx +1 -37
  384. package/src/routes/dash/settings.tsx +714 -111
  385. package/src/routes/discover-announce.ts +96 -0
  386. package/src/routes/feed/__tests__/feed.test.ts +165 -2
  387. package/src/routes/feed/feed.ts +69 -40
  388. package/src/routes/feed/sitemap.ts +95 -11
  389. package/src/routes/pages/__tests__/archive-indexing.test.ts +200 -0
  390. package/src/routes/pages/__tests__/archive-params.test.ts +370 -6
  391. package/src/routes/pages/__tests__/collection-routing.test.ts +16 -0
  392. package/src/routes/pages/__tests__/collections-feed-links.test.ts +72 -0
  393. package/src/routes/pages/__tests__/collections.test.ts +9 -3
  394. package/src/routes/pages/__tests__/custom-archive-url.test.ts +119 -0
  395. package/src/routes/pages/__tests__/feed-autodiscovery.test.ts +148 -0
  396. package/src/routes/pages/__tests__/language-routing.test.ts +679 -0
  397. package/src/routes/pages/__tests__/post-page-round-trips.test.ts +211 -0
  398. package/src/routes/pages/__tests__/post-preview-partial.test.ts +96 -0
  399. package/src/routes/pages/__tests__/smart-collection.test.ts +298 -0
  400. package/src/routes/pages/__tests__/subscribe.test.ts +248 -0
  401. package/src/routes/pages/__tests__/thread-order.test.ts +179 -0
  402. package/src/routes/pages/archive.tsx +487 -379
  403. package/src/routes/pages/collection.tsx +94 -96
  404. package/src/routes/pages/collections.tsx +38 -50
  405. package/src/routes/pages/featured.tsx +43 -18
  406. package/src/routes/pages/home.tsx +25 -6
  407. package/src/routes/pages/language.tsx +113 -0
  408. package/src/routes/pages/latest.tsx +26 -16
  409. package/src/routes/pages/new.tsx +3 -0
  410. package/src/routes/pages/page.tsx +228 -45
  411. package/src/routes/pages/partials.tsx +41 -0
  412. package/src/routes/pages/search.tsx +22 -5
  413. package/src/routes/pages/smart-collection.tsx +340 -0
  414. package/src/routes/pages/subscribe.tsx +182 -0
  415. package/src/runtime/__tests__/index.test.ts +19 -0
  416. package/src/runtime/__tests__/node.test.ts +129 -4
  417. package/src/runtime/__tests__/readiness.test.ts +23 -0
  418. package/src/runtime/cloudflare.ts +21 -2
  419. package/src/runtime/index.ts +27 -1
  420. package/src/runtime/node.ts +67 -1
  421. package/src/runtime/readiness.ts +15 -0
  422. package/src/runtime/site.ts +95 -24
  423. package/src/services/__tests__/auth.test.ts +116 -7
  424. package/src/services/__tests__/bootstrap-setup-instance.test.ts +230 -0
  425. package/src/services/__tests__/collection.test.ts +108 -4
  426. package/src/services/__tests__/custom-url.test.ts +34 -0
  427. package/src/services/__tests__/github-app-installations.test.ts +153 -0
  428. package/src/services/__tests__/github-sync-push.test.ts +46 -0
  429. package/src/services/__tests__/language.test.ts +495 -0
  430. package/src/services/__tests__/media.test.ts +31 -0
  431. package/src/services/__tests__/navigation.test.ts +213 -10
  432. package/src/services/__tests__/path.test.ts +236 -0
  433. package/src/services/__tests__/post-language.test.ts +830 -0
  434. package/src/services/__tests__/post-timeline.test.ts +127 -0
  435. package/src/services/__tests__/post.test.ts +120 -9
  436. package/src/services/__tests__/settings.test.ts +228 -17
  437. package/src/services/__tests__/site-admin.test.ts +5 -5
  438. package/src/services/__tests__/smart-collection.test.ts +651 -0
  439. package/src/services/about-page.ts +22 -1
  440. package/src/services/auth.ts +51 -49
  441. package/src/services/bootstrap.ts +408 -39
  442. package/src/services/collection-directory-position.ts +76 -0
  443. package/src/services/collection.ts +331 -46
  444. package/src/services/custom-url.ts +29 -24
  445. package/src/services/export-theme/assets/client-site.css +1 -1
  446. package/src/services/export-theme/assets/client-site.js +123 -123
  447. package/src/services/export-theme/layouts/_default/alias.html +27 -1
  448. package/src/services/export-theme/layouts/_default/list.html +2 -63
  449. package/src/services/export-theme/layouts/_default/rss.xml +103 -38
  450. package/src/services/export-theme/layouts/collections/list.html +3 -3
  451. package/src/services/export-theme/layouts/featured/list.html +1 -4
  452. package/src/services/export-theme/layouts/index.html +40 -26
  453. package/src/services/export-theme/layouts/partials/collection-members.html +100 -0
  454. package/src/services/export-theme/layouts/partials/collection-threads.html +33 -0
  455. package/src/services/export-theme/layouts/partials/featured-members.html +35 -0
  456. package/src/services/export-theme/layouts/partials/featured-thread.html +1 -1
  457. package/src/services/export-theme/layouts/partials/feed-post-content.xml +105 -2
  458. package/src/services/export-theme/layouts/partials/footer.html +1 -1
  459. package/src/services/export-theme/layouts/partials/head.html +1 -1
  460. package/src/services/export-theme/layouts/partials/header.html +5 -3
  461. package/src/services/export-theme/layouts/partials/jant-data.html +23 -0
  462. package/src/services/export-theme/layouts/partials/latest-members.html +48 -0
  463. package/src/services/export-theme/layouts/partials/media-gallery.html +35 -21
  464. package/src/services/export-theme/layouts/partials/pagination.html +14 -13
  465. package/src/services/export-theme/layouts/partials/post-card.html +8 -4
  466. package/src/services/export-theme/layouts/partials/reply.html +2 -1
  467. package/src/services/export-theme/layouts/partials/smart-collection-members.html +129 -0
  468. package/src/services/export-theme/layouts/partials/thread-preview.html +41 -31
  469. package/src/services/export-theme/layouts/post/list.html +1 -1
  470. package/src/services/export-theme/layouts/smart_collection/list.html +24 -0
  471. package/src/services/export-theme/styles/main.css +13 -2
  472. package/src/services/export-theme/theme.toml +1 -1
  473. package/src/services/export.ts +880 -72
  474. package/src/services/github-app-installations.ts +171 -4
  475. package/src/services/github-sync.ts +69 -15
  476. package/src/services/index.ts +34 -3
  477. package/src/services/language.ts +412 -0
  478. package/src/services/mcp.ts +22 -2
  479. package/src/services/media.ts +22 -7
  480. package/src/services/navigation.ts +429 -67
  481. package/src/services/path.ts +254 -6
  482. package/src/services/post.ts +1234 -190
  483. package/src/services/search.ts +39 -2
  484. package/src/services/settings.ts +238 -43
  485. package/src/services/site-admin.ts +27 -14
  486. package/src/services/site.ts +160 -32
  487. package/src/services/smart-collection.ts +636 -0
  488. package/src/services/telegram.ts +20 -22
  489. package/src/services/upload-session.ts +1 -3
  490. package/src/style-author.css +27 -0
  491. package/src/styles/components-author.css +1622 -0
  492. package/src/styles/components.css +24 -1571
  493. package/src/styles/site-media.css +16 -2
  494. package/src/styles/tokens.css +82 -12
  495. package/src/styles/ui-author.css +7602 -0
  496. package/src/styles/ui.css +1090 -7216
  497. package/src/types/app-context.ts +23 -0
  498. package/src/types/bindings.ts +8 -0
  499. package/src/types/config.ts +94 -15
  500. package/src/types/constants.ts +188 -16
  501. package/src/types/entities.ts +87 -1
  502. package/src/types/operations.ts +74 -2
  503. package/src/types/props.ts +157 -31
  504. package/src/types/views.ts +55 -1
  505. package/src/ui/__tests__/font-themes.test.ts +52 -12
  506. package/src/ui/compose/ComposeDialog.tsx +201 -7
  507. package/src/ui/compose/ComposePrompt.tsx +1 -5
  508. package/src/ui/compose/__tests__/ComposeDialog.test.tsx +125 -0
  509. package/src/ui/dash/appearance/AdvancedContent.tsx +2 -2
  510. package/src/ui/dash/appearance/CodeInjectionContent.tsx +12 -16
  511. package/src/ui/dash/appearance/NavigationContent.tsx +117 -16
  512. package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +33 -11
  513. package/src/ui/dash/appearance/__tests__/system-nav-descriptions.test.ts +44 -0
  514. package/src/ui/dash/appearance/system-nav-descriptions.ts +75 -0
  515. package/src/ui/dash/settings/AccountMenuContent.tsx +2 -2
  516. package/src/ui/dash/settings/ApiTokensContent.tsx +2 -1
  517. package/src/ui/dash/settings/ConfigEditorContent.tsx +35 -16
  518. package/src/ui/dash/settings/GeneralContent.tsx +277 -113
  519. package/src/ui/dash/settings/LanguageContent.tsx +409 -0
  520. package/src/ui/dash/settings/SettingsRootContent.tsx +19 -1
  521. package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +324 -4
  522. package/src/ui/dash/settings/__tests__/LanguageContent.test.tsx +144 -0
  523. package/src/ui/dash/settings/__tests__/discover-copy.test.ts +66 -0
  524. package/src/ui/dash/settings/discover-copy.ts +116 -0
  525. package/src/ui/feed/LinkCard.tsx +55 -45
  526. package/src/ui/feed/NoteCard.tsx +9 -4
  527. package/src/ui/feed/PostStatusBadges.tsx +90 -43
  528. package/src/ui/feed/QuoteCard.tsx +55 -30
  529. package/src/ui/feed/ThreadPreview.tsx +6 -2
  530. package/src/ui/feed/__tests__/thread-preview.test.ts +13 -16
  531. package/src/ui/feed/__tests__/timeline-cards.test.ts +314 -49
  532. package/src/ui/feed/thread-preview-state.ts +20 -6
  533. package/src/ui/font-themes.ts +39 -29
  534. package/src/ui/layouts/BaseLayout.tsx +155 -10
  535. package/src/ui/layouts/SiteLayout.tsx +192 -34
  536. package/src/ui/layouts/__tests__/BaseLayout.test.tsx +196 -17
  537. package/src/ui/layouts/__tests__/SiteLayout.test.tsx +109 -85
  538. package/src/ui/pages/ArchivePage.tsx +282 -289
  539. package/src/ui/pages/CollectionPage.tsx +86 -76
  540. package/src/ui/pages/CollectionsPage.tsx +5 -0
  541. package/src/ui/pages/ComposePage.tsx +8 -0
  542. package/src/ui/pages/PostPage.tsx +47 -0
  543. package/src/ui/pages/SearchPage.tsx +3 -3
  544. package/src/ui/pages/SmartCollectionPage.tsx +434 -0
  545. package/src/ui/pages/SubscribePage.tsx +236 -0
  546. package/src/ui/pages/__tests__/ArchivePage.test.tsx +65 -9
  547. package/src/ui/pages/__tests__/collections-directory-rows.test.tsx +92 -0
  548. package/src/ui/pages/__tests__/collections-view-links.test.tsx +81 -0
  549. package/src/ui/shared/CollectionDirectory.tsx +178 -3
  550. package/src/ui/shared/CollectionsManager.tsx +70 -17
  551. package/src/ui/shared/CopyField.tsx +127 -0
  552. package/src/ui/shared/EmptyPostContent.tsx +23 -0
  553. package/src/ui/shared/MediaGallery.tsx +125 -46
  554. package/src/ui/shared/Pagination.tsx +20 -5
  555. package/src/ui/shared/PostFooter.tsx +20 -11
  556. package/src/ui/shared/__tests__/archive-labels.test.ts +100 -0
  557. package/src/ui/shared/__tests__/media-gallery.test.ts +210 -2
  558. package/src/ui/shared/__tests__/navigation-labels.test.ts +16 -22
  559. package/src/ui/shared/__tests__/pagination.test.ts +38 -24
  560. package/src/ui/shared/__tests__/post-footer.test.ts +32 -0
  561. package/src/ui/shared/archive-labels.ts +98 -0
  562. package/src/ui/shared/collection-management-labels.ts +94 -16
  563. package/src/ui/shared/navigation-labels.ts +45 -71
  564. package/src/ui/shared/post-article-attributes.ts +2 -0
  565. package/src/ui/shared/smart-collection-labels.ts +343 -0
  566. package/dist/app-C75Zc8dD.js +0 -6
  567. package/dist/client/_assets/chunks/heic-to-DUUaO23q.js +0 -1
  568. package/dist/client/_assets/chunks/module-DcsAZQZ_.js +0 -716
  569. package/dist/client/_assets/chunks/url-CU9pEbk5.js +0 -1
  570. package/dist/client/_assets/client-Bq0vre8Y.js +0 -296
  571. package/dist/client/_assets/client-D3VZyG4L.css +0 -2
  572. package/dist/client/_assets/client-auth-F2rb9WfF.js +0 -5571
  573. package/dist/env-BPYViDJJ.js +0 -281
  574. package/dist/export-Dl5KCiEs.js +0 -5562
  575. package/dist/github-api-BNF35Lkx.js +0 -176
  576. package/dist/github-app-DdiD-bC4.js +0 -275
  577. package/dist/github-sync-Bqg62IvV.js +0 -4
  578. package/dist/github-sync-HdK2EgvJ.js +0 -431
  579. package/dist/url-CbLAtlZe.js +0 -900
  580. package/src/client/__tests__/collection-created-notice.test.ts +0 -36
  581. package/src/client/__tests__/collection-form-bridge.test.ts +0 -216
  582. package/src/client/__tests__/post-form-bridge.test.ts +0 -82
  583. package/src/client/collection-created-notice.ts +0 -66
  584. package/src/client/collection-form-bridge.ts +0 -191
  585. package/src/client/components/__tests__/jant-post-form.test.ts +0 -193
  586. package/src/client/components/jant-post-form.ts +0 -422
  587. package/src/client/components/post-form-template.ts +0 -352
  588. package/src/client/components/post-form-types.ts +0 -96
  589. package/src/client/media-metadata.ts +0 -247
  590. package/src/client/post-form-bridge.ts +0 -193
  591. package/src/client/tiptap/exitable-marks.ts +0 -73
  592. package/src/ui/pages/CollectionEditorPage.tsx +0 -175
@@ -1,30 +1,28 @@
1
1
  /**
2
- * Collection Form Component
2
+ * Collection Quick-Create Form
3
3
  *
4
- * Handles create/edit collection form interactions for:
5
- * - title, slug, description, and sort order
6
- * - quick-create slug preview/editing
7
- * - dispatching `jant:collection-submit` for the bridge layer
4
+ * A title and an address, nothing else — the two things a collection cannot be
5
+ * created without. It exists for the places where creating a collection is a
6
+ * step inside another task: picking one while composing, or putting one in the
7
+ * navigation. Everything a collection *can* have is decided in the collection
8
+ * dialog, which is where an author who came to make a collection already is.
9
+ *
10
+ * Submitting dispatches `jant:collection-submit`; the surrounding dialog owns
11
+ * the request and what happens after it.
8
12
  *
9
13
  * Light DOM only — BaseCoat and Tailwind classes apply directly.
10
14
  */
11
15
 
12
16
  import { LitElement, html, nothing } from "lit";
13
17
  import type { PropertyValueMap } from "lit";
14
- import type { Editor } from "@tiptap/core";
15
18
  import {
16
19
  MAX_COLLECTION_SLUG_LENGTH,
17
20
  MAX_COLLECTION_TITLE_LENGTH,
18
- type CollectionSortOrder,
19
21
  } from "../../types.js";
20
22
  import { getCollectionPagePath } from "../../lib/collection-paths.js";
21
23
  import { getSlugValidationIssue, truncateSlug } from "../../lib/slug-format.js";
22
24
  import { slugify } from "../lazy-slugify.js";
23
25
  import { publicPath } from "../runtime-paths.js";
24
- import {
25
- createSettingsEditor,
26
- jsonToMarkdown,
27
- } from "../tiptap/create-editor.js";
28
26
  import type {
29
27
  CollectionFormInitial,
30
28
  CollectionFormLabels,
@@ -36,14 +34,9 @@ export class JantCollectionForm extends LitElement {
36
34
  labels: { type: Object },
37
35
  initial: { type: Object },
38
36
  action: { type: String },
39
- cancelHref: { type: String, attribute: "cancel-href" },
40
- isEdit: { type: Boolean, attribute: "is-edit" },
41
- variant: { type: String },
42
37
 
43
38
  _title: { state: true },
44
39
  _slug: { state: true },
45
- _description: { state: true },
46
- _sortOrder: { state: true },
47
40
  _showSlugEditor: { state: true },
48
41
  _slugEdited: { state: true },
49
42
  _suggestedSlug: { state: true },
@@ -53,21 +46,15 @@ export class JantCollectionForm extends LitElement {
53
46
  declare labels: CollectionFormLabels;
54
47
  declare initial: CollectionFormInitial;
55
48
  declare action: string;
56
- declare cancelHref: string;
57
- declare isEdit: boolean;
58
- declare variant: "full" | "quick";
59
49
 
60
50
  declare _title: string;
61
51
  declare _slug: string;
62
- declare _description: string;
63
- declare _sortOrder: CollectionSortOrder;
64
52
  declare _showSlugEditor: boolean;
65
53
  declare _slugEdited: boolean;
66
54
  declare _suggestedSlug: string;
67
55
  declare _loading: boolean;
68
56
 
69
57
  #initialized = false;
70
- private _descEditor: Editor | null = null;
71
58
  #boundKeydown: ((e: KeyboardEvent) => void) | null = null;
72
59
 
73
60
  createRenderRoot() {
@@ -78,21 +65,11 @@ export class JantCollectionForm extends LitElement {
78
65
  constructor() {
79
66
  super();
80
67
  this.labels = {} as CollectionFormLabels;
81
- this.initial = {
82
- title: "",
83
- slug: "",
84
- description: "",
85
- sortOrder: "newest",
86
- };
68
+ this.initial = { title: "", slug: "" };
87
69
  this.action = "";
88
- this.cancelHref = "/";
89
- this.isEdit = false;
90
- this.variant = "full";
91
70
 
92
71
  this._title = "";
93
72
  this._slug = "";
94
- this._description = "";
95
- this._sortOrder = "newest";
96
73
  this._showSlugEditor = false;
97
74
  this._slugEdited = false;
98
75
  this._suggestedSlug = "";
@@ -117,8 +94,6 @@ export class JantCollectionForm extends LitElement {
117
94
  this.#boundKeydown = null;
118
95
  }
119
96
  super.disconnectedCallback();
120
- this._descEditor?.destroy();
121
- this._descEditor = null;
122
97
  }
123
98
 
124
99
  protected update(
@@ -144,46 +119,15 @@ export class JantCollectionForm extends LitElement {
144
119
  this._title = this.initial.title ?? "";
145
120
  this._slug = this.initial.slug ?? "";
146
121
  this._suggestedSlug = this.initial.slug ?? "";
147
- this._description = this.initial.description ?? "";
148
- this._sortOrder = this.initial.sortOrder ?? "newest";
149
- this._slugEdited = this.isEdit || Boolean(this._slug.trim());
150
- this._showSlugEditor = this.variant !== "quick";
151
-
152
- if (this.variant !== "quick") {
153
- this.updateComplete.then(() => this.#initDescEditor());
154
- }
155
- }
156
-
157
- #initDescEditor() {
158
- const container = this.querySelector<HTMLElement>(
159
- "[data-collection-desc-editor]",
160
- );
161
- if (!container || this._descEditor) return;
162
-
163
- this._descEditor = createSettingsEditor({
164
- element: container,
165
- placeholder: this.isEdit ? undefined : this.labels.descriptionPlaceholder,
166
- content: this._description || undefined,
167
- onUpdate: (markdown) => {
168
- this._description = markdown;
169
- },
170
- });
171
-
172
- // Normalize initial markdown through the editor round-trip
173
- this._description = jsonToMarkdown(this._descEditor.getJSON());
174
-
175
- const pm = container.querySelector<HTMLElement>(".ProseMirror");
176
- if (pm) {
177
- pm.style.outline = "none";
178
- pm.style.minHeight = "5rem";
179
- }
122
+ this._slugEdited = Boolean(this._slug.trim());
123
+ this._showSlugEditor = false;
180
124
  }
181
125
 
182
126
  async #handleTitleInput(event: Event) {
183
127
  const target = event.target as HTMLInputElement;
184
128
  this._title = target.value;
185
129
 
186
- if (this.isEdit || this._slugEdited) {
130
+ if (this._slugEdited) {
187
131
  return;
188
132
  }
189
133
 
@@ -237,9 +181,7 @@ export class JantCollectionForm extends LitElement {
237
181
  if (!this._suggestedSlug) return;
238
182
  this._slug = this._suggestedSlug;
239
183
  this._slugEdited = false;
240
- if (this.variant === "quick") {
241
- this._showSlugEditor = false;
242
- }
184
+ this._showSlugEditor = false;
243
185
  }
244
186
 
245
187
  #getCollectionLinkPreview(): string {
@@ -276,7 +218,7 @@ export class JantCollectionForm extends LitElement {
276
218
  </p>`;
277
219
  }
278
220
 
279
- #renderQuickSlugControls() {
221
+ #renderSlugControls() {
280
222
  const hasPreview = Boolean(this._slug.trim());
281
223
  const canResetToTitle =
282
224
  this._showSlugEditor &&
@@ -293,17 +235,19 @@ export class JantCollectionForm extends LitElement {
293
235
  <div class="field">
294
236
  <div class="collection-quick-link-row">
295
237
  <label class="label mb-0">${this.labels.slugLabel}</label>
296
- ${canResetToTitle
297
- ? html`
298
- <button
299
- type="button"
300
- class="collection-quick-link-action"
301
- @click=${() => this.#resetSlugToSuggested()}
302
- >
303
- ${this.labels.resetSlugLabel}
304
- </button>
305
- `
306
- : nothing}
238
+ ${
239
+ canResetToTitle
240
+ ? html`
241
+ <button
242
+ type="button"
243
+ class="collection-quick-link-action"
244
+ @click=${() => this.#resetSlugToSuggested()}
245
+ >
246
+ ${this.labels.resetSlugLabel}
247
+ </button>
248
+ `
249
+ : nothing
250
+ }
307
251
  </div>
308
252
  <input
309
253
  type="text"
@@ -313,9 +257,9 @@ export class JantCollectionForm extends LitElement {
313
257
  pattern="[a-z0-9]+(?:-[a-z0-9]+)*"
314
258
  maxlength=${MAX_COLLECTION_SLUG_LENGTH}
315
259
  .value=${this._slug}
316
- aria-invalid=${this.#getSlugValidationMessage()
317
- ? "true"
318
- : "false"}
260
+ aria-invalid=${
261
+ this.#getSlugValidationMessage() ? "true" : "false"
262
+ }
319
263
  placeholder="my-collection"
320
264
  @input=${(event: Event) => this.#handleSlugInput(event)}
321
265
  />
@@ -369,9 +313,7 @@ export class JantCollectionForm extends LitElement {
369
313
  }
370
314
 
371
315
  if (!slug || this.#getSlugValidationMessage()) {
372
- if (this.variant === "quick" && !this._showSlugEditor) {
373
- this.#showSlugEditor();
374
- }
316
+ this.#showSlugEditor();
375
317
  this.updateComplete.then(() => {
376
318
  this.querySelector<HTMLInputElement>(
377
319
  "[data-collection-slug-input]",
@@ -382,17 +324,7 @@ export class JantCollectionForm extends LitElement {
382
324
 
383
325
  const detail: CollectionSubmitDetail = {
384
326
  endpoint: this.action,
385
- isEdit: this.isEdit,
386
- data: {
387
- title,
388
- slug,
389
- description:
390
- this.variant === "quick"
391
- ? undefined
392
- : this._description.trim() || undefined,
393
- sortOrder:
394
- this.variant === "quick" ? undefined : this._sortOrder || undefined,
395
- },
327
+ data: { title, slug },
396
328
  };
397
329
 
398
330
  this.dispatchEvent(
@@ -404,141 +336,30 @@ export class JantCollectionForm extends LitElement {
404
336
  }
405
337
 
406
338
  render() {
407
- const isQuick = this.variant === "quick";
408
- const formClass = isQuick
409
- ? "flex flex-col gap-4"
410
- : "collection-editor-form";
411
-
412
339
  return html`
413
340
  <form
414
- class=${formClass}
341
+ class="flex flex-col gap-4"
415
342
  @submit=${(event: Event) => void this.#handleSubmit(event)}
416
343
  >
417
- ${isQuick
418
- ? html`
419
- <div class="field">
420
- <label class="label">${this.labels.titleLabel}</label>
421
- <input
422
- type="text"
423
- class="input"
424
- data-collection-title-input
425
- required
426
- maxlength=${MAX_COLLECTION_TITLE_LENGTH}
427
- .value=${this._title}
428
- placeholder=${this.isEdit
429
- ? nothing
430
- : this.labels.titlePlaceholder}
431
- @input=${(event: Event) => void this.#handleTitleInput(event)}
432
- />
433
- </div>
434
-
435
- ${this.#renderQuickSlugControls()}
436
- `
437
- : html`
438
- <div class="collection-editor-primary-grid">
439
- <div class="field collection-editor-title-field">
440
- <label class="label">${this.labels.titleLabel}</label>
441
- <input
442
- type="text"
443
- class="input"
444
- data-collection-title-input
445
- required
446
- maxlength=${MAX_COLLECTION_TITLE_LENGTH}
447
- .value=${this._title}
448
- placeholder=${this.isEdit
449
- ? nothing
450
- : this.labels.titlePlaceholder}
451
- @input=${(event: Event) =>
452
- void this.#handleTitleInput(event)}
453
- />
454
- </div>
455
-
456
- <div class="field collection-editor-slug-field">
457
- <label class="label">${this.labels.slugLabel}</label>
458
- <input
459
- type="text"
460
- class="input"
461
- data-collection-slug-input
462
- required
463
- pattern="[a-z0-9]+(?:-[a-z0-9]+)*"
464
- maxlength=${MAX_COLLECTION_SLUG_LENGTH}
465
- .value=${this._slug}
466
- aria-invalid=${this.#getSlugValidationMessage()
467
- ? "true"
468
- : "false"}
469
- placeholder=${this.isEdit ? nothing : "my-collection"}
470
- @input=${(event: Event) => this.#handleSlugInput(event)}
471
- />
472
- ${this.#renderSlugHelper()}
473
- </div>
474
- </div>
475
-
476
- <div class="collection-editor-secondary-grid">
477
- <div class="field collection-editor-description-field">
478
- <label class="label">${this.labels.descriptionLabel}</label>
479
- <div
480
- class="settings-tiptap-editor"
481
- data-collection-desc-editor
482
- ></div>
483
- </div>
484
-
485
- <div class="field collection-editor-sort-field">
486
- <label class="label">${this.labels.sortOrderLabel}</label>
487
- <select
488
- class="select"
489
- .value=${this._sortOrder}
490
- @change=${(event: Event) => {
491
- const target = event.target as HTMLSelectElement;
492
- this._sortOrder = target.value as CollectionSortOrder;
493
- }}
494
- >
495
- <option value="newest">${this.labels.sortNewest}</option>
496
- <option value="oldest">${this.labels.sortOldest}</option>
497
- <option value="rating_desc">
498
- ${this.labels.sortRatingDesc}
499
- </option>
500
- </select>
501
- </div>
502
- </div>
503
- `}
504
- ${isQuick
505
- ? html`
506
- <button type="submit" class="sr-only">
507
- ${this.labels.quickSubmitLabel}
508
- </button>
509
- `
510
- : html`
511
- <div class="collection-editor-actions">
512
- <button
513
- type="submit"
514
- class="btn collection-editor-submit"
515
- ?disabled=${this._loading}
516
- >
517
- ${this._loading
518
- ? html`<svg
519
- class="animate-spin size-4"
520
- xmlns="http://www.w3.org/2000/svg"
521
- viewBox="0 0 24 24"
522
- fill="none"
523
- stroke="currentColor"
524
- stroke-width="2"
525
- stroke-linecap="round"
526
- stroke-linejoin="round"
527
- role="status"
528
- >
529
- <path d="M21 12a9 9 0 1 1-6.219-8.56" />
530
- </svg>`
531
- : nothing}
532
- ${this.labels.submitLabel}
533
- </button>
534
- <a
535
- href=${this.cancelHref}
536
- class="btn-outline collection-editor-cancel"
537
- >
538
- ${this.labels.cancelLabel}
539
- </a>
540
- </div>
541
- `}
344
+ <div class="field">
345
+ <label class="label">${this.labels.titleLabel}</label>
346
+ <input
347
+ type="text"
348
+ class="input"
349
+ data-collection-title-input
350
+ required
351
+ maxlength=${MAX_COLLECTION_TITLE_LENGTH}
352
+ .value=${this._title}
353
+ placeholder=${this.labels.titlePlaceholder}
354
+ @input=${(event: Event) => void this.#handleTitleInput(event)}
355
+ />
356
+ </div>
357
+
358
+ ${this.#renderSlugControls()}
359
+
360
+ <button type="submit" class="sr-only">
361
+ ${this.labels.quickSubmitLabel}
362
+ </button>
542
363
  </form>
543
364
  `;
544
365
  }
@@ -13,7 +13,8 @@ import { classMap } from "lit/directives/class-map.js";
13
13
  import { unsafeSVG } from "lit/directives/unsafe-svg.js";
14
14
  import { NAVIGATION_SETTINGS_PATH } from "../../lib/settings-paths.js";
15
15
  import { openNewCompose } from "../compose-launch.js";
16
- import { publicPath } from "../runtime-paths.js";
16
+ import { isFullUrl, toInternalPath } from "../../lib/url.js";
17
+ import { navPath, sitePathPrefix } from "../runtime-paths.js";
17
18
  import { getBestFieldSearchRank, normalizeSearch } from "../search-rank.js";
18
19
 
19
20
  // ---------------------------------------------------------------------------
@@ -23,7 +24,7 @@ import { getBestFieldSearchRank, normalizeSearch } from "../search-rank.js";
23
24
  interface PaletteItem {
24
25
  title: string;
25
26
  path: string;
26
- type: "post" | "collection" | "system";
27
+ type: "post" | "collection" | "smart_collection" | "system";
27
28
  status?: "draft" | "published";
28
29
  }
29
30
 
@@ -58,6 +59,9 @@ const ICONS = {
58
59
  post: `<svg ${ICON_ATTRS}><path d="M8 2v4"/><path d="M12 2v4"/><path d="M16 2v4"/><rect width="16" height="18" x="4" y="4" rx="2"/><path d="M8 10h6"/><path d="M8 14h8"/><path d="M8 18h5"/></svg>`,
59
60
  // clipboard/collection icon from PostFooter
60
61
  collection: `<svg ${ICON_ATTRS} viewBox="0 0 16 16" stroke-width="1.35"><rect x="3" y="5.05" width="10" height="8.15" rx="2.2"/><path d="M5.1 5.05V4.2a1.1 1.1 0 0 1 1.1-1.1h3.6a1.1 1.1 0 0 1 1.1 1.1v.85"/></svg>`,
62
+ // funnel — the same icon the directory and the page use, so a smart
63
+ // collection reads as a collection with a rule behind it everywhere at once.
64
+ smart_collection: `<svg ${ICON_ATTRS}><path d="M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z"/></svg>`,
61
65
  // settings gear from SettingsRootContent
62
66
  system: `<svg ${ICON_ATTRS}><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>`,
63
67
  // zap/lightning for commands
@@ -73,6 +77,29 @@ export function getPathCommandTarget(query: string): string {
73
77
  return stripped ? `/${stripped}` : "/";
74
78
  }
75
79
 
80
+ /**
81
+ * The local path a pasted full URL points at, when it points at this site.
82
+ *
83
+ * Copying an address out of the browser bar and dropping it in the palette
84
+ * should go there, the same as typing `/`. Someone else's URL is left to
85
+ * navigate mode, where it falls through to a full-text search — looking for
86
+ * the post where you linked something is the likelier intent.
87
+ *
88
+ * @param query - Raw palette query
89
+ * @returns Internal path with its query string, or null when not our URL
90
+ */
91
+ export function getPastedAddressTarget(query: string): string | null {
92
+ const trimmed = query.trim();
93
+ if (!isFullUrl(trimmed)) return null;
94
+
95
+ return toInternalPath(trimmed, {
96
+ siteOrigins: [window.location.origin],
97
+ sitePathPrefix: sitePathPrefix(),
98
+ // A pasted `/en/about` was copied from that view and belongs there.
99
+ keepQuery: true,
100
+ });
101
+ }
102
+
76
103
  export function getPathCommandSearchQuery(query: string): string {
77
104
  return query
78
105
  .trim()
@@ -246,6 +273,7 @@ export class JantCommandPalette extends LitElement {
246
273
  if (first === ">" || first === ">") return "command";
247
274
  if (first === "?" || first === "?") return "search";
248
275
  if (first === "/" || first === "/") return "path";
276
+ if (getPastedAddressTarget(this._query)) return "path";
249
277
  return "navigate";
250
278
  }
251
279
 
@@ -326,8 +354,11 @@ export class JantCommandPalette extends LitElement {
326
354
  }
327
355
 
328
356
  if (mode === "path") {
329
- const target = getPathCommandTarget(this._query);
330
- const searchQuery = getPathCommandSearchQuery(this._query);
357
+ const pasted = getPastedAddressTarget(this._query);
358
+ const target = pasted ?? getPathCommandTarget(this._query);
359
+ // A pasted URL is already an answer; only a typed path is also plausible
360
+ // as search words.
361
+ const searchQuery = pasted ? "" : getPathCommandSearchQuery(this._query);
331
362
  const items: DisplayItem[] = [
332
363
  {
333
364
  label: `Go to ${target}`,
@@ -380,7 +411,11 @@ export class JantCommandPalette extends LitElement {
380
411
  #executeSearch(query: string) {
381
412
  saveHistory(SEARCH_HISTORY_KEY, query);
382
413
  this.close();
383
- window.location.href = publicPath(`/search?q=${encodeURIComponent(query)}`);
414
+ // navPath, not publicPath: a search run from inside /en belongs in /en.
415
+ // The palette jumps to settings pages and posts through this same code
416
+ // path, so it cannot assert the kind of any one target — navPath decides
417
+ // per target and leaves the site-wide ones alone.
418
+ window.location.href = navPath(`/search?q=${encodeURIComponent(query)}`);
384
419
  }
385
420
 
386
421
  #executeItem(index: number) {
@@ -409,7 +444,7 @@ export class JantCommandPalette extends LitElement {
409
444
  if (displayItem?.pathTarget) {
410
445
  saveHistory(NAV_HISTORY_KEY, displayItem.pathTarget);
411
446
  this.close();
412
- window.location.href = publicPath(displayItem.pathTarget);
447
+ window.location.href = navPath(displayItem.pathTarget);
413
448
  return;
414
449
  }
415
450
 
@@ -431,11 +466,15 @@ export class JantCommandPalette extends LitElement {
431
466
  saveHistory(NAV_HISTORY_KEY, item.path);
432
467
  this.close();
433
468
  if (item.type === "system") {
434
- window.location.href = publicPath(item.path);
469
+ window.location.href = navPath(item.path);
435
470
  } else if (item.type === "collection") {
436
- window.location.href = publicPath(`/collections/${item.path}`);
471
+ window.location.href = navPath(`/collections/${item.path}`);
472
+ } else if (item.type === "smart_collection") {
473
+ // Straight to its address. `/collections/{slug}` redirects for a
474
+ // collection but names nothing for a smart collection.
475
+ window.location.href = navPath(`/${item.path}`);
437
476
  } else {
438
- window.location.href = publicPath(
477
+ window.location.href = navPath(
439
478
  item.status === "draft"
440
479
  ? `/preview/${item.path}?edit=1`
441
480
  : `/${item.path}`,
@@ -554,19 +593,23 @@ export class JantCommandPalette extends LitElement {
554
593
  <span class="command-palette-result-icon">${unsafeSVG(item.icon)}</span>
555
594
  <span class="command-palette-result-body">
556
595
  <span class="command-palette-result-title">${item.label}</span>
557
- ${item.secondary
558
- ? html`<span class="command-palette-result-path"
559
- >${item.secondary}</span
560
- >`
561
- : nothing}
596
+ ${
597
+ item.secondary
598
+ ? html`<span class="command-palette-result-path"
599
+ >${item.secondary}</span
600
+ >`
601
+ : nothing
602
+ }
562
603
  </span>
563
- ${item.shortcut
564
- ? html`<kbd
565
- class="command-palette-result-shortcut"
566
- aria-label="Command or Control plus Enter"
567
- >${item.shortcut}</kbd
568
- >`
569
- : nothing}
604
+ ${
605
+ item.shortcut
606
+ ? html`<kbd
607
+ class="command-palette-result-shortcut"
608
+ aria-label="Command or Control plus Enter"
609
+ >${item.shortcut}</kbd
610
+ >`
611
+ : nothing
612
+ }
570
613
  </div>
571
614
  `;
572
615
  }
@@ -606,39 +649,47 @@ export class JantCommandPalette extends LitElement {
606
649
  role="combobox"
607
650
  aria-expanded="true"
608
651
  aria-controls="command-palette-results"
609
- aria-activedescendant=${items.length > 0
610
- ? `command-palette-item-${this._selectedIndex}`
611
- : ""}
652
+ aria-activedescendant=${
653
+ items.length > 0
654
+ ? `command-palette-item-${this._selectedIndex}`
655
+ : ""
656
+ }
612
657
  autocomplete="off"
613
658
  spellcheck="false"
614
659
  />
615
- ${this._loading
616
- ? html`<span class="command-palette-spinner"></span>`
617
- : nothing}
660
+ ${
661
+ this._loading
662
+ ? html`<span class="command-palette-spinner"></span>`
663
+ : nothing
664
+ }
618
665
  </div>
619
666
 
620
- ${items.length > 0
621
- ? html`
622
- <div
623
- id="command-palette-results"
624
- class="command-palette-results-container"
625
- role="listbox"
626
- >
627
- ${resultItems.length > 0
628
- ? html`<div class="command-palette-results">
629
- ${resultItems.map(({ item, index }) =>
630
- this.#renderItem(item, index),
631
- )}
632
- </div>`
633
- : nothing}
634
- ${footerItems.map(({ item, index }) =>
635
- this.#renderItem(item, index, true),
636
- )}
637
- </div>
638
- `
639
- : this._query.trim() && !this._loading
640
- ? html`<div class="command-palette-empty">No results</div>`
641
- : nothing}
667
+ ${
668
+ items.length > 0
669
+ ? html`
670
+ <div
671
+ id="command-palette-results"
672
+ class="command-palette-results-container"
673
+ role="listbox"
674
+ >
675
+ ${
676
+ resultItems.length > 0
677
+ ? html`<div class="command-palette-results">
678
+ ${resultItems.map(({ item, index }) =>
679
+ this.#renderItem(item, index),
680
+ )}
681
+ </div>`
682
+ : nothing
683
+ }
684
+ ${footerItems.map(({ item, index }) =>
685
+ this.#renderItem(item, index, true),
686
+ )}
687
+ </div>
688
+ `
689
+ : this._query.trim() && !this._loading
690
+ ? html`<div class="command-palette-empty">No results</div>`
691
+ : nothing
692
+ }
642
693
  </div>
643
694
  </dialog>
644
695
  `;