@jant/core 0.6.15 → 0.7.0

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 (537) hide show
  1. package/README.md +11 -11
  2. package/bin/commands/assets/upload.js +192 -15
  3. package/bin/commands/import-site.js +67 -4
  4. package/bin/commands/reset-password.js +21 -8
  5. package/bin/commands/site/snapshot/export.js +1 -1
  6. package/bin/commands/site/snapshot/import.js +1 -1
  7. package/bin/lib/d1-query.js +5 -16
  8. package/bin/lib/node-env.js +40 -4
  9. package/bin/lib/site-selection.js +85 -29
  10. package/bin/lib/wrangler-cli.js +28 -2
  11. package/bin/lib/wrangler-json.js +35 -0
  12. package/dist/{app-C-oi_t8W.js → app-K_Aa1MMn.js} +52623 -13193
  13. package/dist/client/.vite/manifest.json +255 -90
  14. package/dist/client/_assets/chunks/confirm-D7GrwsCN.js +45 -0
  15. package/dist/client/_assets/chunks/create-editor-CD3FhrOB.js +257 -0
  16. package/dist/client/_assets/chunks/{native-DpcrFAPh.js → emoji-mart-C4RpRKcU.js} +716 -1
  17. package/dist/client/_assets/chunks/footnote-rail-CmeoVtnU.js +1 -0
  18. package/dist/client/_assets/chunks/heic-to-CuP2Qlsw.js +1 -0
  19. package/dist/client/_assets/chunks/jant-locale-picker-DL6mIOT5.js +69 -0
  20. package/dist/client/_assets/chunks/lit-DW8VmJAT.js +2 -0
  21. package/dist/client/_assets/chunks/mediabunny-DRaCQ1r-.js +31 -0
  22. package/dist/client/_assets/chunks/rolldown-runtime-hePW80VL.js +1 -0
  23. package/dist/client/_assets/chunks/settings-paths-BcdySNx6.js +84 -0
  24. package/dist/client/_assets/chunks/site-header-nav-DzXsFk05.js +1 -0
  25. package/dist/client/_assets/chunks/slug-format-CRXO1AL5.js +1 -0
  26. package/dist/client/_assets/chunks/slugify-373citbs.js +1 -0
  27. package/dist/client/_assets/chunks/sortable-list-CgaL2jCs.js +2 -0
  28. package/dist/client/_assets/chunks/types-D5iF3H1a.js +1 -0
  29. package/dist/client/_assets/chunks/unsafe-html-B5ZEXaxg.js +1 -0
  30. package/dist/client/_assets/chunks/unsafe-svg-0QCkP0vZ.js +2 -0
  31. package/dist/client/_assets/client-GiYENVw8.css +2 -0
  32. package/dist/client/_assets/client-VnFxJN7G.js +295 -0
  33. package/dist/client/_assets/client-auth-k8gJ6QJj.js +828 -0
  34. package/dist/client/_assets/client-author-DQ8R-8KR.css +2 -0
  35. package/dist/client/_assets/client-compose-B_kDtWkW.js +2004 -0
  36. package/dist/client/_assets/client-manage-M90aSTOk.js +2170 -0
  37. package/dist/client/_assets/client-settings-DfYs9n1F.js +1112 -0
  38. package/dist/{export-CsFx6F_M.js → github-sync-BPAvT999.js} +2343 -110
  39. package/dist/index.js +2 -5
  40. package/dist/node.js +5 -7
  41. package/package.json +3 -3
  42. package/src/__tests__/auth-session-freshness.test.ts +57 -0
  43. package/src/__tests__/basecoat-variants.test.ts +121 -0
  44. package/src/__tests__/bin/assets-upload.test.ts +68 -0
  45. package/src/__tests__/export-hugo-build.test.ts +260 -0
  46. package/src/__tests__/export-service.test.ts +231 -0
  47. package/src/__tests__/helpers/component-css.ts +91 -0
  48. package/src/__tests__/helpers/db.ts +33 -0
  49. package/src/__tests__/helpers/export-fixtures.ts +3 -0
  50. package/src/__tests__/mise-config.test.ts +22 -0
  51. package/src/__tests__/stylesheet-audience.test.ts +159 -0
  52. package/src/__tests__/stylesheet-breakpoints.test.ts +183 -0
  53. package/src/app.tsx +71 -16
  54. package/src/auth.ts +18 -1
  55. package/src/client/__tests__/collection-page-actions.test.ts +51 -2
  56. package/src/client/__tests__/compose-bridge-image-upload.test.ts +196 -0
  57. package/src/client/__tests__/compose-bridge-video-poster.test.ts +159 -0
  58. package/src/client/__tests__/compose-bridge.test.ts +524 -2
  59. package/src/client/__tests__/compose-launch.test.ts +62 -1
  60. package/src/client/__tests__/compose-shortcuts.test.ts +57 -8
  61. package/src/client/__tests__/compose-triggers.test.ts +90 -0
  62. package/src/client/__tests__/copy-field.test.ts +162 -0
  63. package/src/client/__tests__/custom-url-menu.test.ts +127 -6
  64. package/src/client/__tests__/image-processor.test.ts +324 -2
  65. package/src/client/__tests__/lazy-entries.test.ts +92 -0
  66. package/src/client/__tests__/media-scroll-hint.test.ts +155 -0
  67. package/src/client/__tests__/runtime-paths.test.ts +84 -0
  68. package/src/client/__tests__/smart-collection-page-actions.test.ts +158 -0
  69. package/src/client/__tests__/toast.test.ts +88 -1
  70. package/src/client/__tests__/video-processor-probe.test.ts +175 -0
  71. package/src/client/__tests__/video-processor.test.ts +181 -0
  72. package/src/client/audio-processor.ts +24 -15
  73. package/src/client/audio-waveform.ts +49 -0
  74. package/src/client/collection-dialog-host.ts +72 -0
  75. package/src/client/collection-navigation.ts +51 -16
  76. package/src/client/collection-page-actions.ts +26 -0
  77. package/src/client/components/__tests__/compose-format-convert.test.ts +0 -19
  78. package/src/client/components/__tests__/jant-collection-dialog.test.ts +329 -0
  79. package/src/client/components/__tests__/jant-collection-directory.test.ts +274 -10
  80. package/src/client/components/__tests__/jant-collection-form.test.ts +59 -178
  81. package/src/client/components/__tests__/jant-command-palette.test.ts +79 -0
  82. package/src/client/components/__tests__/jant-compose-dialog-draft-storage.test.ts +340 -0
  83. package/src/client/components/__tests__/jant-compose-dialog.test.ts +3283 -389
  84. package/src/client/components/__tests__/jant-compose-editor.test.ts +581 -86
  85. package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +34 -89
  86. package/src/client/components/__tests__/jant-locale-picker.test.ts +207 -0
  87. package/src/client/components/__tests__/jant-nav-manager.test.ts +234 -15
  88. package/src/client/components/__tests__/jant-post-menu.test.ts +413 -2
  89. package/src/client/components/__tests__/jant-post-picker.test.ts +197 -0
  90. package/src/client/components/__tests__/jant-settings-avatar.test.ts +15 -12
  91. package/src/client/components/__tests__/jant-settings-general.test.ts +541 -172
  92. package/src/client/components/__tests__/jant-settings-language.test.ts +851 -0
  93. package/src/client/components/__tests__/jant-smart-collection-dialog.test.ts +665 -0
  94. package/src/client/components/collection-dialog-types.ts +49 -0
  95. package/src/client/components/collection-manager-types.ts +32 -1
  96. package/src/client/components/collection-types.ts +1 -15
  97. package/src/client/components/compose-format-convert.ts +5 -1
  98. package/src/client/components/compose-types.ts +121 -3
  99. package/src/client/components/jant-collection-dialog.ts +722 -0
  100. package/src/client/components/jant-collection-directory.ts +567 -182
  101. package/src/client/components/jant-collection-form.ts +53 -232
  102. package/src/client/components/jant-command-palette.ts +106 -49
  103. package/src/client/components/jant-compose-dialog.ts +2757 -1194
  104. package/src/client/components/jant-compose-editor.ts +866 -397
  105. package/src/client/components/jant-compose-fullscreen.ts +53 -47
  106. package/src/client/components/jant-config-editor.ts +65 -53
  107. package/src/client/components/jant-confirm-dialog.ts +13 -11
  108. package/src/client/components/jant-locale-picker.ts +418 -0
  109. package/src/client/components/jant-media-lightbox.ts +146 -136
  110. package/src/client/components/jant-nav-manager.ts +690 -366
  111. package/src/client/components/jant-post-menu.ts +1142 -268
  112. package/src/client/components/jant-post-picker.ts +324 -0
  113. package/src/client/components/jant-repo-picker.ts +138 -106
  114. package/src/client/components/jant-settings-avatar.ts +31 -27
  115. package/src/client/components/jant-settings-general.ts +617 -543
  116. package/src/client/components/jant-settings-language.ts +1087 -0
  117. package/src/client/components/jant-smart-collection-dialog.ts +1021 -0
  118. package/src/client/components/jant-text-preview.ts +94 -86
  119. package/src/client/components/nav-manager-types.ts +62 -1
  120. package/src/client/components/settings-types.ts +14 -29
  121. package/src/client/components/smart-collection-conditions.ts +285 -0
  122. package/src/client/components/smart-collection-dialog-types.ts +68 -0
  123. package/src/client/compose-bridge.ts +336 -242
  124. package/src/client/compose-launch.ts +89 -2
  125. package/src/client/compose-shortcuts.ts +9 -7
  126. package/src/client/compose-triggers.ts +50 -0
  127. package/src/client/copy-field.ts +108 -0
  128. package/src/client/custom-url-menu.ts +60 -4
  129. package/src/client/icons.ts +14 -0
  130. package/src/client/image-plan.ts +165 -0
  131. package/src/client/image-processor.ts +195 -117
  132. package/src/client/image-worker-client.ts +45 -0
  133. package/src/client/image-worker.ts +135 -0
  134. package/src/client/lazy-entries.ts +95 -0
  135. package/src/client/lazy-slugify.ts +2 -2
  136. package/src/client/media-scroll-hint.ts +43 -2
  137. package/src/client/mediabunny.ts +29 -0
  138. package/src/client/post-picker.ts +41 -0
  139. package/src/client/post-refresh.ts +135 -0
  140. package/src/client/runtime-paths.ts +74 -1
  141. package/src/client/settings-bridge.ts +4 -10
  142. package/src/client/site-header-nav.js +18 -6
  143. package/src/client/smart-collection-dialog-host.ts +116 -0
  144. package/src/client/smart-collection-page-actions.ts +208 -0
  145. package/src/client/tiptap/__tests__/bubble-menu.test.ts +110 -0
  146. package/src/client/tiptap/__tests__/floating-position.test.ts +136 -1
  147. package/src/client/tiptap/__tests__/inline-image-upload.test.ts +37 -0
  148. package/src/client/tiptap/__tests__/link-input-rules.test.ts +121 -0
  149. package/src/client/tiptap/bubble-menu.ts +47 -16
  150. package/src/client/tiptap/floating-position.ts +216 -2
  151. package/src/client/tiptap/inline-image-upload.ts +7 -3
  152. package/src/client/tiptap/link-input-rules.ts +4 -4
  153. package/src/client/tiptap/link-toolbar.ts +22 -9
  154. package/src/client/toast.ts +143 -38
  155. package/src/client/types/vite-worker.d.ts +12 -0
  156. package/src/client/upload-with-metadata.ts +3 -13
  157. package/src/client/video-processor.ts +351 -107
  158. package/src/client-auth.ts +14 -31
  159. package/src/client-compose.ts +19 -0
  160. package/src/client-manage.ts +13 -0
  161. package/src/client-settings.ts +14 -0
  162. package/src/client-site.ts +10 -3
  163. package/src/client.ts +4 -0
  164. package/src/db/__tests__/backfill-nav-label-mirrors.test.ts +149 -0
  165. package/src/db/__tests__/backfill-thread-activity.test.ts +234 -0
  166. package/src/db/__tests__/schema-enum-sources.test.ts +33 -0
  167. package/src/db/__tests__/smart-collection-placement.test.ts +402 -0
  168. package/src/db/__tests__/thread-activity.test.ts +113 -0
  169. package/src/db/backfills/0005_split_thread_activity_from_quiet_replies.sql +68 -0
  170. package/src/db/backfills/0006_clear_mirrored_page_nav_labels.sql +31 -0
  171. package/src/db/migrations/0031_many_ego.sql +3 -0
  172. package/src/db/migrations/0032_married_tomas.sql +3 -0
  173. package/src/db/migrations/0033_busy_johnny_storm.sql +198 -0
  174. package/src/db/migrations/0034_cheerful_rumiko_fujikawa.sql +35 -0
  175. package/src/db/migrations/meta/0031_snapshot.json +2562 -0
  176. package/src/db/migrations/meta/0032_snapshot.json +2582 -0
  177. package/src/db/migrations/meta/0033_snapshot.json +2816 -0
  178. package/src/db/migrations/meta/0034_snapshot.json +2816 -0
  179. package/src/db/migrations/meta/_journal.json +28 -0
  180. package/src/db/migrations/pg/0029_rare_hairball.sql +3 -0
  181. package/src/db/migrations/pg/0030_abandoned_nighthawk.sql +3 -0
  182. package/src/db/migrations/pg/0031_strong_ultimatum.sql +128 -0
  183. package/src/db/migrations/pg/0032_furry_multiple_man.sql +6 -0
  184. package/src/db/migrations/pg/meta/0029_snapshot.json +3289 -0
  185. package/src/db/migrations/pg/meta/0030_snapshot.json +3329 -0
  186. package/src/db/migrations/pg/meta/0031_snapshot.json +3660 -0
  187. package/src/db/migrations/pg/meta/0032_snapshot.json +3660 -0
  188. package/src/db/migrations/pg/meta/_journal.json +28 -0
  189. package/src/db/pg/schema.ts +213 -27
  190. package/src/db/post-visibility.ts +117 -0
  191. package/src/db/schema.ts +242 -27
  192. package/src/db/thread-activity.ts +101 -0
  193. package/src/i18n/__tests__/detect.test.ts +26 -81
  194. package/src/i18n/__tests__/supported-locales.test.ts +83 -0
  195. package/src/i18n/context.tsx +12 -2
  196. package/src/i18n/detect.ts +7 -66
  197. package/src/i18n/locales/glossary.zh-Hans.yml +44 -4
  198. package/src/i18n/locales/glossary.zh-Hant.yml +44 -4
  199. package/src/i18n/locales/public/en.po +568 -168
  200. package/src/i18n/locales/public/en.ts +1 -1
  201. package/src/i18n/locales/public/zh-Hans.po +562 -162
  202. package/src/i18n/locales/public/zh-Hans.ts +1 -1
  203. package/src/i18n/locales/public/zh-Hant.po +562 -162
  204. package/src/i18n/locales/public/zh-Hant.ts +1 -1
  205. package/src/i18n/locales/settings/en.po +551 -69
  206. package/src/i18n/locales/settings/en.ts +1 -1
  207. package/src/i18n/locales/settings/zh-Hans.po +597 -115
  208. package/src/i18n/locales/settings/zh-Hans.ts +1 -1
  209. package/src/i18n/locales/settings/zh-Hant.po +610 -128
  210. package/src/i18n/locales/settings/zh-Hant.ts +1 -1
  211. package/src/i18n/locales.ts +94 -0
  212. package/src/i18n/middleware.ts +12 -2
  213. package/src/i18n/supported-locales.ts +80 -0
  214. package/src/lib/__tests__/api-settings.test.ts +11 -2
  215. package/src/lib/__tests__/asset-path.test.ts +92 -0
  216. package/src/lib/__tests__/deferred.test.ts +90 -0
  217. package/src/lib/__tests__/discover-ping.test.ts +89 -0
  218. package/src/lib/__tests__/discover.test.ts +410 -0
  219. package/src/lib/__tests__/feed-policy.test.ts +26 -0
  220. package/src/lib/__tests__/feed.test.ts +1691 -12
  221. package/src/lib/__tests__/filter-dimensions.test.ts +341 -0
  222. package/src/lib/__tests__/hosted-signin.test.ts +1 -1
  223. package/src/lib/__tests__/http-cache.test.ts +162 -0
  224. package/src/lib/__tests__/jant-docs-dev.test.ts +21 -0
  225. package/src/lib/__tests__/jant-docs.test.ts +23 -0
  226. package/src/lib/__tests__/lang-detect.test.ts +231 -0
  227. package/src/lib/__tests__/markdown.test.ts +16 -0
  228. package/src/lib/__tests__/media-helpers.test.ts +81 -0
  229. package/src/lib/__tests__/navigation.test.ts +135 -1
  230. package/src/lib/__tests__/resolve-config.test.ts +64 -3
  231. package/src/lib/__tests__/schemas.test.ts +42 -4
  232. package/src/lib/__tests__/site-unavailable-page.test.ts +25 -0
  233. package/src/lib/__tests__/slug.test.ts +88 -0
  234. package/src/lib/__tests__/slugify.test.ts +85 -0
  235. package/src/lib/__tests__/storage.test.ts +32 -0
  236. package/src/lib/__tests__/thread-fold.test.ts +113 -0
  237. package/src/lib/__tests__/translit.test.ts +87 -0
  238. package/src/lib/__tests__/upload-sideload.test.ts +14 -0
  239. package/src/lib/__tests__/url-fetch.test.ts +1 -2
  240. package/src/lib/__tests__/url.test.ts +93 -34
  241. package/src/lib/__tests__/view-language.test.ts +413 -0
  242. package/src/lib/__tests__/worker-response-cache.test.ts +90 -0
  243. package/src/lib/address.ts +57 -0
  244. package/src/lib/api-posts.ts +12 -0
  245. package/src/lib/asset-path.ts +98 -1
  246. package/src/lib/build-env.ts +16 -0
  247. package/src/lib/collection-groups.ts +5 -1
  248. package/src/lib/collection-paths.ts +3 -10
  249. package/src/lib/constants.ts +28 -3
  250. package/src/lib/copy-field.ts +36 -0
  251. package/src/lib/crypto.ts +40 -0
  252. package/src/lib/csp-builder.ts +2 -1
  253. package/src/lib/deferred.ts +79 -0
  254. package/src/lib/discover-ping.ts +141 -0
  255. package/src/lib/discover.ts +437 -0
  256. package/src/lib/env.ts +134 -0
  257. package/src/lib/errors.ts +33 -0
  258. package/src/lib/feed-policy.ts +236 -2
  259. package/src/lib/feed.ts +1021 -66
  260. package/src/lib/filter-dimensions.ts +1349 -0
  261. package/src/lib/github-sync-site-config.ts +3 -0
  262. package/src/lib/hosted-signin.ts +1 -1
  263. package/src/lib/http-cache.ts +148 -0
  264. package/src/lib/hugo-markdown.ts +21 -0
  265. package/src/lib/icons.ts +9 -2
  266. package/src/lib/ids.ts +3 -0
  267. package/src/lib/jant-docs.ts +42 -0
  268. package/src/lib/lang-detect.ts +308 -0
  269. package/src/lib/link-preview.ts +25 -0
  270. package/src/lib/markdown-manager.ts +6 -1
  271. package/src/lib/media-helpers.ts +13 -1
  272. package/src/lib/navigation.ts +78 -11
  273. package/src/lib/pagination.ts +28 -24
  274. package/src/lib/per-language-surfaces.ts +67 -0
  275. package/src/lib/post-display.ts +42 -9
  276. package/src/lib/post-meta.ts +19 -0
  277. package/src/lib/render.tsx +55 -7
  278. package/src/lib/resolve-config.ts +53 -5
  279. package/src/lib/schemas.ts +201 -10
  280. package/src/lib/site-header-fragment.tsx +2 -1
  281. package/src/lib/site-unavailable-page.ts +47 -0
  282. package/src/lib/slug.ts +51 -7
  283. package/src/lib/slugify.ts +113 -0
  284. package/src/lib/startup-config.ts +4 -6
  285. package/src/lib/storage.ts +7 -0
  286. package/src/lib/summary.ts +50 -0
  287. package/src/lib/thread-fold.ts +161 -0
  288. package/src/lib/time.ts +32 -0
  289. package/src/lib/timeline.ts +81 -10
  290. package/src/lib/tiptap-render.ts +5 -4
  291. package/src/lib/translit.ts +288 -0
  292. package/src/lib/upload.ts +16 -13
  293. package/src/lib/url.ts +188 -24
  294. package/src/lib/version.ts +10 -8
  295. package/src/lib/view-language.ts +436 -0
  296. package/src/lib/view.ts +79 -23
  297. package/src/lib/viewer-context.ts +62 -0
  298. package/src/middleware/__tests__/auth.test.ts +70 -2
  299. package/src/middleware/__tests__/cache-control.test.ts +71 -3
  300. package/src/middleware/__tests__/onboarding.test.ts +76 -5
  301. package/src/middleware/__tests__/secure-headers.test.ts +1 -1
  302. package/src/middleware/__tests__/session.test.ts +181 -33
  303. package/src/middleware/auth.ts +50 -31
  304. package/src/middleware/cache-control.ts +75 -10
  305. package/src/middleware/config.ts +6 -6
  306. package/src/middleware/error-handler.ts +12 -0
  307. package/src/middleware/onboarding.ts +32 -10
  308. package/src/middleware/secure-headers.ts +1 -1
  309. package/src/middleware/session.ts +96 -8
  310. package/src/node/__tests__/cli-migrate.test.ts +21 -19
  311. package/src/node/__tests__/cli-reset-password.test.ts +81 -8
  312. package/src/node/__tests__/cli-runtime-target.test.ts +48 -1
  313. package/src/node/__tests__/cli-site-selection.test.ts +128 -38
  314. package/src/node/__tests__/runtime.test.ts +37 -0
  315. package/src/node/index.ts +1 -0
  316. package/src/preset.css +76 -22
  317. package/src/routes/__tests__/feed-validator.test.ts +156 -0
  318. package/src/routes/api/__tests__/collections.test.ts +114 -0
  319. package/src/routes/api/__tests__/discover.test.ts +222 -0
  320. package/src/routes/api/__tests__/nav-items.test.ts +212 -2
  321. package/src/routes/api/__tests__/palette.test.ts +44 -0
  322. package/src/routes/api/__tests__/post-translations.test.ts +829 -0
  323. package/src/routes/api/__tests__/settings.test.ts +24 -3
  324. package/src/routes/api/__tests__/telegram.test.ts +6 -5
  325. package/src/routes/api/collections.ts +49 -6
  326. package/src/routes/api/custom-urls.ts +0 -1
  327. package/src/routes/api/discover.ts +95 -0
  328. package/src/routes/api/export.ts +3 -0
  329. package/src/routes/api/internal/__tests__/sites.test.ts +42 -2
  330. package/src/routes/api/nav-items.ts +55 -8
  331. package/src/routes/api/posts.ts +172 -9
  332. package/src/routes/api/public/__tests__/archive.test.ts +111 -0
  333. package/src/routes/api/public/__tests__/posts.test.ts +38 -0
  334. package/src/routes/api/public/archive.ts +77 -81
  335. package/src/routes/api/public/posts.ts +30 -6
  336. package/src/routes/api/smart-collections.ts +132 -0
  337. package/src/routes/api/telegram.ts +2 -40
  338. package/src/routes/api/uploads.ts +1 -2
  339. package/src/routes/auth/__tests__/setup-auth.test.ts +137 -0
  340. package/src/routes/auth/__tests__/setup-discover.test.ts +204 -0
  341. package/src/routes/auth/__tests__/setup-flow.test.ts +270 -0
  342. package/src/routes/auth/__tests__/setup-page.test.tsx +329 -0
  343. package/src/routes/auth/__tests__/setup-steps.test.ts +310 -0
  344. package/src/routes/auth/__tests__/setup.test.ts +278 -26
  345. package/src/routes/auth/__tests__/signin.test.ts +135 -0
  346. package/src/routes/auth/setup.tsx +934 -186
  347. package/src/routes/auth/signin.tsx +10 -8
  348. package/src/routes/compose.tsx +11 -2
  349. package/src/routes/dash/__tests__/custom-urls-archive.test.ts +44 -0
  350. package/src/routes/dash/__tests__/custom-urls-page.test.ts +49 -0
  351. package/src/routes/dash/__tests__/delete-account.test.ts +106 -0
  352. package/src/routes/dash/__tests__/settings-discover.test.ts +213 -0
  353. package/src/routes/dash/custom-urls.tsx +1 -37
  354. package/src/routes/dash/settings.tsx +502 -41
  355. package/src/routes/discover-announce.ts +96 -0
  356. package/src/routes/feed/__tests__/feed.test.ts +165 -2
  357. package/src/routes/feed/__tests__/sitemap.test.ts +68 -1
  358. package/src/routes/feed/feed.ts +69 -40
  359. package/src/routes/feed/sitemap.ts +95 -11
  360. package/src/routes/pages/__tests__/archive-indexing.test.ts +200 -0
  361. package/src/routes/pages/__tests__/archive-params.test.ts +657 -1
  362. package/src/routes/pages/__tests__/collection-routing.test.ts +16 -0
  363. package/src/routes/pages/__tests__/collections-feed-links.test.ts +72 -0
  364. package/src/routes/pages/__tests__/collections.test.ts +9 -3
  365. package/src/routes/pages/__tests__/custom-archive-url.test.ts +119 -0
  366. package/src/routes/pages/__tests__/featured.test.ts +6 -2
  367. package/src/routes/pages/__tests__/feed-autodiscovery.test.ts +148 -0
  368. package/src/routes/pages/__tests__/language-routing.test.ts +679 -0
  369. package/src/routes/pages/__tests__/post-preview-partial.test.ts +96 -0
  370. package/src/routes/pages/__tests__/preview.test.ts +3 -1
  371. package/src/routes/pages/__tests__/smart-collection.test.ts +298 -0
  372. package/src/routes/pages/__tests__/subscribe.test.ts +248 -0
  373. package/src/routes/pages/archive.tsx +507 -357
  374. package/src/routes/pages/collection.tsx +94 -96
  375. package/src/routes/pages/collections.tsx +38 -50
  376. package/src/routes/pages/featured.tsx +32 -13
  377. package/src/routes/pages/home.tsx +25 -6
  378. package/src/routes/pages/language.tsx +113 -0
  379. package/src/routes/pages/latest.tsx +26 -16
  380. package/src/routes/pages/new.tsx +3 -0
  381. package/src/routes/pages/page.tsx +183 -19
  382. package/src/routes/pages/partials.tsx +41 -0
  383. package/src/routes/pages/search.tsx +18 -3
  384. package/src/routes/pages/smart-collection.tsx +340 -0
  385. package/src/routes/pages/subscribe.tsx +182 -0
  386. package/src/runtime/__tests__/index.test.ts +19 -0
  387. package/src/runtime/__tests__/node.test.ts +129 -4
  388. package/src/runtime/cloudflare.ts +21 -2
  389. package/src/runtime/index.ts +27 -1
  390. package/src/runtime/node.ts +18 -1
  391. package/src/runtime/site.ts +95 -24
  392. package/src/services/__tests__/auth.test.ts +116 -7
  393. package/src/services/__tests__/collection.test.ts +139 -4
  394. package/src/services/__tests__/language.test.ts +495 -0
  395. package/src/services/__tests__/navigation.test.ts +213 -10
  396. package/src/services/__tests__/path.test.ts +180 -0
  397. package/src/services/__tests__/post-language.test.ts +830 -0
  398. package/src/services/__tests__/post-timeline.test.ts +142 -11
  399. package/src/services/__tests__/post.test.ts +323 -9
  400. package/src/services/__tests__/settings.test.ts +228 -17
  401. package/src/services/__tests__/site-admin.test.ts +5 -5
  402. package/src/services/__tests__/smart-collection.test.ts +651 -0
  403. package/src/services/about-page.ts +22 -1
  404. package/src/services/auth.ts +51 -49
  405. package/src/services/bootstrap.ts +188 -38
  406. package/src/services/collection-directory-position.ts +76 -0
  407. package/src/services/collection.ts +330 -47
  408. package/src/services/custom-url.ts +27 -22
  409. package/src/services/export-theme/assets/client-site.css +1 -1
  410. package/src/services/export-theme/assets/client-site.js +123 -123
  411. package/src/services/export-theme/layouts/_default/rss.xml +77 -1
  412. package/src/services/export-theme/layouts/partials/feed-post-content.xml +105 -2
  413. package/src/services/export-theme/layouts/partials/media-gallery.html +35 -21
  414. package/src/services/export-theme/layouts/partials/pagination.html +14 -13
  415. package/src/services/export-theme/layouts/partials/post-card.html +8 -4
  416. package/src/services/export-theme/layouts/partials/reply.html +2 -1
  417. package/src/services/export-theme/layouts/partials/thread-preview.html +40 -30
  418. package/src/services/export-theme/styles/main.css +13 -1
  419. package/src/services/export.ts +281 -24
  420. package/src/services/index.ts +34 -3
  421. package/src/services/language.ts +412 -0
  422. package/src/services/mcp.ts +3 -1
  423. package/src/services/media.ts +14 -6
  424. package/src/services/navigation.ts +429 -67
  425. package/src/services/path.ts +225 -7
  426. package/src/services/post.ts +1318 -252
  427. package/src/services/search.ts +48 -2
  428. package/src/services/settings.ts +238 -43
  429. package/src/services/site-admin.ts +24 -7
  430. package/src/services/site.ts +77 -1
  431. package/src/services/smart-collection.ts +636 -0
  432. package/src/services/telegram.ts +20 -22
  433. package/src/services/upload-session.ts +1 -3
  434. package/src/style-author.css +27 -0
  435. package/src/styles/components-author.css +1622 -0
  436. package/src/styles/components.css +24 -1585
  437. package/src/styles/site-media.css +20 -6
  438. package/src/styles/tokens.css +144 -13
  439. package/src/styles/ui-author.css +7602 -0
  440. package/src/styles/ui.css +1224 -7276
  441. package/src/types/app-context.ts +7 -0
  442. package/src/types/bindings.ts +3 -0
  443. package/src/types/config.ts +94 -15
  444. package/src/types/constants.ts +188 -16
  445. package/src/types/entities.ts +92 -1
  446. package/src/types/operations.ts +64 -2
  447. package/src/types/props.ts +168 -30
  448. package/src/types/views.ts +61 -1
  449. package/src/ui/__tests__/font-themes.test.ts +52 -12
  450. package/src/ui/compose/ComposeDialog.tsx +215 -21
  451. package/src/ui/compose/ComposePrompt.tsx +1 -5
  452. package/src/ui/compose/__tests__/ComposeDialog.test.tsx +125 -0
  453. package/src/ui/dash/appearance/AdvancedContent.tsx +2 -2
  454. package/src/ui/dash/appearance/CodeInjectionContent.tsx +12 -16
  455. package/src/ui/dash/appearance/NavigationContent.tsx +117 -16
  456. package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +33 -11
  457. package/src/ui/dash/appearance/__tests__/system-nav-descriptions.test.ts +44 -0
  458. package/src/ui/dash/appearance/system-nav-descriptions.ts +75 -0
  459. package/src/ui/dash/settings/AccountMenuContent.tsx +2 -2
  460. package/src/ui/dash/settings/ApiTokensContent.tsx +2 -1
  461. package/src/ui/dash/settings/ConfigEditorContent.tsx +35 -16
  462. package/src/ui/dash/settings/GeneralContent.tsx +275 -113
  463. package/src/ui/dash/settings/LanguageContent.tsx +409 -0
  464. package/src/ui/dash/settings/SettingsRootContent.tsx +19 -1
  465. package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +324 -4
  466. package/src/ui/dash/settings/__tests__/LanguageContent.test.tsx +144 -0
  467. package/src/ui/dash/settings/__tests__/discover-copy.test.ts +66 -0
  468. package/src/ui/dash/settings/discover-copy.ts +116 -0
  469. package/src/ui/feed/LinkCard.tsx +55 -45
  470. package/src/ui/feed/LinkPreview.tsx +2 -7
  471. package/src/ui/feed/NoteCard.tsx +9 -4
  472. package/src/ui/feed/PostStatusBadges.tsx +103 -17
  473. package/src/ui/feed/QuoteCard.tsx +55 -30
  474. package/src/ui/feed/ThreadPreview.tsx +6 -2
  475. package/src/ui/feed/__tests__/thread-preview.test.ts +13 -16
  476. package/src/ui/feed/__tests__/timeline-cards.test.ts +314 -49
  477. package/src/ui/feed/thread-preview-state.ts +20 -6
  478. package/src/ui/font-themes.ts +39 -29
  479. package/src/ui/layouts/BaseLayout.tsx +155 -10
  480. package/src/ui/layouts/SiteLayout.tsx +192 -34
  481. package/src/ui/layouts/__tests__/BaseLayout.test.tsx +196 -17
  482. package/src/ui/layouts/__tests__/SiteLayout.test.tsx +109 -85
  483. package/src/ui/pages/ArchivePage.tsx +401 -301
  484. package/src/ui/pages/CollectionPage.tsx +86 -76
  485. package/src/ui/pages/CollectionsPage.tsx +5 -0
  486. package/src/ui/pages/ComposePage.tsx +8 -0
  487. package/src/ui/pages/PostPage.tsx +47 -0
  488. package/src/ui/pages/SearchPage.tsx +3 -3
  489. package/src/ui/pages/SmartCollectionPage.tsx +434 -0
  490. package/src/ui/pages/SubscribePage.tsx +236 -0
  491. package/src/ui/pages/__tests__/ArchivePage.test.tsx +122 -3
  492. package/src/ui/pages/__tests__/collections-directory-rows.test.tsx +92 -0
  493. package/src/ui/pages/__tests__/collections-view-links.test.tsx +81 -0
  494. package/src/ui/shared/CollectionDirectory.tsx +178 -3
  495. package/src/ui/shared/CollectionsManager.tsx +70 -17
  496. package/src/ui/shared/CopyField.tsx +127 -0
  497. package/src/ui/shared/DraftPreviewBar.tsx +25 -9
  498. package/src/ui/shared/EmptyPostContent.tsx +23 -0
  499. package/src/ui/shared/MediaGallery.tsx +125 -46
  500. package/src/ui/shared/Pagination.tsx +20 -5
  501. package/src/ui/shared/PostFooter.tsx +55 -23
  502. package/src/ui/shared/__tests__/archive-labels.test.ts +100 -0
  503. package/src/ui/shared/__tests__/media-gallery.test.ts +210 -2
  504. package/src/ui/shared/__tests__/navigation-labels.test.ts +16 -22
  505. package/src/ui/shared/__tests__/pagination.test.ts +38 -24
  506. package/src/ui/shared/__tests__/post-footer.test.ts +32 -0
  507. package/src/ui/shared/archive-labels.ts +98 -0
  508. package/src/ui/shared/collection-management-labels.ts +94 -16
  509. package/src/ui/shared/custom-icons.ts +5 -0
  510. package/src/ui/shared/navigation-labels.ts +45 -71
  511. package/src/ui/shared/post-article-attributes.ts +8 -0
  512. package/src/ui/shared/smart-collection-labels.ts +343 -0
  513. package/dist/app-BxCOR3Uc.js +0 -6
  514. package/dist/client/_assets/chunks/heic-to-DUUaO23q.js +0 -1
  515. package/dist/client/_assets/chunks/module-DcsAZQZ_.js +0 -716
  516. package/dist/client/_assets/chunks/url-pLre2DM_.js +0 -1
  517. package/dist/client/_assets/client-B_eGKN5p.css +0 -2
  518. package/dist/client/_assets/client-DxY5BFe8.js +0 -296
  519. package/dist/client/_assets/client-auth-CakPESv9.js +0 -5533
  520. package/dist/env-OHRKGcMj.js +0 -281
  521. package/dist/github-api-BgSiE71w.js +0 -176
  522. package/dist/github-app-BbklkFmU.js +0 -275
  523. package/dist/github-sync-Cq1pzzOI.js +0 -431
  524. package/dist/github-sync-ppWXN3jb.js +0 -4
  525. package/dist/url-BMYO-Zlt.js +0 -395
  526. package/src/client/__tests__/collection-created-notice.test.ts +0 -36
  527. package/src/client/__tests__/collection-form-bridge.test.ts +0 -216
  528. package/src/client/__tests__/post-form-bridge.test.ts +0 -82
  529. package/src/client/collection-created-notice.ts +0 -66
  530. package/src/client/collection-form-bridge.ts +0 -191
  531. package/src/client/components/__tests__/jant-post-form.test.ts +0 -193
  532. package/src/client/components/jant-post-form.ts +0 -422
  533. package/src/client/components/post-form-template.ts +0 -352
  534. package/src/client/components/post-form-types.ts +0 -96
  535. package/src/client/media-metadata.ts +0 -247
  536. package/src/client/post-form-bridge.ts +0 -193
  537. 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
