@jant/core 0.6.16 → 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 (508) hide show
  1. package/README.md +11 -11
  2. package/bin/commands/assets/upload.js +192 -15
  3. package/bin/commands/import-site.js +60 -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-C7_4oab9.js → app-K_Aa1MMn.js} +52476 -13330
  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-Dl5KCiEs.js → github-sync-BPAvT999.js} +2254 -107
  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__/stylesheet-audience.test.ts +159 -0
  51. package/src/__tests__/stylesheet-breakpoints.test.ts +183 -0
  52. package/src/app.tsx +71 -16
  53. package/src/auth.ts +18 -1
  54. package/src/client/__tests__/collection-page-actions.test.ts +51 -2
  55. package/src/client/__tests__/compose-bridge-image-upload.test.ts +196 -0
  56. package/src/client/__tests__/compose-bridge-video-poster.test.ts +159 -0
  57. package/src/client/__tests__/compose-bridge.test.ts +272 -0
  58. package/src/client/__tests__/compose-launch.test.ts +62 -1
  59. package/src/client/__tests__/compose-shortcuts.test.ts +38 -9
  60. package/src/client/__tests__/compose-triggers.test.ts +90 -0
  61. package/src/client/__tests__/copy-field.test.ts +162 -0
  62. package/src/client/__tests__/custom-url-menu.test.ts +127 -6
  63. package/src/client/__tests__/image-processor.test.ts +324 -2
  64. package/src/client/__tests__/lazy-entries.test.ts +92 -0
  65. package/src/client/__tests__/media-scroll-hint.test.ts +155 -0
  66. package/src/client/__tests__/runtime-paths.test.ts +84 -0
  67. package/src/client/__tests__/smart-collection-page-actions.test.ts +158 -0
  68. package/src/client/__tests__/toast.test.ts +23 -0
  69. package/src/client/__tests__/video-processor-probe.test.ts +175 -0
  70. package/src/client/__tests__/video-processor.test.ts +181 -0
  71. package/src/client/audio-processor.ts +24 -15
  72. package/src/client/audio-waveform.ts +49 -0
  73. package/src/client/collection-dialog-host.ts +72 -0
  74. package/src/client/collection-navigation.ts +51 -16
  75. package/src/client/collection-page-actions.ts +26 -0
  76. package/src/client/components/__tests__/jant-collection-dialog.test.ts +329 -0
  77. package/src/client/components/__tests__/jant-collection-directory.test.ts +274 -10
  78. package/src/client/components/__tests__/jant-collection-form.test.ts +59 -178
  79. package/src/client/components/__tests__/jant-command-palette.test.ts +28 -0
  80. package/src/client/components/__tests__/jant-compose-dialog-draft-storage.test.ts +340 -0
  81. package/src/client/components/__tests__/jant-compose-dialog.test.ts +1250 -53
  82. package/src/client/components/__tests__/jant-compose-editor.test.ts +544 -40
  83. package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +0 -1
  84. package/src/client/components/__tests__/jant-locale-picker.test.ts +207 -0
  85. package/src/client/components/__tests__/jant-nav-manager.test.ts +234 -15
  86. package/src/client/components/__tests__/jant-post-menu.test.ts +413 -2
  87. package/src/client/components/__tests__/jant-post-picker.test.ts +197 -0
  88. package/src/client/components/__tests__/jant-settings-avatar.test.ts +15 -12
  89. package/src/client/components/__tests__/jant-settings-general.test.ts +541 -172
  90. package/src/client/components/__tests__/jant-settings-language.test.ts +851 -0
  91. package/src/client/components/__tests__/jant-smart-collection-dialog.test.ts +665 -0
  92. package/src/client/components/collection-dialog-types.ts +49 -0
  93. package/src/client/components/collection-manager-types.ts +32 -1
  94. package/src/client/components/collection-types.ts +1 -15
  95. package/src/client/components/compose-types.ts +75 -1
  96. package/src/client/components/jant-collection-dialog.ts +722 -0
  97. package/src/client/components/jant-collection-directory.ts +567 -182
  98. package/src/client/components/jant-collection-form.ts +53 -232
  99. package/src/client/components/jant-command-palette.ts +100 -49
  100. package/src/client/components/jant-compose-dialog.ts +1486 -707
  101. package/src/client/components/jant-compose-editor.ts +673 -336
  102. package/src/client/components/jant-compose-fullscreen.ts +51 -43
  103. package/src/client/components/jant-config-editor.ts +65 -53
  104. package/src/client/components/jant-confirm-dialog.ts +13 -11
  105. package/src/client/components/jant-locale-picker.ts +418 -0
  106. package/src/client/components/jant-media-lightbox.ts +146 -136
  107. package/src/client/components/jant-nav-manager.ts +690 -366
  108. package/src/client/components/jant-post-menu.ts +1086 -282
  109. package/src/client/components/jant-post-picker.ts +324 -0
  110. package/src/client/components/jant-repo-picker.ts +138 -106
  111. package/src/client/components/jant-settings-avatar.ts +31 -27
  112. package/src/client/components/jant-settings-general.ts +617 -543
  113. package/src/client/components/jant-settings-language.ts +1087 -0
  114. package/src/client/components/jant-smart-collection-dialog.ts +1021 -0
  115. package/src/client/components/jant-text-preview.ts +94 -86
  116. package/src/client/components/nav-manager-types.ts +62 -1
  117. package/src/client/components/settings-types.ts +14 -29
  118. package/src/client/components/smart-collection-conditions.ts +285 -0
  119. package/src/client/components/smart-collection-dialog-types.ts +68 -0
  120. package/src/client/compose-bridge.ts +214 -141
  121. package/src/client/compose-launch.ts +76 -2
  122. package/src/client/compose-shortcuts.ts +6 -8
  123. package/src/client/compose-triggers.ts +50 -0
  124. package/src/client/copy-field.ts +108 -0
  125. package/src/client/custom-url-menu.ts +60 -4
  126. package/src/client/icons.ts +14 -0
  127. package/src/client/image-plan.ts +165 -0
  128. package/src/client/image-processor.ts +195 -117
  129. package/src/client/image-worker-client.ts +45 -0
  130. package/src/client/image-worker.ts +135 -0
  131. package/src/client/lazy-entries.ts +95 -0
  132. package/src/client/lazy-slugify.ts +2 -2
  133. package/src/client/media-scroll-hint.ts +43 -2
  134. package/src/client/mediabunny.ts +29 -0
  135. package/src/client/post-picker.ts +41 -0
  136. package/src/client/runtime-paths.ts +74 -1
  137. package/src/client/settings-bridge.ts +4 -10
  138. package/src/client/site-header-nav.js +18 -6
  139. package/src/client/smart-collection-dialog-host.ts +116 -0
  140. package/src/client/smart-collection-page-actions.ts +208 -0
  141. package/src/client/tiptap/__tests__/bubble-menu.test.ts +110 -0
  142. package/src/client/tiptap/__tests__/floating-position.test.ts +136 -1
  143. package/src/client/tiptap/__tests__/inline-image-upload.test.ts +37 -0
  144. package/src/client/tiptap/bubble-menu.ts +47 -16
  145. package/src/client/tiptap/floating-position.ts +216 -2
  146. package/src/client/tiptap/inline-image-upload.ts +7 -3
  147. package/src/client/tiptap/link-toolbar.ts +22 -9
  148. package/src/client/toast.ts +13 -0
  149. package/src/client/types/vite-worker.d.ts +12 -0
  150. package/src/client/upload-with-metadata.ts +3 -13
  151. package/src/client/video-processor.ts +351 -107
  152. package/src/client-auth.ts +14 -31
  153. package/src/client-compose.ts +19 -0
  154. package/src/client-manage.ts +13 -0
  155. package/src/client-settings.ts +14 -0
  156. package/src/client-site.ts +10 -3
  157. package/src/client.ts +4 -0
  158. package/src/db/__tests__/backfill-nav-label-mirrors.test.ts +149 -0
  159. package/src/db/__tests__/schema-enum-sources.test.ts +33 -0
  160. package/src/db/__tests__/smart-collection-placement.test.ts +402 -0
  161. package/src/db/backfills/0006_clear_mirrored_page_nav_labels.sql +31 -0
  162. package/src/db/migrations/0032_married_tomas.sql +3 -0
  163. package/src/db/migrations/0033_busy_johnny_storm.sql +198 -0
  164. package/src/db/migrations/0034_cheerful_rumiko_fujikawa.sql +35 -0
  165. package/src/db/migrations/meta/0032_snapshot.json +2582 -0
  166. package/src/db/migrations/meta/0033_snapshot.json +2816 -0
  167. package/src/db/migrations/meta/0034_snapshot.json +2816 -0
  168. package/src/db/migrations/meta/_journal.json +21 -0
  169. package/src/db/migrations/pg/0030_abandoned_nighthawk.sql +3 -0
  170. package/src/db/migrations/pg/0031_strong_ultimatum.sql +128 -0
  171. package/src/db/migrations/pg/0032_furry_multiple_man.sql +6 -0
  172. package/src/db/migrations/pg/meta/0030_snapshot.json +3329 -0
  173. package/src/db/migrations/pg/meta/0031_snapshot.json +3660 -0
  174. package/src/db/migrations/pg/meta/0032_snapshot.json +3660 -0
  175. package/src/db/migrations/pg/meta/_journal.json +21 -0
  176. package/src/db/pg/schema.ts +195 -27
  177. package/src/db/post-visibility.ts +117 -0
  178. package/src/db/schema.ts +222 -27
  179. package/src/i18n/__tests__/detect.test.ts +26 -81
  180. package/src/i18n/__tests__/supported-locales.test.ts +83 -0
  181. package/src/i18n/context.tsx +12 -2
  182. package/src/i18n/detect.ts +7 -66
  183. package/src/i18n/locales/glossary.zh-Hans.yml +44 -4
  184. package/src/i18n/locales/glossary.zh-Hant.yml +44 -4
  185. package/src/i18n/locales/public/en.po +490 -158
  186. package/src/i18n/locales/public/en.ts +1 -1
  187. package/src/i18n/locales/public/zh-Hans.po +487 -155
  188. package/src/i18n/locales/public/zh-Hans.ts +1 -1
  189. package/src/i18n/locales/public/zh-Hant.po +487 -155
  190. package/src/i18n/locales/public/zh-Hant.ts +1 -1
  191. package/src/i18n/locales/settings/en.po +551 -69
  192. package/src/i18n/locales/settings/en.ts +1 -1
  193. package/src/i18n/locales/settings/zh-Hans.po +597 -115
  194. package/src/i18n/locales/settings/zh-Hans.ts +1 -1
  195. package/src/i18n/locales/settings/zh-Hant.po +610 -128
  196. package/src/i18n/locales/settings/zh-Hant.ts +1 -1
  197. package/src/i18n/locales.ts +94 -0
  198. package/src/i18n/middleware.ts +12 -2
  199. package/src/i18n/supported-locales.ts +80 -0
  200. package/src/lib/__tests__/api-settings.test.ts +11 -2
  201. package/src/lib/__tests__/asset-path.test.ts +92 -0
  202. package/src/lib/__tests__/deferred.test.ts +90 -0
  203. package/src/lib/__tests__/discover-ping.test.ts +89 -0
  204. package/src/lib/__tests__/discover.test.ts +410 -0
  205. package/src/lib/__tests__/feed-policy.test.ts +26 -0
  206. package/src/lib/__tests__/feed.test.ts +1526 -15
  207. package/src/lib/__tests__/filter-dimensions.test.ts +341 -0
  208. package/src/lib/__tests__/hosted-signin.test.ts +1 -1
  209. package/src/lib/__tests__/http-cache.test.ts +162 -0
  210. package/src/lib/__tests__/jant-docs-dev.test.ts +21 -0
  211. package/src/lib/__tests__/jant-docs.test.ts +23 -0
  212. package/src/lib/__tests__/lang-detect.test.ts +231 -0
  213. package/src/lib/__tests__/media-helpers.test.ts +81 -0
  214. package/src/lib/__tests__/navigation.test.ts +135 -1
  215. package/src/lib/__tests__/resolve-config.test.ts +64 -3
  216. package/src/lib/__tests__/schemas.test.ts +42 -4
  217. package/src/lib/__tests__/site-unavailable-page.test.ts +25 -0
  218. package/src/lib/__tests__/slug.test.ts +70 -0
  219. package/src/lib/__tests__/slugify.test.ts +85 -0
  220. package/src/lib/__tests__/storage.test.ts +32 -0
  221. package/src/lib/__tests__/thread-fold.test.ts +113 -0
  222. package/src/lib/__tests__/upload-sideload.test.ts +14 -0
  223. package/src/lib/__tests__/url-fetch.test.ts +1 -2
  224. package/src/lib/__tests__/url.test.ts +78 -72
  225. package/src/lib/__tests__/view-language.test.ts +413 -0
  226. package/src/lib/__tests__/worker-response-cache.test.ts +90 -0
  227. package/src/lib/address.ts +57 -0
  228. package/src/lib/api-posts.ts +12 -0
  229. package/src/lib/asset-path.ts +98 -1
  230. package/src/lib/build-env.ts +16 -0
  231. package/src/lib/collection-groups.ts +5 -1
  232. package/src/lib/collection-paths.ts +3 -10
  233. package/src/lib/constants.ts +28 -3
  234. package/src/lib/copy-field.ts +36 -0
  235. package/src/lib/crypto.ts +40 -0
  236. package/src/lib/csp-builder.ts +2 -1
  237. package/src/lib/deferred.ts +79 -0
  238. package/src/lib/discover-ping.ts +141 -0
  239. package/src/lib/discover.ts +437 -0
  240. package/src/lib/env.ts +134 -0
  241. package/src/lib/errors.ts +33 -0
  242. package/src/lib/feed-policy.ts +236 -2
  243. package/src/lib/feed.ts +926 -73
  244. package/src/lib/filter-dimensions.ts +1349 -0
  245. package/src/lib/github-sync-site-config.ts +3 -0
  246. package/src/lib/hosted-signin.ts +1 -1
  247. package/src/lib/http-cache.ts +148 -0
  248. package/src/lib/hugo-markdown.ts +16 -0
  249. package/src/lib/icons.ts +9 -2
  250. package/src/lib/ids.ts +3 -0
  251. package/src/lib/jant-docs.ts +42 -0
  252. package/src/lib/lang-detect.ts +308 -0
  253. package/src/lib/media-helpers.ts +13 -1
  254. package/src/lib/navigation.ts +78 -11
  255. package/src/lib/pagination.ts +28 -24
  256. package/src/lib/per-language-surfaces.ts +67 -0
  257. package/src/lib/post-meta.ts +19 -0
  258. package/src/lib/render.tsx +55 -7
  259. package/src/lib/resolve-config.ts +53 -5
  260. package/src/lib/schemas.ts +201 -10
  261. package/src/lib/site-header-fragment.tsx +2 -1
  262. package/src/lib/site-unavailable-page.ts +47 -0
  263. package/src/lib/slug.ts +51 -7
  264. package/src/lib/slugify.ts +113 -0
  265. package/src/lib/startup-config.ts +4 -6
  266. package/src/lib/storage.ts +7 -0
  267. package/src/lib/summary.ts +50 -0
  268. package/src/lib/thread-fold.ts +161 -0
  269. package/src/lib/time.ts +32 -0
  270. package/src/lib/timeline.ts +42 -1
  271. package/src/lib/tiptap-render.ts +1 -2
  272. package/src/lib/upload.ts +16 -13
  273. package/src/lib/url.ts +139 -86
  274. package/src/lib/version.ts +10 -8
  275. package/src/lib/view-language.ts +436 -0
  276. package/src/lib/view.ts +51 -23
  277. package/src/lib/viewer-context.ts +62 -0
  278. package/src/middleware/__tests__/auth.test.ts +70 -2
  279. package/src/middleware/__tests__/cache-control.test.ts +71 -3
  280. package/src/middleware/__tests__/onboarding.test.ts +76 -5
  281. package/src/middleware/__tests__/secure-headers.test.ts +1 -1
  282. package/src/middleware/__tests__/session.test.ts +181 -33
  283. package/src/middleware/auth.ts +50 -31
  284. package/src/middleware/cache-control.ts +75 -10
  285. package/src/middleware/config.ts +6 -6
  286. package/src/middleware/error-handler.ts +12 -0
  287. package/src/middleware/onboarding.ts +32 -10
  288. package/src/middleware/secure-headers.ts +1 -1
  289. package/src/middleware/session.ts +96 -8
  290. package/src/node/__tests__/cli-migrate.test.ts +21 -19
  291. package/src/node/__tests__/cli-reset-password.test.ts +81 -8
  292. package/src/node/__tests__/cli-runtime-target.test.ts +48 -1
  293. package/src/node/__tests__/cli-site-selection.test.ts +128 -38
  294. package/src/node/__tests__/runtime.test.ts +37 -0
  295. package/src/node/index.ts +1 -0
  296. package/src/preset.css +76 -22
  297. package/src/routes/__tests__/feed-validator.test.ts +156 -0
  298. package/src/routes/api/__tests__/collections.test.ts +114 -0
  299. package/src/routes/api/__tests__/discover.test.ts +222 -0
  300. package/src/routes/api/__tests__/nav-items.test.ts +212 -2
  301. package/src/routes/api/__tests__/post-translations.test.ts +829 -0
  302. package/src/routes/api/__tests__/settings.test.ts +24 -3
  303. package/src/routes/api/__tests__/telegram.test.ts +6 -5
  304. package/src/routes/api/collections.ts +49 -6
  305. package/src/routes/api/custom-urls.ts +0 -1
  306. package/src/routes/api/discover.ts +95 -0
  307. package/src/routes/api/export.ts +3 -0
  308. package/src/routes/api/internal/__tests__/sites.test.ts +42 -2
  309. package/src/routes/api/nav-items.ts +55 -8
  310. package/src/routes/api/posts.ts +159 -0
  311. package/src/routes/api/public/__tests__/archive.test.ts +111 -0
  312. package/src/routes/api/public/archive.ts +77 -81
  313. package/src/routes/api/public/posts.ts +23 -6
  314. package/src/routes/api/smart-collections.ts +132 -0
  315. package/src/routes/api/telegram.ts +2 -40
  316. package/src/routes/api/uploads.ts +1 -2
  317. package/src/routes/auth/__tests__/setup-auth.test.ts +137 -0
  318. package/src/routes/auth/__tests__/setup-discover.test.ts +204 -0
  319. package/src/routes/auth/__tests__/setup-flow.test.ts +270 -0
  320. package/src/routes/auth/__tests__/setup-page.test.tsx +329 -0
  321. package/src/routes/auth/__tests__/setup-steps.test.ts +310 -0
  322. package/src/routes/auth/__tests__/setup.test.ts +278 -26
  323. package/src/routes/auth/__tests__/signin.test.ts +135 -0
  324. package/src/routes/auth/setup.tsx +934 -186
  325. package/src/routes/auth/signin.tsx +10 -8
  326. package/src/routes/compose.tsx +5 -0
  327. package/src/routes/dash/__tests__/custom-urls-archive.test.ts +44 -0
  328. package/src/routes/dash/__tests__/custom-urls-page.test.ts +49 -0
  329. package/src/routes/dash/__tests__/delete-account.test.ts +106 -0
  330. package/src/routes/dash/__tests__/settings-discover.test.ts +213 -0
  331. package/src/routes/dash/custom-urls.tsx +1 -37
  332. package/src/routes/dash/settings.tsx +502 -41
  333. package/src/routes/discover-announce.ts +96 -0
  334. package/src/routes/feed/__tests__/feed.test.ts +165 -2
  335. package/src/routes/feed/feed.ts +69 -40
  336. package/src/routes/feed/sitemap.ts +95 -11
  337. package/src/routes/pages/__tests__/archive-indexing.test.ts +200 -0
  338. package/src/routes/pages/__tests__/archive-params.test.ts +370 -6
  339. package/src/routes/pages/__tests__/collection-routing.test.ts +16 -0
  340. package/src/routes/pages/__tests__/collections-feed-links.test.ts +72 -0
  341. package/src/routes/pages/__tests__/collections.test.ts +9 -3
  342. package/src/routes/pages/__tests__/custom-archive-url.test.ts +119 -0
  343. package/src/routes/pages/__tests__/feed-autodiscovery.test.ts +148 -0
  344. package/src/routes/pages/__tests__/language-routing.test.ts +679 -0
  345. package/src/routes/pages/__tests__/post-preview-partial.test.ts +96 -0
  346. package/src/routes/pages/__tests__/smart-collection.test.ts +298 -0
  347. package/src/routes/pages/__tests__/subscribe.test.ts +248 -0
  348. package/src/routes/pages/archive.tsx +471 -369
  349. package/src/routes/pages/collection.tsx +94 -96
  350. package/src/routes/pages/collections.tsx +38 -50
  351. package/src/routes/pages/featured.tsx +32 -13
  352. package/src/routes/pages/home.tsx +25 -6
  353. package/src/routes/pages/language.tsx +113 -0
  354. package/src/routes/pages/latest.tsx +26 -16
  355. package/src/routes/pages/new.tsx +3 -0
  356. package/src/routes/pages/page.tsx +178 -17
  357. package/src/routes/pages/partials.tsx +41 -0
  358. package/src/routes/pages/search.tsx +18 -3
  359. package/src/routes/pages/smart-collection.tsx +340 -0
  360. package/src/routes/pages/subscribe.tsx +182 -0
  361. package/src/runtime/__tests__/index.test.ts +19 -0
  362. package/src/runtime/__tests__/node.test.ts +129 -4
  363. package/src/runtime/cloudflare.ts +21 -2
  364. package/src/runtime/index.ts +27 -1
  365. package/src/runtime/node.ts +18 -1
  366. package/src/runtime/site.ts +95 -24
  367. package/src/services/__tests__/auth.test.ts +116 -7
  368. package/src/services/__tests__/collection.test.ts +108 -4
  369. package/src/services/__tests__/language.test.ts +495 -0
  370. package/src/services/__tests__/navigation.test.ts +213 -10
  371. package/src/services/__tests__/path.test.ts +180 -0
  372. package/src/services/__tests__/post-language.test.ts +830 -0
  373. package/src/services/__tests__/post.test.ts +46 -9
  374. package/src/services/__tests__/settings.test.ts +228 -17
  375. package/src/services/__tests__/site-admin.test.ts +5 -5
  376. package/src/services/__tests__/smart-collection.test.ts +651 -0
  377. package/src/services/about-page.ts +22 -1
  378. package/src/services/auth.ts +51 -49
  379. package/src/services/bootstrap.ts +188 -38
  380. package/src/services/collection-directory-position.ts +76 -0
  381. package/src/services/collection.ts +331 -46
  382. package/src/services/custom-url.ts +27 -22
  383. package/src/services/export-theme/assets/client-site.css +1 -1
  384. package/src/services/export-theme/assets/client-site.js +123 -123
  385. package/src/services/export-theme/layouts/_default/rss.xml +77 -1
  386. package/src/services/export-theme/layouts/partials/feed-post-content.xml +105 -2
  387. package/src/services/export-theme/layouts/partials/media-gallery.html +35 -21
  388. package/src/services/export-theme/layouts/partials/pagination.html +14 -13
  389. package/src/services/export-theme/layouts/partials/post-card.html +8 -4
  390. package/src/services/export-theme/layouts/partials/reply.html +2 -1
  391. package/src/services/export-theme/layouts/partials/thread-preview.html +40 -30
  392. package/src/services/export-theme/styles/main.css +13 -1
  393. package/src/services/export.ts +278 -19
  394. package/src/services/index.ts +34 -3
  395. package/src/services/language.ts +412 -0
  396. package/src/services/mcp.ts +3 -1
  397. package/src/services/media.ts +14 -6
  398. package/src/services/navigation.ts +429 -67
  399. package/src/services/path.ts +221 -6
  400. package/src/services/post.ts +1021 -152
  401. package/src/services/search.ts +39 -2
  402. package/src/services/settings.ts +238 -43
  403. package/src/services/site-admin.ts +24 -7
  404. package/src/services/site.ts +77 -1
  405. package/src/services/smart-collection.ts +636 -0
  406. package/src/services/telegram.ts +20 -22
  407. package/src/services/upload-session.ts +1 -3
  408. package/src/style-author.css +27 -0
  409. package/src/styles/components-author.css +1622 -0
  410. package/src/styles/components.css +24 -1571
  411. package/src/styles/site-media.css +16 -2
  412. package/src/styles/tokens.css +82 -12
  413. package/src/styles/ui-author.css +7602 -0
  414. package/src/styles/ui.css +1087 -7219
  415. package/src/types/app-context.ts +7 -0
  416. package/src/types/bindings.ts +3 -0
  417. package/src/types/config.ts +94 -15
  418. package/src/types/constants.ts +188 -16
  419. package/src/types/entities.ts +87 -1
  420. package/src/types/operations.ts +64 -2
  421. package/src/types/props.ts +157 -31
  422. package/src/types/views.ts +55 -1
  423. package/src/ui/__tests__/font-themes.test.ts +52 -12
  424. package/src/ui/compose/ComposeDialog.tsx +201 -7
  425. package/src/ui/compose/ComposePrompt.tsx +1 -5
  426. package/src/ui/compose/__tests__/ComposeDialog.test.tsx +125 -0
  427. package/src/ui/dash/appearance/AdvancedContent.tsx +2 -2
  428. package/src/ui/dash/appearance/CodeInjectionContent.tsx +12 -16
  429. package/src/ui/dash/appearance/NavigationContent.tsx +117 -16
  430. package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +33 -11
  431. package/src/ui/dash/appearance/__tests__/system-nav-descriptions.test.ts +44 -0
  432. package/src/ui/dash/appearance/system-nav-descriptions.ts +75 -0
  433. package/src/ui/dash/settings/AccountMenuContent.tsx +2 -2
  434. package/src/ui/dash/settings/ApiTokensContent.tsx +2 -1
  435. package/src/ui/dash/settings/ConfigEditorContent.tsx +35 -16
  436. package/src/ui/dash/settings/GeneralContent.tsx +275 -113
  437. package/src/ui/dash/settings/LanguageContent.tsx +409 -0
  438. package/src/ui/dash/settings/SettingsRootContent.tsx +19 -1
  439. package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +324 -4
  440. package/src/ui/dash/settings/__tests__/LanguageContent.test.tsx +144 -0
  441. package/src/ui/dash/settings/__tests__/discover-copy.test.ts +66 -0
  442. package/src/ui/dash/settings/discover-copy.ts +116 -0
  443. package/src/ui/feed/LinkCard.tsx +55 -45
  444. package/src/ui/feed/NoteCard.tsx +9 -4
  445. package/src/ui/feed/PostStatusBadges.tsx +90 -43
  446. package/src/ui/feed/QuoteCard.tsx +55 -30
  447. package/src/ui/feed/ThreadPreview.tsx +6 -2
  448. package/src/ui/feed/__tests__/thread-preview.test.ts +13 -16
  449. package/src/ui/feed/__tests__/timeline-cards.test.ts +314 -49
  450. package/src/ui/feed/thread-preview-state.ts +20 -6
  451. package/src/ui/font-themes.ts +39 -29
  452. package/src/ui/layouts/BaseLayout.tsx +155 -10
  453. package/src/ui/layouts/SiteLayout.tsx +192 -34
  454. package/src/ui/layouts/__tests__/BaseLayout.test.tsx +196 -17
  455. package/src/ui/layouts/__tests__/SiteLayout.test.tsx +109 -85
  456. package/src/ui/pages/ArchivePage.tsx +282 -289
  457. package/src/ui/pages/CollectionPage.tsx +86 -76
  458. package/src/ui/pages/CollectionsPage.tsx +5 -0
  459. package/src/ui/pages/ComposePage.tsx +8 -0
  460. package/src/ui/pages/PostPage.tsx +47 -0
  461. package/src/ui/pages/SearchPage.tsx +3 -3
  462. package/src/ui/pages/SmartCollectionPage.tsx +434 -0
  463. package/src/ui/pages/SubscribePage.tsx +236 -0
  464. package/src/ui/pages/__tests__/ArchivePage.test.tsx +65 -9
  465. package/src/ui/pages/__tests__/collections-directory-rows.test.tsx +92 -0
  466. package/src/ui/pages/__tests__/collections-view-links.test.tsx +81 -0
  467. package/src/ui/shared/CollectionDirectory.tsx +178 -3
  468. package/src/ui/shared/CollectionsManager.tsx +70 -17
  469. package/src/ui/shared/CopyField.tsx +127 -0
  470. package/src/ui/shared/EmptyPostContent.tsx +23 -0
  471. package/src/ui/shared/MediaGallery.tsx +125 -46
  472. package/src/ui/shared/Pagination.tsx +20 -5
  473. package/src/ui/shared/PostFooter.tsx +20 -11
  474. package/src/ui/shared/__tests__/archive-labels.test.ts +100 -0
  475. package/src/ui/shared/__tests__/media-gallery.test.ts +210 -2
  476. package/src/ui/shared/__tests__/navigation-labels.test.ts +16 -22
  477. package/src/ui/shared/__tests__/pagination.test.ts +38 -24
  478. package/src/ui/shared/__tests__/post-footer.test.ts +32 -0
  479. package/src/ui/shared/archive-labels.ts +98 -0
  480. package/src/ui/shared/collection-management-labels.ts +94 -16
  481. package/src/ui/shared/navigation-labels.ts +45 -71
  482. package/src/ui/shared/post-article-attributes.ts +2 -0
  483. package/src/ui/shared/smart-collection-labels.ts +343 -0
  484. package/dist/app-C75Zc8dD.js +0 -6
  485. package/dist/client/_assets/chunks/heic-to-DUUaO23q.js +0 -1
  486. package/dist/client/_assets/chunks/module-DcsAZQZ_.js +0 -716
  487. package/dist/client/_assets/chunks/url-CU9pEbk5.js +0 -1
  488. package/dist/client/_assets/client-Bq0vre8Y.js +0 -296
  489. package/dist/client/_assets/client-D3VZyG4L.css +0 -2
  490. package/dist/client/_assets/client-auth-F2rb9WfF.js +0 -5571
  491. package/dist/env-BPYViDJJ.js +0 -281
  492. package/dist/github-api-BNF35Lkx.js +0 -176
  493. package/dist/github-app-DdiD-bC4.js +0 -275
  494. package/dist/github-sync-Bqg62IvV.js +0 -4
  495. package/dist/github-sync-HdK2EgvJ.js +0 -431
  496. package/dist/url-CbLAtlZe.js +0 -900
  497. package/src/client/__tests__/collection-created-notice.test.ts +0 -36
  498. package/src/client/__tests__/collection-form-bridge.test.ts +0 -216
  499. package/src/client/__tests__/post-form-bridge.test.ts +0 -82
  500. package/src/client/collection-created-notice.ts +0 -66
  501. package/src/client/collection-form-bridge.ts +0 -191
  502. package/src/client/components/__tests__/jant-post-form.test.ts +0 -193
  503. package/src/client/components/jant-post-form.ts +0 -422
  504. package/src/client/components/post-form-template.ts +0 -352
  505. package/src/client/components/post-form-types.ts +0 -96
  506. package/src/client/media-metadata.ts +0 -247
  507. package/src/client/post-form-bridge.ts +0 -193
  508. 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>