@jant/core 0.6.16 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (592) hide show
  1. package/README.md +11 -11
  2. package/bin/commands/assets/upload.js +192 -15
  3. package/bin/commands/export.js +3 -1
  4. package/bin/commands/import-site.js +749 -220
  5. package/bin/commands/reset-password.js +21 -8
  6. package/bin/commands/setup.js +136 -0
  7. package/bin/commands/site/export.js +71 -34
  8. package/bin/commands/site/pull-media.js +2 -6
  9. package/bin/commands/site/snapshot/export.js +19 -61
  10. package/bin/commands/site/snapshot/import.js +23 -24
  11. package/bin/lib/d1-query.js +92 -18
  12. package/bin/lib/hugo-markdown.js +4 -0
  13. package/bin/lib/node-env.js +40 -4
  14. package/bin/lib/site-pull-media.js +21 -28
  15. package/bin/lib/site-selection.js +94 -29
  16. package/bin/lib/site-snapshot.js +338 -3
  17. package/bin/lib/sql-export.js +68 -5
  18. package/bin/lib/wrangler-cli.js +37 -2
  19. package/bin/lib/wrangler-json.js +35 -0
  20. package/bin/lib/zip-archive.js +187 -0
  21. package/dist/{app-C7_4oab9.js → app-ZcaI1kPN.js} +53190 -13529
  22. package/dist/client/.vite/manifest.json +255 -90
  23. package/dist/client/_assets/chunks/confirm-D7GrwsCN.js +45 -0
  24. package/dist/client/_assets/chunks/create-editor-B-m7X7S5.js +261 -0
  25. package/dist/client/_assets/chunks/{native-DpcrFAPh.js → emoji-mart-C4RpRKcU.js} +716 -1
  26. package/dist/client/_assets/chunks/footnote-rail-CmeoVtnU.js +1 -0
  27. package/dist/client/_assets/chunks/heic-to-CuP2Qlsw.js +1 -0
  28. package/dist/client/_assets/chunks/jant-locale-picker-DL6mIOT5.js +69 -0
  29. package/dist/client/_assets/chunks/lit-DW8VmJAT.js +2 -0
  30. package/dist/client/_assets/chunks/mediabunny-DRaCQ1r-.js +31 -0
  31. package/dist/client/_assets/chunks/rolldown-runtime-hePW80VL.js +1 -0
  32. package/dist/client/_assets/chunks/settings-paths-BcdySNx6.js +84 -0
  33. package/dist/client/_assets/chunks/site-header-nav-DzXsFk05.js +1 -0
  34. package/dist/client/_assets/chunks/slug-format-CRXO1AL5.js +1 -0
  35. package/dist/client/_assets/chunks/slugify-373citbs.js +1 -0
  36. package/dist/client/_assets/chunks/sortable-list-BJyd-LXE.js +2 -0
  37. package/dist/client/_assets/chunks/types-D5iF3H1a.js +1 -0
  38. package/dist/client/_assets/chunks/unsafe-html-B5ZEXaxg.js +1 -0
  39. package/dist/client/_assets/chunks/unsafe-svg-BkscJx69.js +2 -0
  40. package/dist/client/_assets/client-DAhoqPdr.css +2 -0
  41. package/dist/client/_assets/client-auth-CAMfTrKW.js +828 -0
  42. package/dist/client/_assets/client-author-DQ8R-8KR.css +2 -0
  43. package/dist/client/_assets/client-compose-D6rExKVK.js +2004 -0
  44. package/dist/client/_assets/client-knSEyUJO.js +295 -0
  45. package/dist/client/_assets/client-manage-sVOkqVbH.js +2170 -0
  46. package/dist/client/_assets/client-settings-CGR_nZ78.js +1112 -0
  47. package/dist/github-sync-Gw4orAHk.js +8398 -0
  48. package/dist/index.js +2 -5
  49. package/dist/node.js +11 -9
  50. package/package.json +15 -5
  51. package/src/__tests__/auth-session-freshness.test.ts +57 -0
  52. package/src/__tests__/basecoat-variants.test.ts +121 -0
  53. package/src/__tests__/bin/assets-upload.test.ts +68 -0
  54. package/src/__tests__/dev-scripts.test.ts +203 -0
  55. package/src/__tests__/export-collection-order.test.ts +276 -0
  56. package/src/__tests__/export-feed-ids.test.ts +184 -0
  57. package/src/__tests__/export-feed-order.test.ts +294 -0
  58. package/src/__tests__/export-hugo-build.test.ts +390 -0
  59. package/src/__tests__/export-import-roundtrip.test.ts +94 -0
  60. package/src/__tests__/export-service.test.ts +842 -28
  61. package/src/__tests__/export-smart-collection.test.ts +329 -0
  62. package/src/__tests__/helpers/component-css.ts +91 -0
  63. package/src/__tests__/helpers/db.ts +33 -0
  64. package/src/__tests__/helpers/export-fixtures.ts +3 -0
  65. package/src/__tests__/helpers/hugo-site.ts +146 -0
  66. package/src/__tests__/import-site-command.test.ts +487 -1
  67. package/src/__tests__/mise-config.test.ts +75 -4
  68. package/src/__tests__/node-dev-tasks.test.ts +264 -0
  69. package/src/__tests__/site-export-canonical-import.test.ts +149 -0
  70. package/src/__tests__/snapshot-canonical-replay.test.ts +179 -0
  71. package/src/__tests__/snapshot-settings.test.ts +97 -0
  72. package/src/__tests__/snapshot-tables.test.ts +219 -0
  73. package/src/__tests__/sql-export.test.ts +173 -0
  74. package/src/__tests__/stylesheet-audience.test.ts +159 -0
  75. package/src/__tests__/stylesheet-breakpoints.test.ts +183 -0
  76. package/src/__tests__/zip-archive.test.ts +106 -0
  77. package/src/app.tsx +86 -22
  78. package/src/auth.ts +18 -1
  79. package/src/client/__tests__/collection-page-actions.test.ts +51 -2
  80. package/src/client/__tests__/compose-bridge-image-upload.test.ts +196 -0
  81. package/src/client/__tests__/compose-bridge-video-poster.test.ts +159 -0
  82. package/src/client/__tests__/compose-bridge.test.ts +272 -0
  83. package/src/client/__tests__/compose-launch.test.ts +62 -1
  84. package/src/client/__tests__/compose-shortcuts.test.ts +38 -9
  85. package/src/client/__tests__/compose-triggers.test.ts +90 -0
  86. package/src/client/__tests__/copy-field.test.ts +162 -0
  87. package/src/client/__tests__/custom-url-menu.test.ts +127 -6
  88. package/src/client/__tests__/image-processor.test.ts +324 -2
  89. package/src/client/__tests__/lazy-entries.test.ts +92 -0
  90. package/src/client/__tests__/media-scroll-hint.test.ts +155 -0
  91. package/src/client/__tests__/runtime-paths.test.ts +84 -0
  92. package/src/client/__tests__/smart-collection-page-actions.test.ts +158 -0
  93. package/src/client/__tests__/toast.test.ts +23 -0
  94. package/src/client/__tests__/video-processor-probe.test.ts +175 -0
  95. package/src/client/__tests__/video-processor.test.ts +181 -0
  96. package/src/client/audio-processor.ts +24 -15
  97. package/src/client/audio-waveform.ts +49 -0
  98. package/src/client/collection-dialog-host.ts +72 -0
  99. package/src/client/collection-navigation.ts +51 -16
  100. package/src/client/collection-page-actions.ts +26 -0
  101. package/src/client/components/__tests__/jant-collection-dialog.test.ts +329 -0
  102. package/src/client/components/__tests__/jant-collection-directory.test.ts +274 -10
  103. package/src/client/components/__tests__/jant-collection-form.test.ts +59 -178
  104. package/src/client/components/__tests__/jant-command-palette.test.ts +28 -0
  105. package/src/client/components/__tests__/jant-compose-dialog-draft-storage.test.ts +340 -0
  106. package/src/client/components/__tests__/jant-compose-dialog.test.ts +1250 -53
  107. package/src/client/components/__tests__/jant-compose-editor.test.ts +544 -40
  108. package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +0 -1
  109. package/src/client/components/__tests__/jant-locale-picker.test.ts +207 -0
  110. package/src/client/components/__tests__/jant-nav-manager.test.ts +234 -15
  111. package/src/client/components/__tests__/jant-post-menu.test.ts +413 -2
  112. package/src/client/components/__tests__/jant-post-picker.test.ts +197 -0
  113. package/src/client/components/__tests__/jant-settings-avatar.test.ts +15 -12
  114. package/src/client/components/__tests__/jant-settings-general.test.ts +561 -172
  115. package/src/client/components/__tests__/jant-settings-language.test.ts +851 -0
  116. package/src/client/components/__tests__/jant-smart-collection-dialog.test.ts +665 -0
  117. package/src/client/components/collection-dialog-types.ts +49 -0
  118. package/src/client/components/collection-manager-types.ts +32 -1
  119. package/src/client/components/collection-types.ts +1 -15
  120. package/src/client/components/compose-types.ts +75 -1
  121. package/src/client/components/jant-collection-dialog.ts +722 -0
  122. package/src/client/components/jant-collection-directory.ts +567 -182
  123. package/src/client/components/jant-collection-form.ts +53 -232
  124. package/src/client/components/jant-command-palette.ts +100 -49
  125. package/src/client/components/jant-compose-dialog.ts +1486 -707
  126. package/src/client/components/jant-compose-editor.ts +673 -336
  127. package/src/client/components/jant-compose-fullscreen.ts +51 -43
  128. package/src/client/components/jant-config-editor.ts +65 -53
  129. package/src/client/components/jant-confirm-dialog.ts +13 -11
  130. package/src/client/components/jant-locale-picker.ts +418 -0
  131. package/src/client/components/jant-media-lightbox.ts +146 -136
  132. package/src/client/components/jant-nav-manager.ts +690 -366
  133. package/src/client/components/jant-post-menu.ts +1086 -282
  134. package/src/client/components/jant-post-picker.ts +324 -0
  135. package/src/client/components/jant-repo-picker-types.ts +6 -1
  136. package/src/client/components/jant-repo-picker.ts +137 -108
  137. package/src/client/components/jant-settings-avatar.ts +31 -27
  138. package/src/client/components/jant-settings-general.ts +622 -543
  139. package/src/client/components/jant-settings-language.ts +1087 -0
  140. package/src/client/components/jant-smart-collection-dialog.ts +1021 -0
  141. package/src/client/components/jant-text-preview.ts +94 -86
  142. package/src/client/components/nav-manager-types.ts +62 -1
  143. package/src/client/components/settings-types.ts +14 -29
  144. package/src/client/components/smart-collection-conditions.ts +285 -0
  145. package/src/client/components/smart-collection-dialog-types.ts +68 -0
  146. package/src/client/compose-bridge.ts +214 -141
  147. package/src/client/compose-launch.ts +76 -2
  148. package/src/client/compose-shortcuts.ts +6 -8
  149. package/src/client/compose-triggers.ts +50 -0
  150. package/src/client/copy-field.ts +108 -0
  151. package/src/client/custom-url-menu.ts +60 -4
  152. package/src/client/icons.ts +14 -0
  153. package/src/client/image-plan.ts +165 -0
  154. package/src/client/image-processor.ts +195 -117
  155. package/src/client/image-worker-client.ts +45 -0
  156. package/src/client/image-worker.ts +135 -0
  157. package/src/client/lazy-entries.ts +95 -0
  158. package/src/client/lazy-slugify.ts +2 -2
  159. package/src/client/media-scroll-hint.ts +43 -2
  160. package/src/client/mediabunny.ts +29 -0
  161. package/src/client/post-picker.ts +41 -0
  162. package/src/client/runtime-paths.ts +74 -1
  163. package/src/client/settings-bridge.ts +4 -10
  164. package/src/client/site-header-nav.js +18 -6
  165. package/src/client/smart-collection-dialog-host.ts +116 -0
  166. package/src/client/smart-collection-page-actions.ts +208 -0
  167. package/src/client/tiptap/__tests__/bubble-menu.test.ts +110 -0
  168. package/src/client/tiptap/__tests__/floating-position.test.ts +136 -1
  169. package/src/client/tiptap/__tests__/inline-image-upload.test.ts +37 -0
  170. package/src/client/tiptap/__tests__/list-editing.test.ts +224 -86
  171. package/src/client/tiptap/__tests__/mark-exit.test.ts +1 -2
  172. package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +26 -0
  173. package/src/client/tiptap/bubble-menu.ts +47 -16
  174. package/src/client/tiptap/extensions.ts +0 -3
  175. package/src/client/tiptap/floating-position.ts +216 -2
  176. package/src/client/tiptap/inline-image-upload.ts +7 -3
  177. package/src/client/tiptap/link-toolbar.ts +22 -9
  178. package/src/client/tiptap/structural-keymap.ts +158 -47
  179. package/src/client/toast.ts +13 -0
  180. package/src/client/types/vite-worker.d.ts +12 -0
  181. package/src/client/upload-with-metadata.ts +3 -13
  182. package/src/client/video-processor.ts +351 -107
  183. package/src/client-auth.ts +14 -31
  184. package/src/client-compose.ts +19 -0
  185. package/src/client-manage.ts +13 -0
  186. package/src/client-settings.ts +14 -0
  187. package/src/client-site.ts +10 -3
  188. package/src/client.ts +4 -0
  189. package/src/db/__tests__/backfill-nav-label-mirrors.test.ts +149 -0
  190. package/src/db/__tests__/d1-query.test.ts +56 -1
  191. package/src/db/__tests__/schema-enum-sources.test.ts +33 -0
  192. package/src/db/__tests__/smart-collection-placement.test.ts +402 -0
  193. package/src/db/backfills/0006_clear_mirrored_page_nav_labels.sql +31 -0
  194. package/src/db/migrations/0032_married_tomas.sql +3 -0
  195. package/src/db/migrations/0033_busy_johnny_storm.sql +198 -0
  196. package/src/db/migrations/0034_cheerful_rumiko_fujikawa.sql +35 -0
  197. package/src/db/migrations/meta/0032_snapshot.json +2582 -0
  198. package/src/db/migrations/meta/0033_snapshot.json +2816 -0
  199. package/src/db/migrations/meta/0034_snapshot.json +2816 -0
  200. package/src/db/migrations/meta/_journal.json +21 -0
  201. package/src/db/migrations/pg/0030_abandoned_nighthawk.sql +3 -0
  202. package/src/db/migrations/pg/0031_strong_ultimatum.sql +128 -0
  203. package/src/db/migrations/pg/0032_furry_multiple_man.sql +6 -0
  204. package/src/db/migrations/pg/meta/0030_snapshot.json +3329 -0
  205. package/src/db/migrations/pg/meta/0031_snapshot.json +3660 -0
  206. package/src/db/migrations/pg/meta/0032_snapshot.json +3660 -0
  207. package/src/db/migrations/pg/meta/_journal.json +21 -0
  208. package/src/db/pg/schema.ts +195 -27
  209. package/src/db/post-visibility.ts +117 -0
  210. package/src/db/schema.ts +222 -27
  211. package/src/i18n/__tests__/detect.test.ts +26 -81
  212. package/src/i18n/__tests__/supported-locales.test.ts +83 -0
  213. package/src/i18n/context.tsx +12 -2
  214. package/src/i18n/detect.ts +7 -66
  215. package/src/i18n/locales/glossary.zh-Hans.yml +44 -4
  216. package/src/i18n/locales/glossary.zh-Hant.yml +44 -4
  217. package/src/i18n/locales/public/en.po +490 -158
  218. package/src/i18n/locales/public/en.ts +1 -1
  219. package/src/i18n/locales/public/zh-Hans.po +487 -155
  220. package/src/i18n/locales/public/zh-Hans.ts +1 -1
  221. package/src/i18n/locales/public/zh-Hant.po +487 -155
  222. package/src/i18n/locales/public/zh-Hant.ts +1 -1
  223. package/src/i18n/locales/settings/en.po +555 -73
  224. package/src/i18n/locales/settings/en.ts +1 -1
  225. package/src/i18n/locales/settings/zh-Hans.po +601 -119
  226. package/src/i18n/locales/settings/zh-Hans.ts +1 -1
  227. package/src/i18n/locales/settings/zh-Hant.po +614 -132
  228. package/src/i18n/locales/settings/zh-Hant.ts +1 -1
  229. package/src/i18n/locales.ts +94 -0
  230. package/src/i18n/middleware.ts +12 -2
  231. package/src/i18n/supported-locales.ts +80 -0
  232. package/src/lib/__tests__/api-settings.test.ts +11 -2
  233. package/src/lib/__tests__/asset-path.test.ts +92 -0
  234. package/src/lib/__tests__/deferred.test.ts +90 -0
  235. package/src/lib/__tests__/discover-ping.test.ts +89 -0
  236. package/src/lib/__tests__/discover.test.ts +410 -0
  237. package/src/lib/__tests__/feed-policy.test.ts +26 -0
  238. package/src/lib/__tests__/feed.test.ts +1526 -15
  239. package/src/lib/__tests__/filter-dimensions.test.ts +341 -0
  240. package/src/lib/__tests__/github-sync-repo-name.test.ts +40 -0
  241. package/src/lib/__tests__/hosted-signin.test.ts +1 -1
  242. package/src/lib/__tests__/http-cache.test.ts +162 -0
  243. package/src/lib/__tests__/image.test.ts +27 -1
  244. package/src/lib/__tests__/jant-docs-dev.test.ts +21 -0
  245. package/src/lib/__tests__/jant-docs.test.ts +23 -0
  246. package/src/lib/__tests__/lang-detect.test.ts +231 -0
  247. package/src/lib/__tests__/markdown-to-tiptap.test.ts +105 -0
  248. package/src/lib/__tests__/markdown.test.ts +10 -0
  249. package/src/lib/__tests__/media-helpers.test.ts +81 -0
  250. package/src/lib/__tests__/navigation.test.ts +135 -1
  251. package/src/lib/__tests__/resolve-config.test.ts +131 -3
  252. package/src/lib/__tests__/schemas.test.ts +69 -5
  253. package/src/lib/__tests__/site-unavailable-page.test.ts +25 -0
  254. package/src/lib/__tests__/slug.test.ts +70 -0
  255. package/src/lib/__tests__/slugify.test.ts +85 -0
  256. package/src/lib/__tests__/storage.test.ts +32 -0
  257. package/src/lib/__tests__/thread-fold.test.ts +113 -0
  258. package/src/lib/__tests__/timeline.test.ts +87 -0
  259. package/src/lib/__tests__/tiptap-to-markdown.test.ts +172 -4
  260. package/src/lib/__tests__/upload-sideload.test.ts +14 -0
  261. package/src/lib/__tests__/url-fetch.test.ts +1 -2
  262. package/src/lib/__tests__/url.test.ts +78 -72
  263. package/src/lib/__tests__/view-language.test.ts +413 -0
  264. package/src/lib/__tests__/worker-response-cache.test.ts +90 -0
  265. package/src/lib/address.ts +57 -0
  266. package/src/lib/api-posts.ts +12 -0
  267. package/src/lib/asset-path.ts +98 -1
  268. package/src/lib/build-env.ts +16 -0
  269. package/src/lib/collection-groups.ts +5 -1
  270. package/src/lib/collection-paths.ts +3 -10
  271. package/src/lib/constants.ts +28 -3
  272. package/src/lib/copy-field.ts +36 -0
  273. package/src/lib/crypto.ts +40 -0
  274. package/src/lib/csp-builder.ts +2 -1
  275. package/src/lib/deferred.ts +79 -0
  276. package/src/lib/discover-ping.ts +141 -0
  277. package/src/lib/discover.ts +439 -0
  278. package/src/lib/env.ts +134 -0
  279. package/src/lib/errors.ts +33 -0
  280. package/src/lib/feed-policy.ts +236 -2
  281. package/src/lib/feed.ts +926 -73
  282. package/src/lib/filter-dimensions.ts +1349 -0
  283. package/src/lib/github-sync-repo-name.ts +45 -0
  284. package/src/lib/github-sync-site-config.ts +3 -0
  285. package/src/lib/hosted-signin.ts +1 -1
  286. package/src/lib/http-cache.ts +148 -0
  287. package/src/lib/hugo-markdown.ts +62 -0
  288. package/src/lib/icons.ts +9 -2
  289. package/src/lib/ids.ts +3 -0
  290. package/src/lib/image.ts +17 -4
  291. package/src/lib/jant-docs.ts +42 -0
  292. package/src/lib/lang-detect.ts +308 -0
  293. package/src/lib/markdown-manager.ts +392 -2
  294. package/src/lib/media-helpers.ts +13 -1
  295. package/src/lib/navigation.ts +78 -11
  296. package/src/lib/pagination.ts +28 -24
  297. package/src/lib/per-language-surfaces.ts +67 -0
  298. package/src/lib/post-body-html.ts +11 -4
  299. package/src/lib/post-meta.ts +19 -0
  300. package/src/lib/render.tsx +55 -7
  301. package/src/lib/resolve-config.ts +111 -6
  302. package/src/lib/schemas.ts +251 -15
  303. package/src/lib/site-header-fragment.tsx +2 -1
  304. package/src/lib/site-unavailable-page.ts +47 -0
  305. package/src/lib/slug.ts +51 -7
  306. package/src/lib/slugify.ts +113 -0
  307. package/src/lib/startup-config.ts +4 -6
  308. package/src/lib/storage.ts +7 -0
  309. package/src/lib/summary.ts +50 -0
  310. package/src/lib/thread-fold.ts +161 -0
  311. package/src/lib/time.ts +32 -0
  312. package/src/lib/timeline.ts +43 -2
  313. package/src/lib/tiptap-render.ts +1 -2
  314. package/src/lib/tiptap-to-markdown.ts +13 -7
  315. package/src/lib/upload.ts +16 -13
  316. package/src/lib/url.ts +159 -86
  317. package/src/lib/version.ts +10 -8
  318. package/src/lib/view-language.ts +436 -0
  319. package/src/lib/view.ts +53 -25
  320. package/src/lib/viewer-context.ts +62 -0
  321. package/src/middleware/__tests__/auth.test.ts +70 -2
  322. package/src/middleware/__tests__/cache-control.test.ts +71 -3
  323. package/src/middleware/__tests__/onboarding.test.ts +76 -5
  324. package/src/middleware/__tests__/secure-headers.test.ts +1 -1
  325. package/src/middleware/__tests__/session.test.ts +181 -33
  326. package/src/middleware/auth.ts +50 -31
  327. package/src/middleware/cache-control.ts +75 -10
  328. package/src/middleware/config.ts +6 -6
  329. package/src/middleware/error-handler.ts +12 -0
  330. package/src/middleware/onboarding.ts +32 -10
  331. package/src/middleware/secure-headers.ts +1 -1
  332. package/src/middleware/session.ts +96 -8
  333. package/src/node/__tests__/cli-migrate.test.ts +21 -19
  334. package/src/node/__tests__/cli-reset-password.test.ts +81 -8
  335. package/src/node/__tests__/cli-runtime-target.test.ts +48 -1
  336. package/src/node/__tests__/cli-setup.test.ts +163 -0
  337. package/src/node/__tests__/cli-site-selection.test.ts +128 -38
  338. package/src/node/__tests__/cli-site-snapshot.test.ts +67 -0
  339. package/src/node/__tests__/cli-snapshot-meta.test.ts +20 -0
  340. package/src/node/__tests__/runtime.test.ts +75 -0
  341. package/src/node/index.ts +3 -0
  342. package/src/node/request-handler.ts +3 -1
  343. package/src/preset.css +76 -22
  344. package/src/routes/__tests__/feed-validator.test.ts +156 -0
  345. package/src/routes/api/__tests__/collections.test.ts +114 -0
  346. package/src/routes/api/__tests__/discover.test.ts +222 -0
  347. package/src/routes/api/__tests__/nav-items.test.ts +212 -2
  348. package/src/routes/api/__tests__/post-translations.test.ts +829 -0
  349. package/src/routes/api/__tests__/posts.test.ts +24 -0
  350. package/src/routes/api/__tests__/settings.test.ts +24 -3
  351. package/src/routes/api/__tests__/telegram.test.ts +6 -5
  352. package/src/routes/api/__tests__/upload.test.ts +34 -0
  353. package/src/routes/api/collections.ts +49 -6
  354. package/src/routes/api/custom-urls.ts +0 -1
  355. package/src/routes/api/discover.ts +95 -0
  356. package/src/routes/api/export.ts +6 -3
  357. package/src/routes/api/internal/__tests__/sites.test.ts +42 -2
  358. package/src/routes/api/internal/sites.ts +0 -1
  359. package/src/routes/api/nav-items.ts +55 -8
  360. package/src/routes/api/posts.ts +161 -0
  361. package/src/routes/api/public/__tests__/archive.test.ts +111 -0
  362. package/src/routes/api/public/archive.ts +77 -81
  363. package/src/routes/api/public/posts.ts +44 -7
  364. package/src/routes/api/smart-collections.ts +132 -0
  365. package/src/routes/api/telegram.ts +2 -40
  366. package/src/routes/api/upload.ts +10 -3
  367. package/src/routes/api/uploads.ts +1 -2
  368. package/src/routes/auth/__tests__/setup-auth.test.ts +137 -0
  369. package/src/routes/auth/__tests__/setup-discover.test.ts +204 -0
  370. package/src/routes/auth/__tests__/setup-flow.test.ts +270 -0
  371. package/src/routes/auth/__tests__/setup-page.test.tsx +329 -0
  372. package/src/routes/auth/__tests__/setup-steps.test.ts +310 -0
  373. package/src/routes/auth/__tests__/setup.test.ts +278 -26
  374. package/src/routes/auth/__tests__/signin.test.ts +135 -0
  375. package/src/routes/auth/setup.tsx +934 -186
  376. package/src/routes/auth/signin.tsx +10 -8
  377. package/src/routes/compose.tsx +9 -0
  378. package/src/routes/dash/__tests__/custom-urls-archive.test.ts +44 -0
  379. package/src/routes/dash/__tests__/custom-urls-page.test.ts +49 -0
  380. package/src/routes/dash/__tests__/delete-account.test.ts +106 -0
  381. package/src/routes/dash/__tests__/github-sync-app.test.ts +365 -0
  382. package/src/routes/dash/__tests__/settings-discover.test.ts +213 -0
  383. package/src/routes/dash/custom-urls.tsx +1 -37
  384. package/src/routes/dash/settings.tsx +714 -111
  385. package/src/routes/discover-announce.ts +96 -0
  386. package/src/routes/feed/__tests__/feed.test.ts +165 -2
  387. package/src/routes/feed/feed.ts +69 -40
  388. package/src/routes/feed/sitemap.ts +95 -11
  389. package/src/routes/pages/__tests__/archive-indexing.test.ts +200 -0
  390. package/src/routes/pages/__tests__/archive-params.test.ts +370 -6
  391. package/src/routes/pages/__tests__/collection-routing.test.ts +16 -0
  392. package/src/routes/pages/__tests__/collections-feed-links.test.ts +72 -0
  393. package/src/routes/pages/__tests__/collections.test.ts +9 -3
  394. package/src/routes/pages/__tests__/custom-archive-url.test.ts +119 -0
  395. package/src/routes/pages/__tests__/feed-autodiscovery.test.ts +148 -0
  396. package/src/routes/pages/__tests__/language-routing.test.ts +679 -0
  397. package/src/routes/pages/__tests__/post-page-round-trips.test.ts +211 -0
  398. package/src/routes/pages/__tests__/post-preview-partial.test.ts +96 -0
  399. package/src/routes/pages/__tests__/smart-collection.test.ts +298 -0
  400. package/src/routes/pages/__tests__/subscribe.test.ts +248 -0
  401. package/src/routes/pages/__tests__/thread-order.test.ts +179 -0
  402. package/src/routes/pages/archive.tsx +487 -379
  403. package/src/routes/pages/collection.tsx +94 -96
  404. package/src/routes/pages/collections.tsx +38 -50
  405. package/src/routes/pages/featured.tsx +43 -18
  406. package/src/routes/pages/home.tsx +25 -6
  407. package/src/routes/pages/language.tsx +113 -0
  408. package/src/routes/pages/latest.tsx +26 -16
  409. package/src/routes/pages/new.tsx +3 -0
  410. package/src/routes/pages/page.tsx +228 -45
  411. package/src/routes/pages/partials.tsx +41 -0
  412. package/src/routes/pages/search.tsx +22 -5
  413. package/src/routes/pages/smart-collection.tsx +340 -0
  414. package/src/routes/pages/subscribe.tsx +182 -0
  415. package/src/runtime/__tests__/index.test.ts +19 -0
  416. package/src/runtime/__tests__/node.test.ts +129 -4
  417. package/src/runtime/__tests__/readiness.test.ts +23 -0
  418. package/src/runtime/cloudflare.ts +21 -2
  419. package/src/runtime/index.ts +27 -1
  420. package/src/runtime/node.ts +67 -1
  421. package/src/runtime/readiness.ts +15 -0
  422. package/src/runtime/site.ts +95 -24
  423. package/src/services/__tests__/auth.test.ts +116 -7
  424. package/src/services/__tests__/bootstrap-setup-instance.test.ts +230 -0
  425. package/src/services/__tests__/collection.test.ts +108 -4
  426. package/src/services/__tests__/custom-url.test.ts +34 -0
  427. package/src/services/__tests__/github-app-installations.test.ts +153 -0
  428. package/src/services/__tests__/github-sync-push.test.ts +46 -0
  429. package/src/services/__tests__/language.test.ts +495 -0
  430. package/src/services/__tests__/media.test.ts +31 -0
  431. package/src/services/__tests__/navigation.test.ts +213 -10
  432. package/src/services/__tests__/path.test.ts +236 -0
  433. package/src/services/__tests__/post-language.test.ts +830 -0
  434. package/src/services/__tests__/post-timeline.test.ts +127 -0
  435. package/src/services/__tests__/post.test.ts +120 -9
  436. package/src/services/__tests__/settings.test.ts +228 -17
  437. package/src/services/__tests__/site-admin.test.ts +5 -5
  438. package/src/services/__tests__/smart-collection.test.ts +651 -0
  439. package/src/services/about-page.ts +22 -1
  440. package/src/services/auth.ts +51 -49
  441. package/src/services/bootstrap.ts +408 -39
  442. package/src/services/collection-directory-position.ts +76 -0
  443. package/src/services/collection.ts +331 -46
  444. package/src/services/custom-url.ts +29 -24
  445. package/src/services/export-theme/assets/client-site.css +1 -1
  446. package/src/services/export-theme/assets/client-site.js +123 -123
  447. package/src/services/export-theme/layouts/_default/alias.html +27 -1
  448. package/src/services/export-theme/layouts/_default/list.html +2 -63
  449. package/src/services/export-theme/layouts/_default/rss.xml +103 -38
  450. package/src/services/export-theme/layouts/collections/list.html +3 -3
  451. package/src/services/export-theme/layouts/featured/list.html +1 -4
  452. package/src/services/export-theme/layouts/index.html +40 -26
  453. package/src/services/export-theme/layouts/partials/collection-members.html +100 -0
  454. package/src/services/export-theme/layouts/partials/collection-threads.html +33 -0
  455. package/src/services/export-theme/layouts/partials/featured-members.html +35 -0
  456. package/src/services/export-theme/layouts/partials/featured-thread.html +1 -1
  457. package/src/services/export-theme/layouts/partials/feed-post-content.xml +105 -2
  458. package/src/services/export-theme/layouts/partials/footer.html +1 -1
  459. package/src/services/export-theme/layouts/partials/head.html +1 -1
  460. package/src/services/export-theme/layouts/partials/header.html +5 -3
  461. package/src/services/export-theme/layouts/partials/jant-data.html +23 -0
  462. package/src/services/export-theme/layouts/partials/latest-members.html +48 -0
  463. package/src/services/export-theme/layouts/partials/media-gallery.html +35 -21
  464. package/src/services/export-theme/layouts/partials/pagination.html +14 -13
  465. package/src/services/export-theme/layouts/partials/post-card.html +8 -4
  466. package/src/services/export-theme/layouts/partials/reply.html +2 -1
  467. package/src/services/export-theme/layouts/partials/smart-collection-members.html +129 -0
  468. package/src/services/export-theme/layouts/partials/thread-preview.html +41 -31
  469. package/src/services/export-theme/layouts/post/list.html +1 -1
  470. package/src/services/export-theme/layouts/smart_collection/list.html +24 -0
  471. package/src/services/export-theme/styles/main.css +13 -2
  472. package/src/services/export-theme/theme.toml +1 -1
  473. package/src/services/export.ts +880 -72
  474. package/src/services/github-app-installations.ts +171 -4
  475. package/src/services/github-sync.ts +69 -15
  476. package/src/services/index.ts +34 -3
  477. package/src/services/language.ts +412 -0
  478. package/src/services/mcp.ts +22 -2
  479. package/src/services/media.ts +22 -7
  480. package/src/services/navigation.ts +429 -67
  481. package/src/services/path.ts +254 -6
  482. package/src/services/post.ts +1234 -190
  483. package/src/services/search.ts +39 -2
  484. package/src/services/settings.ts +238 -43
  485. package/src/services/site-admin.ts +27 -14
  486. package/src/services/site.ts +160 -32
  487. package/src/services/smart-collection.ts +636 -0
  488. package/src/services/telegram.ts +20 -22
  489. package/src/services/upload-session.ts +1 -3
  490. package/src/style-author.css +27 -0
  491. package/src/styles/components-author.css +1622 -0
  492. package/src/styles/components.css +24 -1571
  493. package/src/styles/site-media.css +16 -2
  494. package/src/styles/tokens.css +82 -12
  495. package/src/styles/ui-author.css +7602 -0
  496. package/src/styles/ui.css +1090 -7216
  497. package/src/types/app-context.ts +23 -0
  498. package/src/types/bindings.ts +8 -0
  499. package/src/types/config.ts +94 -15
  500. package/src/types/constants.ts +188 -16
  501. package/src/types/entities.ts +87 -1
  502. package/src/types/operations.ts +74 -2
  503. package/src/types/props.ts +157 -31
  504. package/src/types/views.ts +55 -1
  505. package/src/ui/__tests__/font-themes.test.ts +52 -12
  506. package/src/ui/compose/ComposeDialog.tsx +201 -7
  507. package/src/ui/compose/ComposePrompt.tsx +1 -5
  508. package/src/ui/compose/__tests__/ComposeDialog.test.tsx +125 -0
  509. package/src/ui/dash/appearance/AdvancedContent.tsx +2 -2
  510. package/src/ui/dash/appearance/CodeInjectionContent.tsx +12 -16
  511. package/src/ui/dash/appearance/NavigationContent.tsx +117 -16
  512. package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +33 -11
  513. package/src/ui/dash/appearance/__tests__/system-nav-descriptions.test.ts +44 -0
  514. package/src/ui/dash/appearance/system-nav-descriptions.ts +75 -0
  515. package/src/ui/dash/settings/AccountMenuContent.tsx +2 -2
  516. package/src/ui/dash/settings/ApiTokensContent.tsx +2 -1
  517. package/src/ui/dash/settings/ConfigEditorContent.tsx +35 -16
  518. package/src/ui/dash/settings/GeneralContent.tsx +277 -113
  519. package/src/ui/dash/settings/LanguageContent.tsx +409 -0
  520. package/src/ui/dash/settings/SettingsRootContent.tsx +19 -1
  521. package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +324 -4
  522. package/src/ui/dash/settings/__tests__/LanguageContent.test.tsx +144 -0
  523. package/src/ui/dash/settings/__tests__/discover-copy.test.ts +66 -0
  524. package/src/ui/dash/settings/discover-copy.ts +116 -0
  525. package/src/ui/feed/LinkCard.tsx +55 -45
  526. package/src/ui/feed/NoteCard.tsx +9 -4
  527. package/src/ui/feed/PostStatusBadges.tsx +90 -43
  528. package/src/ui/feed/QuoteCard.tsx +55 -30
  529. package/src/ui/feed/ThreadPreview.tsx +6 -2
  530. package/src/ui/feed/__tests__/thread-preview.test.ts +13 -16
  531. package/src/ui/feed/__tests__/timeline-cards.test.ts +314 -49
  532. package/src/ui/feed/thread-preview-state.ts +20 -6
  533. package/src/ui/font-themes.ts +39 -29
  534. package/src/ui/layouts/BaseLayout.tsx +155 -10
  535. package/src/ui/layouts/SiteLayout.tsx +192 -34
  536. package/src/ui/layouts/__tests__/BaseLayout.test.tsx +196 -17
  537. package/src/ui/layouts/__tests__/SiteLayout.test.tsx +109 -85
  538. package/src/ui/pages/ArchivePage.tsx +282 -289
  539. package/src/ui/pages/CollectionPage.tsx +86 -76
  540. package/src/ui/pages/CollectionsPage.tsx +5 -0
  541. package/src/ui/pages/ComposePage.tsx +8 -0
  542. package/src/ui/pages/PostPage.tsx +47 -0
  543. package/src/ui/pages/SearchPage.tsx +3 -3
  544. package/src/ui/pages/SmartCollectionPage.tsx +434 -0
  545. package/src/ui/pages/SubscribePage.tsx +236 -0
  546. package/src/ui/pages/__tests__/ArchivePage.test.tsx +65 -9
  547. package/src/ui/pages/__tests__/collections-directory-rows.test.tsx +92 -0
  548. package/src/ui/pages/__tests__/collections-view-links.test.tsx +81 -0
  549. package/src/ui/shared/CollectionDirectory.tsx +178 -3
  550. package/src/ui/shared/CollectionsManager.tsx +70 -17
  551. package/src/ui/shared/CopyField.tsx +127 -0
  552. package/src/ui/shared/EmptyPostContent.tsx +23 -0
  553. package/src/ui/shared/MediaGallery.tsx +125 -46
  554. package/src/ui/shared/Pagination.tsx +20 -5
  555. package/src/ui/shared/PostFooter.tsx +20 -11
  556. package/src/ui/shared/__tests__/archive-labels.test.ts +100 -0
  557. package/src/ui/shared/__tests__/media-gallery.test.ts +210 -2
  558. package/src/ui/shared/__tests__/navigation-labels.test.ts +16 -22
  559. package/src/ui/shared/__tests__/pagination.test.ts +38 -24
  560. package/src/ui/shared/__tests__/post-footer.test.ts +32 -0
  561. package/src/ui/shared/archive-labels.ts +98 -0
  562. package/src/ui/shared/collection-management-labels.ts +94 -16
  563. package/src/ui/shared/navigation-labels.ts +45 -71
  564. package/src/ui/shared/post-article-attributes.ts +2 -0
  565. package/src/ui/shared/smart-collection-labels.ts +343 -0
  566. package/dist/app-C75Zc8dD.js +0 -6
  567. package/dist/client/_assets/chunks/heic-to-DUUaO23q.js +0 -1
  568. package/dist/client/_assets/chunks/module-DcsAZQZ_.js +0 -716
  569. package/dist/client/_assets/chunks/url-CU9pEbk5.js +0 -1
  570. package/dist/client/_assets/client-Bq0vre8Y.js +0 -296
  571. package/dist/client/_assets/client-D3VZyG4L.css +0 -2
  572. package/dist/client/_assets/client-auth-F2rb9WfF.js +0 -5571
  573. package/dist/env-BPYViDJJ.js +0 -281
  574. package/dist/export-Dl5KCiEs.js +0 -5562
  575. package/dist/github-api-BNF35Lkx.js +0 -176
  576. package/dist/github-app-DdiD-bC4.js +0 -275
  577. package/dist/github-sync-Bqg62IvV.js +0 -4
  578. package/dist/github-sync-HdK2EgvJ.js +0 -431
  579. package/dist/url-CbLAtlZe.js +0 -900
  580. package/src/client/__tests__/collection-created-notice.test.ts +0 -36
  581. package/src/client/__tests__/collection-form-bridge.test.ts +0 -216
  582. package/src/client/__tests__/post-form-bridge.test.ts +0 -82
  583. package/src/client/collection-created-notice.ts +0 -66
  584. package/src/client/collection-form-bridge.ts +0 -191
  585. package/src/client/components/__tests__/jant-post-form.test.ts +0 -193
  586. package/src/client/components/jant-post-form.ts +0 -422
  587. package/src/client/components/post-form-template.ts +0 -352
  588. package/src/client/components/post-form-types.ts +0 -96
  589. package/src/client/media-metadata.ts +0 -247
  590. package/src/client/post-form-bridge.ts +0 -193
  591. package/src/client/tiptap/exitable-marks.ts +0 -73
  592. package/src/ui/pages/CollectionEditorPage.tsx +0 -175