+ }
@@ -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>
@@ -552,17 +560,20 @@ export class JantRepoPicker extends LitElement {
552
560
  class="flex w-full 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,45 +667,52 @@ 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 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
@@ -696,14 +722,16 @@ export class JantRepoPicker extends LitElement {
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.replace(
729
+ "{name}",
730
+ this.createRepoNameHint,
731
+ )}
732
+ </span>`
733
+ : nothing
734
+ }
707
735
  </span>
708
736
  </button>
709
737
  </div>
@@ -730,17 +758,21 @@ export class JantRepoPicker extends LitElement {
730
758
  </p>`;
731
759
  }
732
760
  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
- )}
761
+ return html`<div class="alert-destructive text-sm" role="alert">
762
+ <section>
763
+ <p>
764
+ ${this.labels.classificationOwnedByOther.replace(
765
+ "{host}",
766
+ c.marker.site_host,
767
+ )}
768
+ </p>
769
+ </section>
738
770
  </div>`;
739
771
  }
740
772
  // foreign — show confirm input
741
773
  const full = this._selectedRepo.fullName;
742
774
  return html`
743
- <div class="alert alert-warning flex flex-col gap-3 text-sm">
775
+ <div class="alert flex flex-col gap-3 text-sm">
744
776
  <div>
745
777
  <strong class="block mb-1">${this.labels.confirmHeading}</strong>
746
778
  <span>${this.labels.confirmBody.replace("{repo}", full)}</span>