@@ -0,0 +1,324 @@
1
+ /**
2
+ * Shared "find a post" dialog.
3
+ *
4
+ * Mounted once at the document body level and opened through `pickPost()`.
5
+ * Searching for one of your own posts is a task that keeps coming up — linking
6
+ * a translation today, and anything else that has to point at a post later —
7
+ * and a popover is the wrong shape for it: results need room for a real title,
8
+ * a line of context, and a keyboard.
9
+ *
10
+ * The dialog knows nothing about what it is picking *for*. The caller supplies
11
+ * the copy and a `search` function, so the eligibility rules stay wherever they
12
+ * belong instead of leaking in here.
13
+ */
14
+
15
+ import { LitElement, html, nothing } from "lit";
16
+ import { classMap } from "lit/directives/class-map.js";
17
+
18
+ /** One row in the picker. */
19
+ export interface PostPickerItem {
20
+ id: string;
21
+ /** What to call it — a title, or something derived when there is none. */
22
+ label: string;
23
+ /** Short context on the right, e.g. the post's language. */
24
+ meta?: string;
25
+ }
26
+
27
+ /**
28
+ * What one round of searching produced.
29
+ *
30
+ * The note carries an answer the list cannot: an author who pasted a URL is
31
+ * looking at a page they know exists, so "nothing matched" is the wrong thing
32
+ * to tell them — "that post is a draft" is the right thing.
33
+ */
34
+ export interface PostPickerResult {
35
+ items: PostPickerItem[];
36
+ /** Shown in place of the empty hint. */
37
+ note?: string;
38
+ }
39
+
40
+ export interface PostPickerOptions {
41
+ heading: string;
42
+ /** One line under the heading saying what picking will do. */
43
+ hint?: string;
44
+ placeholder: string;
45
+ /** Shown when a search comes back with nothing and has nothing to add. */
46
+ emptyHint: string;
47
+ /** Shortest query worth sending. Defaults to 2. */
48
+ minQueryLength?: number;
49
+ search(query: string): Promise<PostPickerResult>;
50
+ }
51
+
52
+ interface PickerRequest extends PostPickerOptions {
53
+ resolve: (value: string | null) => void;
54
+ }
55
+
56
+ const SEARCH_DEBOUNCE_MS = 200;
57
+
58
+ export class JantPostPicker extends LitElement {
59
+ static properties = {
60
+ _open: { state: true },
61
+ _query: { state: true },
62
+ _items: { state: true },
63
+ _note: { state: true },
64
+ _loading: { state: true },
65
+ _searched: { state: true },
66
+ };
67
+
68
+ declare _open: boolean;
69
+ declare _query: string;
70
+ declare _items: PostPickerItem[];
71
+ declare _note: string;
72
+ declare _loading: boolean;
73
+ declare _searched: boolean;
74
+
75
+ #current: PickerRequest | null = null;
76
+ #searchTimer: ReturnType<typeof setTimeout> | null = null;
77
+ /** Guards against a slow earlier search overwriting a newer one's results. */
78
+ #searchToken = 0;
79
+
80
+ createRenderRoot() {
81
+ this.innerHTML = "";
82
+ return this;
83
+ }
84
+
85
+ constructor() {
86
+ super();
87
+ this._open = false;
88
+ this._query = "";
89
+ this._items = [];
90
+ this._note = "";
91
+ this._loading = false;
92
+ this._searched = false;
93
+ }
94
+
95
+ disconnectedCallback() {
96
+ super.disconnectedCallback();
97
+ const pending = this.#current;
98
+ this.#current = null;
99
+ pending?.resolve(null);
100
+ }
101
+
102
+ /**
103
+ * Open the picker and resolve to the chosen post.
104
+ *
105
+ * @param options - Copy, and the search that produces the candidates
106
+ * @returns The picked post's ID, or null when dismissed
107
+ */
108
+ pick(options: PostPickerOptions): Promise<string | null> {
109
+ // One picker at a time: a second request means the first is stale.
110
+ this.#current?.resolve(null);
111
+
112
+ this._query = "";
113
+ this._items = [];
114
+ this._note = "";
115
+ this._loading = false;
116
+ this._searched = false;
117
+ this._open = true;
118
+
119
+ return new Promise<string | null>((resolve) => {
120
+ this.#current = { ...options, resolve };
121
+ void this.updateComplete.then(() => {
122
+ const dialog = this.querySelector<HTMLDialogElement>(".picker-dialog");
123
+ if (dialog && !dialog.open) dialog.showModal();
124
+ this.querySelector<HTMLInputElement>(".picker-dialog-input")?.focus();
125
+ });
126
+ });
127
+ }
128
+
129
+ #finish(value: string | null) {
130
+ if (this.#searchTimer) clearTimeout(this.#searchTimer);
131
+ this.#searchTimer = null;
132
+ this.#searchToken += 1;
133
+
134
+ const dialog = this.querySelector<HTMLDialogElement>(".picker-dialog");
135
+ if (dialog?.open) dialog.close();
136
+
137
+ const pending = this.#current;
138
+ this.#current = null;
139
+ this._open = false;
140
+ pending?.resolve(value);
141
+ }
142
+
143
+ #onInput(event: Event) {
144
+ const request = this.#current;
145
+ if (!request) return;
146
+
147
+ const query = (event.target as HTMLInputElement).value;
148
+ this._query = query;
149
+
150
+ if (this.#searchTimer) clearTimeout(this.#searchTimer);
151
+ const minLength = request.minQueryLength ?? 2;
152
+ if (query.trim().length < minLength) {
153
+ this.#searchToken += 1;
154
+ this._items = [];
155
+ this._note = "";
156
+ this._loading = false;
157
+ this._searched = false;
158
+ return;
159
+ }
160
+
161
+ this._loading = true;
162
+ // Debounced: the author is typing a phrase, not one query per keystroke.
163
+ this.#searchTimer = setTimeout(() => {
164
+ void this.#runSearch(query, request);
165
+ }, SEARCH_DEBOUNCE_MS);
166
+ }
167
+
168
+ async #runSearch(query: string, request: PickerRequest) {
169
+ const token = ++this.#searchToken;
170
+ try {
171
+ const result = await request.search(query);
172
+ if (token !== this.#searchToken) return;
173
+ this._items = result.items;
174
+ this._note = result.note ?? "";
175
+ } catch {
176
+ if (token !== this.#searchToken) return;
177
+ this._items = [];
178
+ this._note = "";
179
+ } finally {
180
+ if (token === this.#searchToken) {
181
+ this._loading = false;
182
+ this._searched = true;
183
+ }
184
+ }
185
+ }
186
+
187
+ /** Arrow keys walk the results; Escape backs out. */
188
+ #onKeydown(event: KeyboardEvent) {
189
+ if (event.key === "Escape") {
190
+ event.preventDefault();
191
+ this.#finish(null);
192
+ return;
193
+ }
194
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
195
+
196
+ const options = Array.from(
197
+ this.querySelectorAll<HTMLButtonElement>(".picker-dialog-option"),
198
+ );
199
+ if (options.length === 0) return;
200
+ event.preventDefault();
201
+
202
+ const active = document.activeElement;
203
+ const index = options.findIndex((option) => option === active);
204
+ const next =
205
+ event.key === "ArrowDown"
206
+ ? index < 0
207
+ ? 0
208
+ : Math.min(index + 1, options.length - 1)
209
+ : index <= 0
210
+ ? -1
211
+ : index - 1;
212
+
213
+ if (next < 0) {
214
+ this.querySelector<HTMLInputElement>(".picker-dialog-input")?.focus();
215
+ return;
216
+ }
217
+ options[next]?.focus();
218
+ }
219
+
220
+ #onBackdropClick(event: MouseEvent) {
221
+ if (event.target === event.currentTarget) this.#finish(null);
222
+ }
223
+
224
+ render() {
225
+ const request = this.#current;
226
+ if (!this._open || !request) return nothing;
227
+
228
+ const minLength = request.minQueryLength ?? 2;
229
+ const tooShort = this._query.trim().length < minLength;
230
+
231
+ return html`
232
+ <dialog
233
+ class="dialog picker-dialog"
234
+ @cancel=${(e: Event) => {
235
+ e.preventDefault();
236
+ this.#finish(null);
237
+ }}
238
+ @click=${this.#onBackdropClick}
239
+ @keydown=${this.#onKeydown}
240
+ >
241
+ <div
242
+ class="picker-dialog-panel"
243
+ role="document"
244
+ aria-labelledby="picker-dialog-title"
245
+ >
246
+ <header class="picker-dialog-header">
247
+ <h2 id="picker-dialog-title" class="picker-dialog-title">
248
+ ${request.heading}
249
+ </h2>
250
+ ${
251
+ request.hint
252
+ ? html`<p class="picker-dialog-hint">${request.hint}</p>`
253
+ : nothing
254
+ }
255
+ </header>
256
+
257
+ <input
258
+ type="search"
259
+ class="input picker-dialog-input"
260
+ placeholder=${request.placeholder}
261
+ aria-label=${request.heading}
262
+ autocomplete="off"
263
+ .value=${this._query}
264
+ @input=${this.#onInput}
265
+ />
266
+
267
+ <div class="picker-dialog-results" role="listbox">
268
+ ${this._items.map(
269
+ (item) => html`
270
+ <button
271
+ type="button"
272
+ role="option"
273
+ aria-selected="false"
274
+ class="picker-dialog-option"
275
+ @click=${() => this.#finish(item.id)}
276
+ >
277
+ <span class="picker-dialog-option-label">${item.label}</span>
278
+ ${
279
+ item.meta
280
+ ? html`<span class="picker-dialog-option-meta"
281
+ >${item.meta}</span
282
+ >`
283
+ : nothing
284
+ }
285
+ </button>
286
+ `,
287
+ )}
288
+ ${
289
+ this._loading
290
+ ? html`<p class="picker-dialog-status">Searching…</p>`
291
+ : nothing
292
+ }
293
+ ${
294
+ !this._loading && this._searched && this._items.length === 0
295
+ ? html`<p class="picker-dialog-status">
296
+ ${this._note || request.emptyHint}
297
+ </p>`
298
+ : nothing
299
+ }
300
+ </div>
301
+
302
+ <footer
303
+ class=${classMap({
304
+ "picker-dialog-actions": true,
305
+ "picker-dialog-actions-quiet": tooShort,
306
+ })}
307
+ >
308
+ <button
309
+ type="button"
310
+ class="btn-outline"
311
+ @click=${() => this.#finish(null)}
312
+ >
313
+ Cancel
314
+ </button>
315
+ </footer>
316
+ </div>
317
+ </dialog>
318
+ `;
319
+ }
320
+ }
321
+
322
+ if (!customElements.get("jant-post-picker")) {
323
+ customElements.define("jant-post-picker", JantPostPicker);
324
+ }
@@ -1,6 +1,11 @@
1
1
  /**
2
2
  * Types shared between the server-side template and the
3
3
  * `jant-repo-picker` Lit component.
4
+ *
5
+ * Labels arrive translated and already formatted. A `{name}` left in one is
6
+ * deliberate: the value only exists in the browser, so the server keeps the
7
+ * token literal (see `keepForClient` in the settings route) and the component
8
+ * replaces it. Anything the server already knows is interpolated there.
4
9
  */
5
10
 
6
11
  export interface RepoPickerLabels {
@@ -20,7 +25,7 @@ export interface RepoPickerLabels {
20
25
  repoSearchHint: string;
21
26
  refreshRepos: string; // aria-label + tooltip for the refresh button
22
27
  createOnGitHub: string; // primary call-to-action text
23
- createOnGitHubHint: string; // "We'll prefill the name {name} for you"
28
+ createOnGitHubHint: string; // repo name already interpolated
24
29
  classifyLoading: string;
25
30
  classificationEmpty: string;
26
31
  classificationOwned: string;
@@ -490,11 +490,13 @@ export class JantRepoPicker extends LitElement {
490
490
 
491
491
  ${this.#renderOwner()} ${this.#renderRepo()}
492
492
  ${this.#renderClassification()} ${this.#renderActions()}
493
- ${this._error
494
- ? html`<div class="alert alert-destructive text-sm">
495
- ${this._error}
496
- </div>`
497
- : nothing}
493
+ ${
494
+ this._error
495
+ ? html`<div class="alert-destructive text-sm" role="alert">
496
+ <section><p>${this._error}</p></section>
497
+ </div>`
498
+ : nothing
499
+ }
498
500
  </div>
499
501
  `;
500
502
  }
@@ -512,23 +514,29 @@ export class JantRepoPicker extends LitElement {
512
514
  aria-expanded=${this._ownerOpen}
513
515
  >
514
516
  <span class="flex items-center gap-2 truncate">
515
- ${selected
516
- ? html`
517
- ${selected.account.avatarUrl
518
- ? html`<img
519
- src=${selected.account.avatarUrl}
520
- alt=""
521
- class="w-5 h-5 rounded-full"
522
- loading="lazy"
523
- />`
524
- : nothing}
525
- <span class="truncate">${selected.account.login}</span>
526
- `
527
- : html`<span class="text-muted-foreground"
528
- >${this._installations.length === 0
529
- ? this.labels.ownerEmpty
530
- : this.labels.ownerPlaceholder}</span
531
- >`}
517
+ ${
518
+ selected
519
+ ? html`
520
+ ${
521
+ selected.account.avatarUrl
522
+ ? html`<img
523
+ src=${selected.account.avatarUrl}
524
+ alt=""
525
+ class="w-5 h-5 rounded-full"
526
+ loading="lazy"
527
+ />`
528
+ : nothing
529
+ }
530
+ <span class="truncate">${selected.account.login}</span>
531
+ `
532
+ : html`<span class="text-muted-foreground"
533
+ >${
534
+ this._installations.length === 0
535
+ ? this.labels.ownerEmpty
536
+ : this.labels.ownerPlaceholder
537
+ }</span
538
+ >`
539
+ }
532
540
  </span>
533
541
  <span class="text-muted-foreground">▾</span>
534
542
  </button>
@@ -549,20 +557,23 @@ export class JantRepoPicker extends LitElement {
549
557
  <li>
550
558
  <button
551
559
  type="button"
552
- class="flex w-full items-center gap-2 px-3 py-2 text-left text-sm hover:bg-muted"
560
+ class="flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left text-sm hover:bg-muted"
553
561
  @click=${() => this.#selectOwner(inst)}
554
562
  role="option"
555
- aria-selected=${this._selectedOwner?.installationId ===
556
- inst.installationId}
563
+ aria-selected=${
564
+ this._selectedOwner?.installationId === inst.installationId
565
+ }
557
566
  >
558
- ${inst.account.avatarUrl
559
- ? html`<img
560
- src=${inst.account.avatarUrl}
561
- alt=""
562
- class="w-5 h-5 rounded-full"
563
- loading="lazy"
564
- />`
565
- : nothing}
567
+ ${
568
+ inst.account.avatarUrl
569
+ ? html`<img
570
+ src=${inst.account.avatarUrl}
571
+ alt=""
572
+ class="w-5 h-5 rounded-full"
573
+ loading="lazy"
574
+ />`
575
+ : nothing
576
+ }
566
577
  <span class="truncate">${inst.account.login}</span>
567
578
  <span class="ml-auto text-xs text-muted-foreground">
568
579
  ${inst.account.type === "Organization" ? "Org" : ""}
@@ -571,18 +582,20 @@ export class JantRepoPicker extends LitElement {
571
582
  </li>
572
583
  `,
573
584
  )}
574
- ${this.installUrl
575
- ? html`
576
- <li class="border-t mt-1 pt-1">
577
- <a
578
- href=${this.installUrl}
579
- class="block px-3 py-2 text-sm text-primary hover:bg-muted"
580
- >
581
- ${this.labels.installAnother}
582
- </a>
583
- </li>
584
- `
585
- : nothing}
585
+ ${
586
+ this.installUrl
587
+ ? html`
588
+ <li class="border-t mt-1 pt-1">
589
+ <a
590
+ href=${this.installUrl}
591
+ class="block px-3 py-2 text-sm text-primary hover:bg-muted"
592
+ >
593
+ ${this.labels.installAnother}
594
+ </a>
595
+ </li>
596
+ `
597
+ : nothing
598
+ }
586
599
  </ul>
587
600
  </div>
588
601
  `;
@@ -603,17 +616,23 @@ export class JantRepoPicker extends LitElement {
603
616
  ?disabled=${disabled}
604
617
  >
605
618
  <span class="truncate">
606
- ${selected
607
- ? html`<span>${selected.name}</span> ${selected.private
608
- ? html`<span class="text-xs text-muted-foreground ml-1"
609
- >${this.labels.privateBadge}</span
610
- >`
611
- : nothing}`
612
- : html`<span class="text-muted-foreground"
613
- >${disabled
614
- ? this.labels.repoPlaceholderNoOwner
615
- : this.labels.repoPlaceholder}</span
616
- >`}
619
+ ${
620
+ selected
621
+ ? html`<span>${selected.name}</span> ${
622
+ selected.private
623
+ ? html`<span class="text-xs text-muted-foreground ml-1"
624
+ >${this.labels.privateBadge}</span
625
+ >`
626
+ : nothing
627
+ }`
628
+ : html`<span class="text-muted-foreground"
629
+ >${
630
+ disabled
631
+ ? this.labels.repoPlaceholderNoOwner
632
+ : this.labels.repoPlaceholder
633
+ }</span
634
+ >`
635
+ }
617
636
  </span>
618
637
  <span class="text-muted-foreground">▾</span>
619
638
  </button>
@@ -638,7 +657,7 @@ export class JantRepoPicker extends LitElement {
638
657
  />
639
658
  <button
640
659
  type="button"
641
- class="btn-ghost btn-icon"
660
+ class="btn-icon-ghost"
642
661
  title=${this.labels.refreshRepos}
643
662
  aria-label=${this.labels.refreshRepos}
644
663
  @click=${() => this.#refreshRepos()}
@@ -648,62 +667,68 @@ export class JantRepoPicker extends LitElement {
648
667
  <span aria-hidden="true">⟳</span>
649
668
  </button>
650
669
  </div>
651
- ${this._hasMore && this._reposMode === "list"
652
- ? html`<p class="px-3 pt-2 text-xs text-muted-foreground">
653
- ${this.labels.repoShowingOf
654
- .replace("{shown}", String(this._repos.length))
655
- .replace("{total}", String(this._totalCount))}
656
- — ${this.labels.repoSearchHint}
657
- </p>`
658
- : nothing}
670
+ ${
671
+ this._hasMore && this._reposMode === "list"
672
+ ? html`<p class="px-3 pt-2 text-xs text-muted-foreground">
673
+ ${this.labels.repoShowingOf
674
+ .replace("{shown}", String(this._repos.length))
675
+ .replace("{total}", String(this._totalCount))}
676
+ — ${this.labels.repoSearchHint}
677
+ </p>`
678
+ : nothing
679
+ }
659
680
  <ul class="max-h-64 overflow-y-auto py-1" role="listbox">
660
- ${this._loadingRepos
661
- ? html`<li class="px-3 py-2 text-sm text-muted-foreground">
662
- ${this.labels.repoLoading}
663
- </li>`
664
- : items.length === 0
681
+ ${
682
+ this._loadingRepos
665
683
  ? html`<li class="px-3 py-2 text-sm text-muted-foreground">
666
- ${this.labels.repoEmpty}
684
+ ${this.labels.repoLoading}
667
685
  </li>`
668
- : items.map(
669
- (r) => html`
670
- <li>
671
- <button
672
- type="button"
673
- class="flex w-full items-center gap-2 px-3 py-2 text-left text-sm hover:bg-muted"
674
- @click=${() => this.#selectRepo(r)}
675
- role="option"
676
- aria-selected=${this._selectedRepo?.fullName ===
677
- r.fullName}
678
- >
679
- <span class="truncate">${r.name}</span>
680
- ${r.private
681
- ? html`<span
682
- class="ml-auto text-xs text-muted-foreground"
683
- >${this.labels.privateBadge}</span
684
- >`
685
- : nothing}
686
- </button>
687
- </li>
688
- `,
689
- )}
686
+ : items.length === 0
687
+ ? html`<li class="px-3 py-2 text-sm text-muted-foreground">
688
+ ${this.labels.repoEmpty}
689
+ </li>`
690
+ : items.map(
691
+ (r) => html`
692
+ <li>
693
+ <button
694
+ type="button"
695
+ class="flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left text-sm hover:bg-muted"
696
+ @click=${() => this.#selectRepo(r)}
697
+ role="option"
698
+ aria-selected=${
699
+ this._selectedRepo?.fullName === r.fullName
700
+ }
701
+ >
702
+ <span class="truncate">${r.name}</span>
703
+ ${
704
+ r.private
705
+ ? html`<span
706
+ class="ml-auto text-xs text-muted-foreground"
707
+ >${this.labels.privateBadge}</span
708
+ >`
709
+ : nothing
710
+ }
711
+ </button>
712
+ </li>
713
+ `,
714
+ )
715
+ }
690
716
  </ul>
691
717
  <div class="border-t p-2">
692
718
  <button
693
719
  type="button"
694
- class="flex w-full items-start gap-2 px-2 py-2 text-left text-sm text-primary hover:bg-muted rounded-md"
720
+ class="flex w-full cursor-pointer items-start gap-2 rounded-md px-2 py-2 text-left text-sm text-primary hover:bg-muted"
695
721
  @click=${() => this.#openCreateOnGitHub()}
696
722
  >
697
723
  <span class="flex flex-col">
698
724
  <span class="font-medium">${this.labels.createOnGitHub} →</span>
699
- ${this.createRepoNameHint
700
- ? html`<span class="text-xs text-muted-foreground">
701
- ${this.labels.createOnGitHubHint.replace(
702
- "{name}",
703
- this.createRepoNameHint,
704
- )}
705
- </span>`
706
- : nothing}
725
+ ${
726
+ this.createRepoNameHint
727
+ ? html`<span class="text-xs text-muted-foreground">
728
+ ${this.labels.createOnGitHubHint}
729
+ </span>`
730
+ : nothing
731
+ }
707
732
  </span>
708
733
  </button>
709
734
  </div>
@@ -730,17 +755,21 @@ export class JantRepoPicker extends LitElement {
730
755
  </p>`;
731
756
  }
732
757
  if (c.kind === "owned-by-other-site") {
733
- return html`<div class="alert alert-destructive text-sm">
734
- ${this.labels.classificationOwnedByOther.replace(
735
- "{host}",
736
- c.marker.site_host,
737
- )}
758
+ return html`<div class="alert-destructive text-sm" role="alert">
759
+ <section>
760
+ <p>
761
+ ${this.labels.classificationOwnedByOther.replace(
762
+ "{host}",
763
+ c.marker.site_host,
764
+ )}
765
+ </p>
766
+ </section>
738
767
  </div>`;
739
768
  }
740
769
  // foreign — show confirm input
741
770
  const full = this._selectedRepo.fullName;
742
771
  return html`
743
- <div class="alert alert-warning flex flex-col gap-3 text-sm">
772
+ <div class="alert flex flex-col gap-3 text-sm">
744
773
  <div>
745
774
  <strong class="block mb-1">${this.labels.confirmHeading}</strong>
746
775
  <span>${this.labels.confirmBody.replace("{repo}", full)}</span>