@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.
- package/README.md +11 -11
- package/bin/commands/assets/upload.js +192 -15
- package/bin/commands/import-site.js +60 -4
- package/bin/commands/reset-password.js +21 -8
- package/bin/commands/site/snapshot/export.js +1 -1
- package/bin/commands/site/snapshot/import.js +1 -1
- package/bin/lib/d1-query.js +5 -16
- package/bin/lib/node-env.js +40 -4
- package/bin/lib/site-selection.js +85 -29
- package/bin/lib/wrangler-cli.js +28 -2
- package/bin/lib/wrangler-json.js +35 -0
- package/dist/{app-C7_4oab9.js → app-K_Aa1MMn.js} +52476 -13330
- package/dist/client/.vite/manifest.json +255 -90
- package/dist/client/_assets/chunks/confirm-D7GrwsCN.js +45 -0
- package/dist/client/_assets/chunks/create-editor-CD3FhrOB.js +257 -0
- package/dist/client/_assets/chunks/{native-DpcrFAPh.js → emoji-mart-C4RpRKcU.js} +716 -1
- package/dist/client/_assets/chunks/footnote-rail-CmeoVtnU.js +1 -0
- package/dist/client/_assets/chunks/heic-to-CuP2Qlsw.js +1 -0
- package/dist/client/_assets/chunks/jant-locale-picker-DL6mIOT5.js +69 -0
- package/dist/client/_assets/chunks/lit-DW8VmJAT.js +2 -0
- package/dist/client/_assets/chunks/mediabunny-DRaCQ1r-.js +31 -0
- package/dist/client/_assets/chunks/rolldown-runtime-hePW80VL.js +1 -0
- package/dist/client/_assets/chunks/settings-paths-BcdySNx6.js +84 -0
- package/dist/client/_assets/chunks/site-header-nav-DzXsFk05.js +1 -0
- package/dist/client/_assets/chunks/slug-format-CRXO1AL5.js +1 -0
- package/dist/client/_assets/chunks/slugify-373citbs.js +1 -0
- package/dist/client/_assets/chunks/sortable-list-CgaL2jCs.js +2 -0
- package/dist/client/_assets/chunks/types-D5iF3H1a.js +1 -0
- package/dist/client/_assets/chunks/unsafe-html-B5ZEXaxg.js +1 -0
- package/dist/client/_assets/chunks/unsafe-svg-0QCkP0vZ.js +2 -0
- package/dist/client/_assets/client-GiYENVw8.css +2 -0
- package/dist/client/_assets/client-VnFxJN7G.js +295 -0
- package/dist/client/_assets/client-auth-k8gJ6QJj.js +828 -0
- package/dist/client/_assets/client-author-DQ8R-8KR.css +2 -0
- package/dist/client/_assets/client-compose-B_kDtWkW.js +2004 -0
- package/dist/client/_assets/client-manage-M90aSTOk.js +2170 -0
- package/dist/client/_assets/client-settings-DfYs9n1F.js +1112 -0
- package/dist/{export-Dl5KCiEs.js → github-sync-BPAvT999.js} +2254 -107
- package/dist/index.js +2 -5
- package/dist/node.js +5 -7
- package/package.json +3 -3
- package/src/__tests__/auth-session-freshness.test.ts +57 -0
- package/src/__tests__/basecoat-variants.test.ts +121 -0
- package/src/__tests__/bin/assets-upload.test.ts +68 -0
- package/src/__tests__/export-hugo-build.test.ts +260 -0
- package/src/__tests__/export-service.test.ts +231 -0
- package/src/__tests__/helpers/component-css.ts +91 -0
- package/src/__tests__/helpers/db.ts +33 -0
- package/src/__tests__/helpers/export-fixtures.ts +3 -0
- package/src/__tests__/stylesheet-audience.test.ts +159 -0
- package/src/__tests__/stylesheet-breakpoints.test.ts +183 -0
- package/src/app.tsx +71 -16
- package/src/auth.ts +18 -1
- package/src/client/__tests__/collection-page-actions.test.ts +51 -2
- package/src/client/__tests__/compose-bridge-image-upload.test.ts +196 -0
- package/src/client/__tests__/compose-bridge-video-poster.test.ts +159 -0
- package/src/client/__tests__/compose-bridge.test.ts +272 -0
- package/src/client/__tests__/compose-launch.test.ts +62 -1
- package/src/client/__tests__/compose-shortcuts.test.ts +38 -9
- package/src/client/__tests__/compose-triggers.test.ts +90 -0
- package/src/client/__tests__/copy-field.test.ts +162 -0
- package/src/client/__tests__/custom-url-menu.test.ts +127 -6
- package/src/client/__tests__/image-processor.test.ts +324 -2
- package/src/client/__tests__/lazy-entries.test.ts +92 -0
- package/src/client/__tests__/media-scroll-hint.test.ts +155 -0
- package/src/client/__tests__/runtime-paths.test.ts +84 -0
- package/src/client/__tests__/smart-collection-page-actions.test.ts +158 -0
- package/src/client/__tests__/toast.test.ts +23 -0
- package/src/client/__tests__/video-processor-probe.test.ts +175 -0
- package/src/client/__tests__/video-processor.test.ts +181 -0
- package/src/client/audio-processor.ts +24 -15
- package/src/client/audio-waveform.ts +49 -0
- package/src/client/collection-dialog-host.ts +72 -0
- package/src/client/collection-navigation.ts +51 -16
- package/src/client/collection-page-actions.ts +26 -0
- package/src/client/components/__tests__/jant-collection-dialog.test.ts +329 -0
- package/src/client/components/__tests__/jant-collection-directory.test.ts +274 -10
- package/src/client/components/__tests__/jant-collection-form.test.ts +59 -178
- package/src/client/components/__tests__/jant-command-palette.test.ts +28 -0
- package/src/client/components/__tests__/jant-compose-dialog-draft-storage.test.ts +340 -0
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +1250 -53
- package/src/client/components/__tests__/jant-compose-editor.test.ts +544 -40
- package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +0 -1
- package/src/client/components/__tests__/jant-locale-picker.test.ts +207 -0
- package/src/client/components/__tests__/jant-nav-manager.test.ts +234 -15
- package/src/client/components/__tests__/jant-post-menu.test.ts +413 -2
- package/src/client/components/__tests__/jant-post-picker.test.ts +197 -0
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +15 -12
- package/src/client/components/__tests__/jant-settings-general.test.ts +541 -172
- package/src/client/components/__tests__/jant-settings-language.test.ts +851 -0
- package/src/client/components/__tests__/jant-smart-collection-dialog.test.ts +665 -0
- package/src/client/components/collection-dialog-types.ts +49 -0
- package/src/client/components/collection-manager-types.ts +32 -1
- package/src/client/components/collection-types.ts +1 -15
- package/src/client/components/compose-types.ts +75 -1
- package/src/client/components/jant-collection-dialog.ts +722 -0
- package/src/client/components/jant-collection-directory.ts +567 -182
- package/src/client/components/jant-collection-form.ts +53 -232
- package/src/client/components/jant-command-palette.ts +100 -49
- package/src/client/components/jant-compose-dialog.ts +1486 -707
- package/src/client/components/jant-compose-editor.ts +673 -336
- package/src/client/components/jant-compose-fullscreen.ts +51 -43
- package/src/client/components/jant-config-editor.ts +65 -53
- package/src/client/components/jant-confirm-dialog.ts +13 -11
- package/src/client/components/jant-locale-picker.ts +418 -0
- package/src/client/components/jant-media-lightbox.ts +146 -136
- package/src/client/components/jant-nav-manager.ts +690 -366
- package/src/client/components/jant-post-menu.ts +1086 -282
- package/src/client/components/jant-post-picker.ts +324 -0
- package/src/client/components/jant-repo-picker.ts +138 -106
- package/src/client/components/jant-settings-avatar.ts +31 -27
- package/src/client/components/jant-settings-general.ts +617 -543
- package/src/client/components/jant-settings-language.ts +1087 -0
- package/src/client/components/jant-smart-collection-dialog.ts +1021 -0
- package/src/client/components/jant-text-preview.ts +94 -86
- package/src/client/components/nav-manager-types.ts +62 -1
- package/src/client/components/settings-types.ts +14 -29
- package/src/client/components/smart-collection-conditions.ts +285 -0
- package/src/client/components/smart-collection-dialog-types.ts +68 -0
- package/src/client/compose-bridge.ts +214 -141
- package/src/client/compose-launch.ts +76 -2
- package/src/client/compose-shortcuts.ts +6 -8
- package/src/client/compose-triggers.ts +50 -0
- package/src/client/copy-field.ts +108 -0
- package/src/client/custom-url-menu.ts +60 -4
- package/src/client/icons.ts +14 -0
- package/src/client/image-plan.ts +165 -0
- package/src/client/image-processor.ts +195 -117
- package/src/client/image-worker-client.ts +45 -0
- package/src/client/image-worker.ts +135 -0
- package/src/client/lazy-entries.ts +95 -0
- package/src/client/lazy-slugify.ts +2 -2
- package/src/client/media-scroll-hint.ts +43 -2
- package/src/client/mediabunny.ts +29 -0
- package/src/client/post-picker.ts +41 -0
- package/src/client/runtime-paths.ts +74 -1
- package/src/client/settings-bridge.ts +4 -10
- package/src/client/site-header-nav.js +18 -6
- package/src/client/smart-collection-dialog-host.ts +116 -0
- package/src/client/smart-collection-page-actions.ts +208 -0
- package/src/client/tiptap/__tests__/bubble-menu.test.ts +110 -0
- package/src/client/tiptap/__tests__/floating-position.test.ts +136 -1
- package/src/client/tiptap/__tests__/inline-image-upload.test.ts +37 -0
- package/src/client/tiptap/bubble-menu.ts +47 -16
- package/src/client/tiptap/floating-position.ts +216 -2
- package/src/client/tiptap/inline-image-upload.ts +7 -3
- package/src/client/tiptap/link-toolbar.ts +22 -9
- package/src/client/toast.ts +13 -0
- package/src/client/types/vite-worker.d.ts +12 -0
- package/src/client/upload-with-metadata.ts +3 -13
- package/src/client/video-processor.ts +351 -107
- package/src/client-auth.ts +14 -31
- package/src/client-compose.ts +19 -0
- package/src/client-manage.ts +13 -0
- package/src/client-settings.ts +14 -0
- package/src/client-site.ts +10 -3
- package/src/client.ts +4 -0
- package/src/db/__tests__/backfill-nav-label-mirrors.test.ts +149 -0
- package/src/db/__tests__/schema-enum-sources.test.ts +33 -0
- package/src/db/__tests__/smart-collection-placement.test.ts +402 -0
- package/src/db/backfills/0006_clear_mirrored_page_nav_labels.sql +31 -0
- package/src/db/migrations/0032_married_tomas.sql +3 -0
- package/src/db/migrations/0033_busy_johnny_storm.sql +198 -0
- package/src/db/migrations/0034_cheerful_rumiko_fujikawa.sql +35 -0
- package/src/db/migrations/meta/0032_snapshot.json +2582 -0
- package/src/db/migrations/meta/0033_snapshot.json +2816 -0
- package/src/db/migrations/meta/0034_snapshot.json +2816 -0
- package/src/db/migrations/meta/_journal.json +21 -0
- package/src/db/migrations/pg/0030_abandoned_nighthawk.sql +3 -0
- package/src/db/migrations/pg/0031_strong_ultimatum.sql +128 -0
- package/src/db/migrations/pg/0032_furry_multiple_man.sql +6 -0
- package/src/db/migrations/pg/meta/0030_snapshot.json +3329 -0
- package/src/db/migrations/pg/meta/0031_snapshot.json +3660 -0
- package/src/db/migrations/pg/meta/0032_snapshot.json +3660 -0
- package/src/db/migrations/pg/meta/_journal.json +21 -0
- package/src/db/pg/schema.ts +195 -27
- package/src/db/post-visibility.ts +117 -0
- package/src/db/schema.ts +222 -27
- package/src/i18n/__tests__/detect.test.ts +26 -81
- package/src/i18n/__tests__/supported-locales.test.ts +83 -0
- package/src/i18n/context.tsx +12 -2
- package/src/i18n/detect.ts +7 -66
- package/src/i18n/locales/glossary.zh-Hans.yml +44 -4
- package/src/i18n/locales/glossary.zh-Hant.yml +44 -4
- package/src/i18n/locales/public/en.po +490 -158
- package/src/i18n/locales/public/en.ts +1 -1
- package/src/i18n/locales/public/zh-Hans.po +487 -155
- package/src/i18n/locales/public/zh-Hans.ts +1 -1
- package/src/i18n/locales/public/zh-Hant.po +487 -155
- package/src/i18n/locales/public/zh-Hant.ts +1 -1
- package/src/i18n/locales/settings/en.po +551 -69
- package/src/i18n/locales/settings/en.ts +1 -1
- package/src/i18n/locales/settings/zh-Hans.po +597 -115
- package/src/i18n/locales/settings/zh-Hans.ts +1 -1
- package/src/i18n/locales/settings/zh-Hant.po +610 -128
- package/src/i18n/locales/settings/zh-Hant.ts +1 -1
- package/src/i18n/locales.ts +94 -0
- package/src/i18n/middleware.ts +12 -2
- package/src/i18n/supported-locales.ts +80 -0
- package/src/lib/__tests__/api-settings.test.ts +11 -2
- package/src/lib/__tests__/asset-path.test.ts +92 -0
- package/src/lib/__tests__/deferred.test.ts +90 -0
- package/src/lib/__tests__/discover-ping.test.ts +89 -0
- package/src/lib/__tests__/discover.test.ts +410 -0
- package/src/lib/__tests__/feed-policy.test.ts +26 -0
- package/src/lib/__tests__/feed.test.ts +1526 -15
- package/src/lib/__tests__/filter-dimensions.test.ts +341 -0
- package/src/lib/__tests__/hosted-signin.test.ts +1 -1
- package/src/lib/__tests__/http-cache.test.ts +162 -0
- package/src/lib/__tests__/jant-docs-dev.test.ts +21 -0
- package/src/lib/__tests__/jant-docs.test.ts +23 -0
- package/src/lib/__tests__/lang-detect.test.ts +231 -0
- package/src/lib/__tests__/media-helpers.test.ts +81 -0
- package/src/lib/__tests__/navigation.test.ts +135 -1
- package/src/lib/__tests__/resolve-config.test.ts +64 -3
- package/src/lib/__tests__/schemas.test.ts +42 -4
- package/src/lib/__tests__/site-unavailable-page.test.ts +25 -0
- package/src/lib/__tests__/slug.test.ts +70 -0
- package/src/lib/__tests__/slugify.test.ts +85 -0
- package/src/lib/__tests__/storage.test.ts +32 -0
- package/src/lib/__tests__/thread-fold.test.ts +113 -0
- package/src/lib/__tests__/upload-sideload.test.ts +14 -0
- package/src/lib/__tests__/url-fetch.test.ts +1 -2
- package/src/lib/__tests__/url.test.ts +78 -72
- package/src/lib/__tests__/view-language.test.ts +413 -0
- package/src/lib/__tests__/worker-response-cache.test.ts +90 -0
- package/src/lib/address.ts +57 -0
- package/src/lib/api-posts.ts +12 -0
- package/src/lib/asset-path.ts +98 -1
- package/src/lib/build-env.ts +16 -0
- package/src/lib/collection-groups.ts +5 -1
- package/src/lib/collection-paths.ts +3 -10
- package/src/lib/constants.ts +28 -3
- package/src/lib/copy-field.ts +36 -0
- package/src/lib/crypto.ts +40 -0
- package/src/lib/csp-builder.ts +2 -1
- package/src/lib/deferred.ts +79 -0
- package/src/lib/discover-ping.ts +141 -0
- package/src/lib/discover.ts +437 -0
- package/src/lib/env.ts +134 -0
- package/src/lib/errors.ts +33 -0
- package/src/lib/feed-policy.ts +236 -2
- package/src/lib/feed.ts +926 -73
- package/src/lib/filter-dimensions.ts +1349 -0
- package/src/lib/github-sync-site-config.ts +3 -0
- package/src/lib/hosted-signin.ts +1 -1
- package/src/lib/http-cache.ts +148 -0
- package/src/lib/hugo-markdown.ts +16 -0
- package/src/lib/icons.ts +9 -2
- package/src/lib/ids.ts +3 -0
- package/src/lib/jant-docs.ts +42 -0
- package/src/lib/lang-detect.ts +308 -0
- package/src/lib/media-helpers.ts +13 -1
- package/src/lib/navigation.ts +78 -11
- package/src/lib/pagination.ts +28 -24
- package/src/lib/per-language-surfaces.ts +67 -0
- package/src/lib/post-meta.ts +19 -0
- package/src/lib/render.tsx +55 -7
- package/src/lib/resolve-config.ts +53 -5
- package/src/lib/schemas.ts +201 -10
- package/src/lib/site-header-fragment.tsx +2 -1
- package/src/lib/site-unavailable-page.ts +47 -0
- package/src/lib/slug.ts +51 -7
- package/src/lib/slugify.ts +113 -0
- package/src/lib/startup-config.ts +4 -6
- package/src/lib/storage.ts +7 -0
- package/src/lib/summary.ts +50 -0
- package/src/lib/thread-fold.ts +161 -0
- package/src/lib/time.ts +32 -0
- package/src/lib/timeline.ts +42 -1
- package/src/lib/tiptap-render.ts +1 -2
- package/src/lib/upload.ts +16 -13
- package/src/lib/url.ts +139 -86
- package/src/lib/version.ts +10 -8
- package/src/lib/view-language.ts +436 -0
- package/src/lib/view.ts +51 -23
- package/src/lib/viewer-context.ts +62 -0
- package/src/middleware/__tests__/auth.test.ts +70 -2
- package/src/middleware/__tests__/cache-control.test.ts +71 -3
- package/src/middleware/__tests__/onboarding.test.ts +76 -5
- package/src/middleware/__tests__/secure-headers.test.ts +1 -1
- package/src/middleware/__tests__/session.test.ts +181 -33
- package/src/middleware/auth.ts +50 -31
- package/src/middleware/cache-control.ts +75 -10
- package/src/middleware/config.ts +6 -6
- package/src/middleware/error-handler.ts +12 -0
- package/src/middleware/onboarding.ts +32 -10
- package/src/middleware/secure-headers.ts +1 -1
- package/src/middleware/session.ts +96 -8
- package/src/node/__tests__/cli-migrate.test.ts +21 -19
- package/src/node/__tests__/cli-reset-password.test.ts +81 -8
- package/src/node/__tests__/cli-runtime-target.test.ts +48 -1
- package/src/node/__tests__/cli-site-selection.test.ts +128 -38
- package/src/node/__tests__/runtime.test.ts +37 -0
- package/src/node/index.ts +1 -0
- package/src/preset.css +76 -22
- package/src/routes/__tests__/feed-validator.test.ts +156 -0
- package/src/routes/api/__tests__/collections.test.ts +114 -0
- package/src/routes/api/__tests__/discover.test.ts +222 -0
- package/src/routes/api/__tests__/nav-items.test.ts +212 -2
- package/src/routes/api/__tests__/post-translations.test.ts +829 -0
- package/src/routes/api/__tests__/settings.test.ts +24 -3
- package/src/routes/api/__tests__/telegram.test.ts +6 -5
- package/src/routes/api/collections.ts +49 -6
- package/src/routes/api/custom-urls.ts +0 -1
- package/src/routes/api/discover.ts +95 -0
- package/src/routes/api/export.ts +3 -0
- package/src/routes/api/internal/__tests__/sites.test.ts +42 -2
- package/src/routes/api/nav-items.ts +55 -8
- package/src/routes/api/posts.ts +159 -0
- package/src/routes/api/public/__tests__/archive.test.ts +111 -0
- package/src/routes/api/public/archive.ts +77 -81
- package/src/routes/api/public/posts.ts +23 -6
- package/src/routes/api/smart-collections.ts +132 -0
- package/src/routes/api/telegram.ts +2 -40
- package/src/routes/api/uploads.ts +1 -2
- package/src/routes/auth/__tests__/setup-auth.test.ts +137 -0
- package/src/routes/auth/__tests__/setup-discover.test.ts +204 -0
- package/src/routes/auth/__tests__/setup-flow.test.ts +270 -0
- package/src/routes/auth/__tests__/setup-page.test.tsx +329 -0
- package/src/routes/auth/__tests__/setup-steps.test.ts +310 -0
- package/src/routes/auth/__tests__/setup.test.ts +278 -26
- package/src/routes/auth/__tests__/signin.test.ts +135 -0
- package/src/routes/auth/setup.tsx +934 -186
- package/src/routes/auth/signin.tsx +10 -8
- package/src/routes/compose.tsx +5 -0
- package/src/routes/dash/__tests__/custom-urls-archive.test.ts +44 -0
- package/src/routes/dash/__tests__/custom-urls-page.test.ts +49 -0
- package/src/routes/dash/__tests__/delete-account.test.ts +106 -0
- package/src/routes/dash/__tests__/settings-discover.test.ts +213 -0
- package/src/routes/dash/custom-urls.tsx +1 -37
- package/src/routes/dash/settings.tsx +502 -41
- package/src/routes/discover-announce.ts +96 -0
- package/src/routes/feed/__tests__/feed.test.ts +165 -2
- package/src/routes/feed/feed.ts +69 -40
- package/src/routes/feed/sitemap.ts +95 -11
- package/src/routes/pages/__tests__/archive-indexing.test.ts +200 -0
- package/src/routes/pages/__tests__/archive-params.test.ts +370 -6
- package/src/routes/pages/__tests__/collection-routing.test.ts +16 -0
- package/src/routes/pages/__tests__/collections-feed-links.test.ts +72 -0
- package/src/routes/pages/__tests__/collections.test.ts +9 -3
- package/src/routes/pages/__tests__/custom-archive-url.test.ts +119 -0
- package/src/routes/pages/__tests__/feed-autodiscovery.test.ts +148 -0
- package/src/routes/pages/__tests__/language-routing.test.ts +679 -0
- package/src/routes/pages/__tests__/post-preview-partial.test.ts +96 -0
- package/src/routes/pages/__tests__/smart-collection.test.ts +298 -0
- package/src/routes/pages/__tests__/subscribe.test.ts +248 -0
- package/src/routes/pages/archive.tsx +471 -369
- package/src/routes/pages/collection.tsx +94 -96
- package/src/routes/pages/collections.tsx +38 -50
- package/src/routes/pages/featured.tsx +32 -13
- package/src/routes/pages/home.tsx +25 -6
- package/src/routes/pages/language.tsx +113 -0
- package/src/routes/pages/latest.tsx +26 -16
- package/src/routes/pages/new.tsx +3 -0
- package/src/routes/pages/page.tsx +178 -17
- package/src/routes/pages/partials.tsx +41 -0
- package/src/routes/pages/search.tsx +18 -3
- package/src/routes/pages/smart-collection.tsx +340 -0
- package/src/routes/pages/subscribe.tsx +182 -0
- package/src/runtime/__tests__/index.test.ts +19 -0
- package/src/runtime/__tests__/node.test.ts +129 -4
- package/src/runtime/cloudflare.ts +21 -2
- package/src/runtime/index.ts +27 -1
- package/src/runtime/node.ts +18 -1
- package/src/runtime/site.ts +95 -24
- package/src/services/__tests__/auth.test.ts +116 -7
- package/src/services/__tests__/collection.test.ts +108 -4
- package/src/services/__tests__/language.test.ts +495 -0
- package/src/services/__tests__/navigation.test.ts +213 -10
- package/src/services/__tests__/path.test.ts +180 -0
- package/src/services/__tests__/post-language.test.ts +830 -0
- package/src/services/__tests__/post.test.ts +46 -9
- package/src/services/__tests__/settings.test.ts +228 -17
- package/src/services/__tests__/site-admin.test.ts +5 -5
- package/src/services/__tests__/smart-collection.test.ts +651 -0
- package/src/services/about-page.ts +22 -1
- package/src/services/auth.ts +51 -49
- package/src/services/bootstrap.ts +188 -38
- package/src/services/collection-directory-position.ts +76 -0
- package/src/services/collection.ts +331 -46
- package/src/services/custom-url.ts +27 -22
- package/src/services/export-theme/assets/client-site.css +1 -1
- package/src/services/export-theme/assets/client-site.js +123 -123
- package/src/services/export-theme/layouts/_default/rss.xml +77 -1
- package/src/services/export-theme/layouts/partials/feed-post-content.xml +105 -2
- package/src/services/export-theme/layouts/partials/media-gallery.html +35 -21
- package/src/services/export-theme/layouts/partials/pagination.html +14 -13
- package/src/services/export-theme/layouts/partials/post-card.html +8 -4
- package/src/services/export-theme/layouts/partials/reply.html +2 -1
- package/src/services/export-theme/layouts/partials/thread-preview.html +40 -30
- package/src/services/export-theme/styles/main.css +13 -1
- package/src/services/export.ts +278 -19
- package/src/services/index.ts +34 -3
- package/src/services/language.ts +412 -0
- package/src/services/mcp.ts +3 -1
- package/src/services/media.ts +14 -6
- package/src/services/navigation.ts +429 -67
- package/src/services/path.ts +221 -6
- package/src/services/post.ts +1021 -152
- package/src/services/search.ts +39 -2
- package/src/services/settings.ts +238 -43
- package/src/services/site-admin.ts +24 -7
- package/src/services/site.ts +77 -1
- package/src/services/smart-collection.ts +636 -0
- package/src/services/telegram.ts +20 -22
- package/src/services/upload-session.ts +1 -3
- package/src/style-author.css +27 -0
- package/src/styles/components-author.css +1622 -0
- package/src/styles/components.css +24 -1571
- package/src/styles/site-media.css +16 -2
- package/src/styles/tokens.css +82 -12
- package/src/styles/ui-author.css +7602 -0
- package/src/styles/ui.css +1087 -7219
- package/src/types/app-context.ts +7 -0
- package/src/types/bindings.ts +3 -0
- package/src/types/config.ts +94 -15
- package/src/types/constants.ts +188 -16
- package/src/types/entities.ts +87 -1
- package/src/types/operations.ts +64 -2
- package/src/types/props.ts +157 -31
- package/src/types/views.ts +55 -1
- package/src/ui/__tests__/font-themes.test.ts +52 -12
- package/src/ui/compose/ComposeDialog.tsx +201 -7
- package/src/ui/compose/ComposePrompt.tsx +1 -5
- package/src/ui/compose/__tests__/ComposeDialog.test.tsx +125 -0
- package/src/ui/dash/appearance/AdvancedContent.tsx +2 -2
- package/src/ui/dash/appearance/CodeInjectionContent.tsx +12 -16
- package/src/ui/dash/appearance/NavigationContent.tsx +117 -16
- package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +33 -11
- package/src/ui/dash/appearance/__tests__/system-nav-descriptions.test.ts +44 -0
- package/src/ui/dash/appearance/system-nav-descriptions.ts +75 -0
- package/src/ui/dash/settings/AccountMenuContent.tsx +2 -2
- package/src/ui/dash/settings/ApiTokensContent.tsx +2 -1
- package/src/ui/dash/settings/ConfigEditorContent.tsx +35 -16
- package/src/ui/dash/settings/GeneralContent.tsx +275 -113
- package/src/ui/dash/settings/LanguageContent.tsx +409 -0
- package/src/ui/dash/settings/SettingsRootContent.tsx +19 -1
- package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +324 -4
- package/src/ui/dash/settings/__tests__/LanguageContent.test.tsx +144 -0
- package/src/ui/dash/settings/__tests__/discover-copy.test.ts +66 -0
- package/src/ui/dash/settings/discover-copy.ts +116 -0
- package/src/ui/feed/LinkCard.tsx +55 -45
- package/src/ui/feed/NoteCard.tsx +9 -4
- package/src/ui/feed/PostStatusBadges.tsx +90 -43
- package/src/ui/feed/QuoteCard.tsx +55 -30
- package/src/ui/feed/ThreadPreview.tsx +6 -2
- package/src/ui/feed/__tests__/thread-preview.test.ts +13 -16
- package/src/ui/feed/__tests__/timeline-cards.test.ts +314 -49
- package/src/ui/feed/thread-preview-state.ts +20 -6
- package/src/ui/font-themes.ts +39 -29
- package/src/ui/layouts/BaseLayout.tsx +155 -10
- package/src/ui/layouts/SiteLayout.tsx +192 -34
- package/src/ui/layouts/__tests__/BaseLayout.test.tsx +196 -17
- package/src/ui/layouts/__tests__/SiteLayout.test.tsx +109 -85
- package/src/ui/pages/ArchivePage.tsx +282 -289
- package/src/ui/pages/CollectionPage.tsx +86 -76
- package/src/ui/pages/CollectionsPage.tsx +5 -0
- package/src/ui/pages/ComposePage.tsx +8 -0
- package/src/ui/pages/PostPage.tsx +47 -0
- package/src/ui/pages/SearchPage.tsx +3 -3
- package/src/ui/pages/SmartCollectionPage.tsx +434 -0
- package/src/ui/pages/SubscribePage.tsx +236 -0
- package/src/ui/pages/__tests__/ArchivePage.test.tsx +65 -9
- package/src/ui/pages/__tests__/collections-directory-rows.test.tsx +92 -0
- package/src/ui/pages/__tests__/collections-view-links.test.tsx +81 -0
- package/src/ui/shared/CollectionDirectory.tsx +178 -3
- package/src/ui/shared/CollectionsManager.tsx +70 -17
- package/src/ui/shared/CopyField.tsx +127 -0
- package/src/ui/shared/EmptyPostContent.tsx +23 -0
- package/src/ui/shared/MediaGallery.tsx +125 -46
- package/src/ui/shared/Pagination.tsx +20 -5
- package/src/ui/shared/PostFooter.tsx +20 -11
- package/src/ui/shared/__tests__/archive-labels.test.ts +100 -0
- package/src/ui/shared/__tests__/media-gallery.test.ts +210 -2
- package/src/ui/shared/__tests__/navigation-labels.test.ts +16 -22
- package/src/ui/shared/__tests__/pagination.test.ts +38 -24
- package/src/ui/shared/__tests__/post-footer.test.ts +32 -0
- package/src/ui/shared/archive-labels.ts +98 -0
- package/src/ui/shared/collection-management-labels.ts +94 -16
- package/src/ui/shared/navigation-labels.ts +45 -71
- package/src/ui/shared/post-article-attributes.ts +2 -0
- package/src/ui/shared/smart-collection-labels.ts +343 -0
- package/dist/app-C75Zc8dD.js +0 -6
- package/dist/client/_assets/chunks/heic-to-DUUaO23q.js +0 -1
- package/dist/client/_assets/chunks/module-DcsAZQZ_.js +0 -716
- package/dist/client/_assets/chunks/url-CU9pEbk5.js +0 -1
- package/dist/client/_assets/client-Bq0vre8Y.js +0 -296
- package/dist/client/_assets/client-D3VZyG4L.css +0 -2
- package/dist/client/_assets/client-auth-F2rb9WfF.js +0 -5571
- package/dist/env-BPYViDJJ.js +0 -281
- package/dist/github-api-BNF35Lkx.js +0 -176
- package/dist/github-app-DdiD-bC4.js +0 -275
- package/dist/github-sync-Bqg62IvV.js +0 -4
- package/dist/github-sync-HdK2EgvJ.js +0 -431
- package/dist/url-CbLAtlZe.js +0 -900
- package/src/client/__tests__/collection-created-notice.test.ts +0 -36
- package/src/client/__tests__/collection-form-bridge.test.ts +0 -216
- package/src/client/__tests__/post-form-bridge.test.ts +0 -82
- package/src/client/collection-created-notice.ts +0 -66
- package/src/client/collection-form-bridge.ts +0 -191
- package/src/client/components/__tests__/jant-post-form.test.ts +0 -193
- package/src/client/components/jant-post-form.ts +0 -422
- package/src/client/components/post-form-template.ts +0 -352
- package/src/client/components/post-form-types.ts +0 -96
- package/src/client/media-metadata.ts +0 -247
- package/src/client/post-form-bridge.ts +0 -193
- package/src/ui/pages/CollectionEditorPage.tsx +0 -175
package/src/types/app-context.ts
CHANGED
|
@@ -53,6 +53,13 @@ export interface AppVariables {
|
|
|
53
53
|
* `c.var.rateLimiter.check(...)` instead of caring which impl is used.
|
|
54
54
|
*/
|
|
55
55
|
rateLimiter: RateLimiter;
|
|
56
|
+
/**
|
|
57
|
+
* Language whose browsing view is being served, set by the language routes
|
|
58
|
+
* when the URL carries a language prefix (`/en/archive`). Undefined on the
|
|
59
|
+
* root view, which shows the primary language — or every language, when
|
|
60
|
+
* multilingual content is off.
|
|
61
|
+
*/
|
|
62
|
+
viewLang?: string;
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
export type App = Hono<{ Bindings: Bindings; Variables: AppVariables }>;
|
package/src/types/bindings.ts
CHANGED
|
@@ -37,11 +37,14 @@ export interface Bindings {
|
|
|
37
37
|
SITE_LANGUAGE?: EnvBindingValue;
|
|
38
38
|
DASHBOARD_LANGUAGE?: EnvBindingValue;
|
|
39
39
|
MAIN_RSS_FEED?: EnvBindingValue;
|
|
40
|
+
ARCHIVE_DEFAULT_LAYOUT?: EnvBindingValue;
|
|
40
41
|
TIME_ZONE?: EnvBindingValue;
|
|
41
42
|
SITE_FOOTER?: EnvBindingValue;
|
|
42
43
|
NOINDEX?: EnvBindingValue;
|
|
43
44
|
PUBLIC_API_ENABLED?: EnvBindingValue;
|
|
44
45
|
RSS_FEEDS_ENABLED?: EnvBindingValue;
|
|
46
|
+
DISCOVER?: EnvBindingValue;
|
|
47
|
+
DISCOVER_PING_URL?: EnvBindingValue;
|
|
45
48
|
R2_PUBLIC_URL?: EnvBindingValue;
|
|
46
49
|
IMAGE_TRANSFORM_URL?: EnvBindingValue;
|
|
47
50
|
DEMO_EMAIL?: EnvBindingValue;
|
package/src/types/config.ts
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* Single Source of Truth for all configuration fields.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import type { DiscoverMode } from "../lib/discover.js";
|
|
7
8
|
import {
|
|
8
9
|
MAX_SITE_DESCRIPTION_LENGTH,
|
|
9
10
|
MAX_SITE_FOOTER_LENGTH,
|
|
10
11
|
MAX_SITE_NAME_LENGTH,
|
|
12
|
+
type ArchiveLayout,
|
|
11
13
|
type FeedKind,
|
|
12
14
|
} from "./constants.js";
|
|
13
15
|
|
|
@@ -125,13 +127,37 @@ export const CONFIG_FIELDS = {
|
|
|
125
127
|
options: ["", "en", "zh-Hans", "zh-Hant"],
|
|
126
128
|
},
|
|
127
129
|
},
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
// Multilingual content. Both keys are deliberately DB-only:
|
|
131
|
+
//
|
|
132
|
+
// - No `envKeys`: an env var could flip multilingual on without the confirm
|
|
133
|
+
// step that stamps existing posts with the primary language, and every
|
|
134
|
+
// unstamped post would vanish from the root view. Adding a language through
|
|
135
|
+
// env would likewise skip the URL-prefix conflict check.
|
|
136
|
+
// - No `editor`: `PUT /api/settings` and the generic Config Editor both gate
|
|
137
|
+
// on `"editor" in field`, so omitting it leaves exactly one writer — the
|
|
138
|
+
// language service, which owns the ordering these settings depend on.
|
|
139
|
+
//
|
|
140
|
+
// `configEditorLink` keeps them findable in settings search without making
|
|
141
|
+
// them directly writable.
|
|
142
|
+
MULTILINGUAL_ENABLED: {
|
|
143
|
+
defaultValue: "false",
|
|
130
144
|
envOnly: false,
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
145
|
+
configEditorLink: {
|
|
146
|
+
type: "boolean",
|
|
147
|
+
settingsPath: "/settings/language",
|
|
148
|
+
display: "value",
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
// Comma-separated canonical BCP 47 tags, in switcher order. Never contains
|
|
152
|
+
// the primary language. Comma-separated rather than JSON because tags cannot
|
|
153
|
+
// contain commas: no escaping, readable in the settings table, order-preserving.
|
|
154
|
+
ADDITIONAL_LANGUAGES: {
|
|
155
|
+
defaultValue: "",
|
|
156
|
+
envOnly: false,
|
|
157
|
+
configEditorLink: {
|
|
158
|
+
type: "string",
|
|
159
|
+
settingsPath: "/settings/language",
|
|
160
|
+
display: "configured",
|
|
135
161
|
},
|
|
136
162
|
},
|
|
137
163
|
MAIN_RSS_FEED: {
|
|
@@ -140,6 +166,15 @@ export const CONFIG_FIELDS = {
|
|
|
140
166
|
envKeys: ["MAIN_RSS_FEED"],
|
|
141
167
|
editor: { type: "enum", options: ["featured", "latest"] },
|
|
142
168
|
},
|
|
169
|
+
// The archive is the widest of Featured / Latest / All, and the other two
|
|
170
|
+
// render as timelines. `list` keeps the trio consistent; `grid` turns the
|
|
171
|
+
// page back into a tile catalogue.
|
|
172
|
+
ARCHIVE_DEFAULT_LAYOUT: {
|
|
173
|
+
defaultValue: "list",
|
|
174
|
+
envOnly: false,
|
|
175
|
+
envKeys: ["ARCHIVE_DEFAULT_LAYOUT"],
|
|
176
|
+
editor: { type: "enum", options: ["list", "grid"] },
|
|
177
|
+
},
|
|
143
178
|
// Environment-only (deployment/infrastructure config)
|
|
144
179
|
DEFAULT_THEME: {
|
|
145
180
|
defaultValue: "tufte",
|
|
@@ -192,7 +227,7 @@ export const CONFIG_FIELDS = {
|
|
|
192
227
|
envKeys: ["DEMO_MODE"],
|
|
193
228
|
},
|
|
194
229
|
PAGE_SIZE: {
|
|
195
|
-
defaultValue: "
|
|
230
|
+
defaultValue: "25",
|
|
196
231
|
envOnly: false,
|
|
197
232
|
envKeys: ["PAGE_SIZE"],
|
|
198
233
|
editor: { type: "number", min: 1, max: 100, step: 1 },
|
|
@@ -432,6 +467,30 @@ export const CONFIG_FIELDS = {
|
|
|
432
467
|
envKeys: ["RSS_FEEDS_ENABLED"],
|
|
433
468
|
editor: { type: "boolean" },
|
|
434
469
|
},
|
|
470
|
+
// No default value, and deliberately no `editor`. "Never chosen" has to stay
|
|
471
|
+
// tellable apart from an explicit choice — an unset site falls through to
|
|
472
|
+
// `noindex` and then to the deployment default, an explicit one does not —
|
|
473
|
+
// and Config Editor has no way to show a field whose default is "unset": it
|
|
474
|
+
// requires every editable field to resolve to a concrete valid value. The
|
|
475
|
+
// effective mode is derived in `lib/discover.ts`, and the checkbox in
|
|
476
|
+
// settings/general is where this is edited.
|
|
477
|
+
//
|
|
478
|
+
// The binding is read as this deployment's default rather than as the
|
|
479
|
+
// site's answer, which is how hosted Jant lists its fleet while a
|
|
480
|
+
// self-hosted site, with nothing set anywhere, stays out until asked.
|
|
481
|
+
DISCOVER: {
|
|
482
|
+
defaultValue: "",
|
|
483
|
+
envOnly: false,
|
|
484
|
+
envKeys: ["DISCOVER"],
|
|
485
|
+
},
|
|
486
|
+
// What the last announcement to the directory came to. State, not
|
|
487
|
+
// configuration — nobody edits this — so it is internal like
|
|
488
|
+
// `ONBOARDING_STATUS`. Shape and reasoning in `lib/discover-ping.ts`.
|
|
489
|
+
DISCOVER_ANNOUNCE_STATE: {
|
|
490
|
+
defaultValue: "",
|
|
491
|
+
envOnly: false,
|
|
492
|
+
internal: true,
|
|
493
|
+
},
|
|
435
494
|
DISCOVERY_COMPOSE_OPEN_SHORTCUT_AT: {
|
|
436
495
|
defaultValue: "",
|
|
437
496
|
envOnly: false,
|
|
@@ -452,11 +511,6 @@ export const CONFIG_FIELDS = {
|
|
|
452
511
|
envOnly: false,
|
|
453
512
|
internal: true,
|
|
454
513
|
},
|
|
455
|
-
DELETE_CSRF_TOKEN: {
|
|
456
|
-
defaultValue: "",
|
|
457
|
-
envOnly: false,
|
|
458
|
-
internal: true,
|
|
459
|
-
},
|
|
460
514
|
|
|
461
515
|
// GitHub Sync (DB-only, managed via GitHub Sync settings page)
|
|
462
516
|
GITHUB_SYNC_ENABLED: {
|
|
@@ -683,6 +737,10 @@ export interface AppConfig {
|
|
|
683
737
|
siteDescription: string;
|
|
684
738
|
/** true only when description is set in DB or ENV (not just the default) */
|
|
685
739
|
siteDescriptionExplicit: boolean;
|
|
740
|
+
/**
|
|
741
|
+
* The site's content language, and — once multilingual content is on — its
|
|
742
|
+
* primary language: the one served from the unprefixed root URLs.
|
|
743
|
+
*/
|
|
686
744
|
siteLanguage: string;
|
|
687
745
|
/**
|
|
688
746
|
* Admin dashboard UI locale. Empty string means "follow the content
|
|
@@ -690,14 +748,29 @@ export interface AppConfig {
|
|
|
690
748
|
* the translated catalog locales ("en", "zh-Hans", "zh-Hant").
|
|
691
749
|
*/
|
|
692
750
|
dashboardLanguage: string;
|
|
693
|
-
/**
|
|
694
|
-
|
|
751
|
+
/** Whether the site serves per-language browsing views. */
|
|
752
|
+
multilingualEnabled: boolean;
|
|
753
|
+
/**
|
|
754
|
+
* Canonical BCP 47 tags served under a URL prefix, in switcher order. Never
|
|
755
|
+
* includes `siteLanguage`. Retained when multilingual is switched off so
|
|
756
|
+
* turning it back on restores the same setup — and so the old prefixes can
|
|
757
|
+
* still redirect rather than 404.
|
|
758
|
+
*/
|
|
759
|
+
additionalLanguages: string[];
|
|
695
760
|
mainRssFeed: FeedKind;
|
|
761
|
+
/** Layout `/archive` uses when the URL names no explicit layout. */
|
|
762
|
+
archiveDefaultLayout: ArchiveLayout;
|
|
696
763
|
/** Canonical IANA timezone identifier used for date/time display. */
|
|
697
764
|
timeZone: string;
|
|
698
765
|
siteFooter: string;
|
|
699
766
|
showJantBrandingOnHome: boolean;
|
|
700
767
|
noindex: boolean;
|
|
768
|
+
/**
|
|
769
|
+
* Effective Jant Discover mode this site's feeds declare. Derived from the
|
|
770
|
+
* `DISCOVER` setting plus the demo, `noindex`, and feeds-enabled rules in
|
|
771
|
+
* `lib/discover.ts` — never the stored value on its own.
|
|
772
|
+
*/
|
|
773
|
+
discover: DiscoverMode;
|
|
701
774
|
/** Whether published content can be read from JSON APIs without authentication. */
|
|
702
775
|
publicApiEnabled: boolean;
|
|
703
776
|
/** Whether the site publishes its Atom feed endpoints. */
|
|
@@ -757,8 +830,14 @@ export interface AppConfig {
|
|
|
757
830
|
// Site appearance (DB internal)
|
|
758
831
|
siteAvatar: string;
|
|
759
832
|
showHeaderAvatar: boolean;
|
|
760
|
-
/** Derived: getMediaUrl(siteAvatar, publicUrl)
|
|
833
|
+
/** Derived: getMediaUrl(siteAvatar, publicUrl) — full resolution, for the
|
|
834
|
+
* social card, the settings editor, and anything that syncs the canonical
|
|
835
|
+
* asset. */
|
|
761
836
|
siteAvatarUrl: string;
|
|
837
|
+
/** Derived: `siteAvatarUrl` sized for the header and drawer marks, which
|
|
838
|
+
* render it at 32px and 24px. Falls back to `siteAvatarUrl` when no image
|
|
839
|
+
* transform is configured. */
|
|
840
|
+
siteAvatarThumbUrl: string;
|
|
762
841
|
faviconVersion: string;
|
|
763
842
|
|
|
764
843
|
// Rate limiting (ENV only)
|
package/src/types/constants.ts
CHANGED
|
@@ -14,22 +14,102 @@ export type Visibility = (typeof VISIBILITIES)[number];
|
|
|
14
14
|
export const FEED_KINDS = ["latest", "featured"] as const;
|
|
15
15
|
export type FeedKind = (typeof FEED_KINDS)[number];
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Layouts the archive page can render in. `list` renders the same timeline the
|
|
19
|
+
* rest of the nav trio uses; `grid` renders the tile catalogue.
|
|
20
|
+
*
|
|
21
|
+
* Named "layout", not "view": on the archive, `view` is reserved for the
|
|
22
|
+
* saved-selection concept, and the URL param follows the same split
|
|
23
|
+
* (`?layout=grid`, with `?view=` kept only as the legacy spelling).
|
|
24
|
+
*/
|
|
25
|
+
export const ARCHIVE_LAYOUTS = ["list", "grid"] as const;
|
|
26
|
+
export type ArchiveLayout = (typeof ARCHIVE_LAYOUTS)[number];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Visibility values a signed-out reader may select on the archive.
|
|
30
|
+
*
|
|
31
|
+
* `featured` is a virtual member: it is a separate flag rather than a stored
|
|
32
|
+
* visibility, and it lives here because the filter bar presents the two as one
|
|
33
|
+
* dimension.
|
|
34
|
+
*
|
|
35
|
+
* `private` is deliberately absent. It names a set only the author can see, so
|
|
36
|
+
* a reader who asks for it has to be told so — handing back a different set
|
|
37
|
+
* under the same name is the one answer that is never right.
|
|
38
|
+
*/
|
|
39
|
+
export const PUBLIC_ARCHIVE_VISIBILITIES = [
|
|
40
|
+
"public",
|
|
41
|
+
"featured",
|
|
42
|
+
"latest_hidden",
|
|
22
43
|
] as const;
|
|
23
|
-
export type SortOrder = (typeof SORT_ORDERS)[number];
|
|
24
44
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
45
|
+
/** Every visibility the author can select, in filter-bar order. */
|
|
46
|
+
export const ARCHIVE_VISIBILITIES = [
|
|
47
|
+
...PUBLIC_ARCHIVE_VISIBILITIES,
|
|
48
|
+
"private",
|
|
29
49
|
] as const;
|
|
30
|
-
export type CollectionSortOrder = (typeof COLLECTION_SORT_ORDERS)[number];
|
|
31
50
|
|
|
32
|
-
export
|
|
51
|
+
export type ArchiveVisibility = (typeof ARCHIVE_VISIBILITIES)[number];
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The range a `year` filter may name, inclusive at both ends.
|
|
55
|
+
*
|
|
56
|
+
* Unix timestamps start in 1970, so anything at or below it is a parse failure
|
|
57
|
+
* rather than a very old archive.
|
|
58
|
+
*
|
|
59
|
+
* The ceiling is not pedantry. A year is translated into a pair of bounds with
|
|
60
|
+
* `Date.UTC`, which returns `NaN` past year 275760 — and a `NaN` bound is a
|
|
61
|
+
* comparison every row silently fails, so the page renders empty with nothing
|
|
62
|
+
* to explain it. Four digits is the largest year a post can honestly carry, and
|
|
63
|
+
* it is bounded in three places at once: the URL parser, the stored condition's
|
|
64
|
+
* validator, and the table CHECK in both dialects.
|
|
65
|
+
*/
|
|
66
|
+
export const EARLIEST_FILTERABLE_YEAR = 1971;
|
|
67
|
+
export const LATEST_FILTERABLE_YEAR = 9999;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* How a chronological query orders its results.
|
|
71
|
+
*
|
|
72
|
+
* `newest` and `oldest` read whichever time axis `PostFilters.sortBy` selects,
|
|
73
|
+
* so "newest" on the activity axis means "last gained a post" and on the
|
|
74
|
+
* published axis means "published last".
|
|
75
|
+
*
|
|
76
|
+
* Not to be confused with the archive's `?sort=`, which names a *time axis*
|
|
77
|
+
* (`published` / `updated`) and always runs newest-first. Two vocabularies, and
|
|
78
|
+
* mixing them is how month headers stop agreeing with the order beneath them.
|
|
79
|
+
*/
|
|
80
|
+
export const SORT_ORDERS = ["newest", "oldest", "rating_desc"] as const;
|
|
81
|
+
export type SortOrder = (typeof SORT_ORDERS)[number];
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The orders a collection offers its readers — the same three, deliberately.
|
|
85
|
+
*
|
|
86
|
+
* Kept as its own name because the two vocabularies answer different questions
|
|
87
|
+
* (one orders a query, one is stored on a collection row and shown in a menu),
|
|
88
|
+
* and pointing them at one array is what keeps them from drifting apart the way
|
|
89
|
+
* the smart collection's fourth order once did.
|
|
90
|
+
*/
|
|
91
|
+
export const COLLECTION_SORT_ORDERS = SORT_ORDERS;
|
|
92
|
+
export type CollectionSortOrder = SortOrder;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* How a smart collection orders the posts its conditions gather.
|
|
96
|
+
*
|
|
97
|
+
* Identical to a manual collection's orders, and for the same reasons: on both,
|
|
98
|
+
* `newest` means the Thread that last gained a post, not the Thread published
|
|
99
|
+
* last. A smart collection briefly carried a fourth order named `updated` that
|
|
100
|
+
* said the same thing in different words; folding it into `newest` left one
|
|
101
|
+
* menu the reader can learn once.
|
|
102
|
+
*/
|
|
103
|
+
export const SMART_COLLECTION_SORT_ORDERS = COLLECTION_SORT_ORDERS;
|
|
104
|
+
export type SmartCollectionSortOrder = CollectionSortOrder;
|
|
105
|
+
|
|
106
|
+
export const NAV_ITEM_TYPES = [
|
|
107
|
+
"link",
|
|
108
|
+
"system",
|
|
109
|
+
"collection",
|
|
110
|
+
"smart_collection",
|
|
111
|
+
"page",
|
|
112
|
+
] as const;
|
|
33
113
|
export type NavItemType = (typeof NAV_ITEM_TYPES)[number];
|
|
34
114
|
|
|
35
115
|
export const NAV_ITEM_PLACEMENTS = ["header", "more"] as const;
|
|
@@ -41,6 +121,7 @@ export const SYSTEM_NAV_KEY_VALUES = [
|
|
|
41
121
|
"collections",
|
|
42
122
|
"archive",
|
|
43
123
|
"rss",
|
|
124
|
+
"subscribe",
|
|
44
125
|
"settings",
|
|
45
126
|
] as const;
|
|
46
127
|
export type SystemNavKey = (typeof SYSTEM_NAV_KEY_VALUES)[number];
|
|
@@ -59,14 +140,31 @@ export const SYSTEM_NAV_KEYS = {
|
|
|
59
140
|
collections: {
|
|
60
141
|
defaultLabel: "Collections",
|
|
61
142
|
url: "/collections",
|
|
62
|
-
|
|
143
|
+
// The header switches between how much of one list you see (Featured /
|
|
144
|
+
// Latest / All). Collections is a different axis — what a post is about —
|
|
145
|
+
// and readers reach that through the collection tags on posts they are
|
|
146
|
+
// already reading, so it starts one level down instead of widening the
|
|
147
|
+
// header for everyone.
|
|
148
|
+
defaultPlacement: "more",
|
|
63
149
|
},
|
|
64
150
|
archive: {
|
|
65
|
-
defaultLabel: "
|
|
151
|
+
defaultLabel: "All",
|
|
66
152
|
url: "/archive",
|
|
67
153
|
defaultPlacement: "header",
|
|
68
154
|
},
|
|
69
|
-
|
|
155
|
+
// Two entries, two audiences. `rss` links straight at the Atom document, for
|
|
156
|
+
// an author whose readers already run a feed reader; `subscribe` links at the
|
|
157
|
+
// page that explains the feeds, for everyone else. Repointing `rss` at
|
|
158
|
+
// /subscribe instead of adding this would have destroyed the first case, and
|
|
159
|
+
// left a key named `rss` addressing an HTML page — nav data themes read.
|
|
160
|
+
rss: { defaultLabel: "RSS", url: "/feed", defaultPlacement: "more" },
|
|
161
|
+
subscribe: {
|
|
162
|
+
defaultLabel: "Subscribe",
|
|
163
|
+
url: "/subscribe",
|
|
164
|
+
// A once-in-a-reader's-lifetime utility page, not a section to come back
|
|
165
|
+
// and read, so it sits with Settings rather than beside Featured and All.
|
|
166
|
+
defaultPlacement: "more",
|
|
167
|
+
},
|
|
70
168
|
settings: {
|
|
71
169
|
defaultLabel: "Settings",
|
|
72
170
|
url: "/settings",
|
|
@@ -81,6 +179,32 @@ export const SYSTEM_NAV_KEYS = {
|
|
|
81
179
|
}
|
|
82
180
|
>;
|
|
83
181
|
|
|
182
|
+
/**
|
|
183
|
+
* The system nav entries that exist only while RSS feeds do.
|
|
184
|
+
*
|
|
185
|
+
* `rss` addresses the Atom document; `subscribe` addresses the page that
|
|
186
|
+
* explains it, which 404s without feeds. Both disappear from the rendered
|
|
187
|
+
* navigation when feeds are switched off — while staying in the database, so
|
|
188
|
+
* placement and custom labels survive being switched back on.
|
|
189
|
+
*/
|
|
190
|
+
export const FEED_NAV_KEYS = [
|
|
191
|
+
"rss",
|
|
192
|
+
"subscribe",
|
|
193
|
+
] as const satisfies readonly SystemNavKey[];
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Whether a system nav key is one of the feed entries.
|
|
197
|
+
*
|
|
198
|
+
* @param key - A system nav key, or nothing for a non-system item
|
|
199
|
+
* @returns Whether the entry depends on feeds being enabled
|
|
200
|
+
* @example
|
|
201
|
+
* isFeedNavKey("subscribe"); // true
|
|
202
|
+
* isFeedNavKey("archive"); // false
|
|
203
|
+
*/
|
|
204
|
+
export function isFeedNavKey(key: string | null | undefined): boolean {
|
|
205
|
+
return (FEED_NAV_KEYS as readonly string[]).includes(key ?? "");
|
|
206
|
+
}
|
|
207
|
+
|
|
84
208
|
export interface DefaultNavigationProfile {
|
|
85
209
|
version: number;
|
|
86
210
|
systemKeys: readonly SystemNavKey[];
|
|
@@ -96,9 +220,24 @@ export const DEFAULT_NAVIGATION_PROFILES = {
|
|
|
96
220
|
version: 1,
|
|
97
221
|
systemKeys: ["featured", "collections", "archive", "rss", "settings"],
|
|
98
222
|
},
|
|
223
|
+
// Featured and All sit next to each other because they are the same list at
|
|
224
|
+
// two widths; version 1 split that pair with Collections, which reads as a
|
|
225
|
+
// third unrelated section rather than the wide end of one range.
|
|
226
|
+
2: {
|
|
227
|
+
version: 2,
|
|
228
|
+
systemKeys: ["featured", "archive", "collections", "rss", "settings"],
|
|
229
|
+
},
|
|
230
|
+
// `rss` handed a browser a screen of Atom XML, which is a dead end for a
|
|
231
|
+
// reader who does not already know what to do with it. New sites point at
|
|
232
|
+
// /subscribe instead; existing rows keep `rss`, and an author who wants the
|
|
233
|
+
// new entry turns it on beside the old one in Appearance.
|
|
234
|
+
3: {
|
|
235
|
+
version: 3,
|
|
236
|
+
systemKeys: ["featured", "archive", "collections", "subscribe", "settings"],
|
|
237
|
+
},
|
|
99
238
|
} as const satisfies Record<number, DefaultNavigationProfile>;
|
|
100
239
|
|
|
101
|
-
export const DEFAULT_NAVIGATION_PROFILE_VERSION =
|
|
240
|
+
export const DEFAULT_NAVIGATION_PROFILE_VERSION = 3 as const;
|
|
102
241
|
export const DEFAULT_NAVIGATION_PROFILE =
|
|
103
242
|
DEFAULT_NAVIGATION_PROFILES[DEFAULT_NAVIGATION_PROFILE_VERSION];
|
|
104
243
|
|
|
@@ -131,6 +270,23 @@ export type StorageDriver = (typeof STORAGE_DRIVERS)[number];
|
|
|
131
270
|
export const PATH_KINDS = ["slug", "alias", "redirect", "archive"] as const;
|
|
132
271
|
export type PathKind = (typeof PATH_KINDS)[number];
|
|
133
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Row kinds the collections directory can hold.
|
|
275
|
+
*
|
|
276
|
+
* A `collection` row points at a collection and carries no text of its own; a
|
|
277
|
+
* `divider` and a `link` carry text and point at nothing. The table CHECK in
|
|
278
|
+
* both dialects is generated from this list, so a new kind is one edit here
|
|
279
|
+
* plus the shape branch that describes which columns it fills.
|
|
280
|
+
*/
|
|
281
|
+
export const COLLECTION_DIRECTORY_ENTRY_TYPES = [
|
|
282
|
+
"collection",
|
|
283
|
+
"smart_collection",
|
|
284
|
+
"divider",
|
|
285
|
+
"link",
|
|
286
|
+
] as const;
|
|
287
|
+
export type CollectionDirectoryEntryType =
|
|
288
|
+
(typeof COLLECTION_DIRECTORY_ENTRY_TYPES)[number];
|
|
289
|
+
|
|
134
290
|
/** How long a collection nav item stays "fresh" after new content is added */
|
|
135
291
|
export const COLLECTION_FRESHNESS_WINDOW_SECONDS = 48 * 60 * 60;
|
|
136
292
|
|
|
@@ -142,3 +298,19 @@ export type SiteDomainKind = (typeof SITE_DOMAIN_KINDS)[number];
|
|
|
142
298
|
|
|
143
299
|
export const SITE_MEMBER_ROLES = ["owner", "admin", "editor"] as const;
|
|
144
300
|
export type SiteMemberRole = (typeof SITE_MEMBER_ROLES)[number];
|
|
301
|
+
|
|
302
|
+
export const UPLOAD_SESSION_STATES = [
|
|
303
|
+
"pending",
|
|
304
|
+
"uploaded",
|
|
305
|
+
"completed",
|
|
306
|
+
"aborted",
|
|
307
|
+
"failed",
|
|
308
|
+
] as const;
|
|
309
|
+
export type UploadSessionState = (typeof UPLOAD_SESSION_STATES)[number];
|
|
310
|
+
|
|
311
|
+
/** How a stored object is served: rendered in place, or downloaded. */
|
|
312
|
+
export const CONTENT_DISPOSITIONS = ["inline", "attachment"] as const;
|
|
313
|
+
export type ContentDisposition = (typeof CONTENT_DISPOSITIONS)[number];
|
|
314
|
+
|
|
315
|
+
export const GITHUB_APP_ACCOUNT_TYPES = ["User", "Organization"] as const;
|
|
316
|
+
export type GithubAppAccountType = (typeof GITHUB_APP_ACCOUNT_TYPES)[number];
|
package/src/types/entities.ts
CHANGED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
|
+
CollectionDirectoryEntryType,
|
|
6
7
|
Format,
|
|
7
8
|
Status,
|
|
8
9
|
Visibility,
|
|
9
10
|
CollectionSortOrder,
|
|
11
|
+
SmartCollectionSortOrder,
|
|
12
|
+
ArchiveLayout,
|
|
10
13
|
NavItemType,
|
|
11
14
|
NavItemPlacement,
|
|
12
15
|
SystemNavKey,
|
|
@@ -17,6 +20,10 @@ import type {
|
|
|
17
20
|
SiteMemberRole,
|
|
18
21
|
} from "./constants.js";
|
|
19
22
|
|
|
23
|
+
import type { PostFilterSelection } from "../lib/filter-dimensions.js";
|
|
24
|
+
|
|
25
|
+
export type { CollectionDirectoryEntryType };
|
|
26
|
+
|
|
20
27
|
export interface Site {
|
|
21
28
|
id: string;
|
|
22
29
|
key: string;
|
|
@@ -66,6 +73,15 @@ export interface Post {
|
|
|
66
73
|
previewProvider: string | null;
|
|
67
74
|
replyToId: string | null;
|
|
68
75
|
threadId: string;
|
|
76
|
+
/**
|
|
77
|
+
* BCP 47 content language, canonical form. Uniform across a Thread. `null`
|
|
78
|
+
* only until the site first enables multilingual content.
|
|
79
|
+
*/
|
|
80
|
+
language: string | null;
|
|
81
|
+
/**
|
|
82
|
+
* Shared key for Posts that are translations of one another. Roots only.
|
|
83
|
+
*/
|
|
84
|
+
translationGroupId: string | null;
|
|
69
85
|
/** Reply published without announcing it on Latest. Always false on roots. */
|
|
70
86
|
quietReply: boolean;
|
|
71
87
|
publishedAt: number | null;
|
|
@@ -141,13 +157,60 @@ export interface CollectionDirectoryCollection extends Collection {
|
|
|
141
157
|
recentActivityAt: number;
|
|
142
158
|
}
|
|
143
159
|
|
|
144
|
-
|
|
160
|
+
/**
|
|
161
|
+
* A collection whose members are decided by conditions rather than by tagging.
|
|
162
|
+
*
|
|
163
|
+
* To a reader this is a collection: a root-level address, a title, a
|
|
164
|
+
* description, a list of posts, a feed. The asymmetries are permanent and
|
|
165
|
+
* deliberate — nothing can be added to it by hand, nothing pinned, nothing
|
|
166
|
+
* reordered — so every "add this post to a collection" surface has to leave it
|
|
167
|
+
* out. See `selection` for what decides membership.
|
|
168
|
+
*/
|
|
169
|
+
export interface SmartCollection {
|
|
170
|
+
id: string;
|
|
171
|
+
siteId: string;
|
|
172
|
+
slug: string;
|
|
173
|
+
title: string;
|
|
174
|
+
description: string | null;
|
|
175
|
+
/** The conditions, in the shared dimension vocabulary. Empty = every post. */
|
|
176
|
+
selection: PostFilterSelection;
|
|
177
|
+
sort: SmartCollectionSortOrder;
|
|
178
|
+
/** `null` follows the site's configured archive layout. */
|
|
179
|
+
layout: ArchiveLayout | null;
|
|
180
|
+
createdAt: number;
|
|
181
|
+
updatedAt: number;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface SmartCollectionDirectoryEntry extends SmartCollection {
|
|
185
|
+
threadCount: number;
|
|
186
|
+
/**
|
|
187
|
+
* Newest activity among the threads the conditions match, or the smart
|
|
188
|
+
* collection's own `updatedAt` when they match nothing.
|
|
189
|
+
*
|
|
190
|
+
* The same measure `CollectionDirectoryCollection` carries, on the same
|
|
191
|
+
* definition, because the two sit in one list and a reader compares them.
|
|
192
|
+
*/
|
|
193
|
+
recentActivityAt: number;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* One entry in the collections directory, either kind.
|
|
198
|
+
*
|
|
199
|
+
* The two are told apart by `kind`, not by which optional field happens to be
|
|
200
|
+
* present. Surfaces that treat them alike — the directory, the navigation
|
|
201
|
+
* picker, the command palette — take this; surfaces where the difference
|
|
202
|
+
* matters take one or the other.
|
|
203
|
+
*/
|
|
204
|
+
export type AnyCollectionEntry =
|
|
205
|
+
| { kind: "manual"; collection: CollectionDirectoryCollection }
|
|
206
|
+
| { kind: "smart"; smartCollection: SmartCollectionDirectoryEntry };
|
|
145
207
|
|
|
146
208
|
export interface CollectionDirectoryEntry {
|
|
147
209
|
id: string;
|
|
148
210
|
siteId: string;
|
|
149
211
|
type: CollectionDirectoryEntryType;
|
|
150
212
|
collectionId: string | null;
|
|
213
|
+
smartCollectionId: string | null;
|
|
151
214
|
label: string | null;
|
|
152
215
|
url: string | null;
|
|
153
216
|
description: string | null;
|
|
@@ -163,10 +226,12 @@ export interface CollectionDirectoryItem {
|
|
|
163
226
|
url?: string | null;
|
|
164
227
|
description?: string | null;
|
|
165
228
|
collection?: CollectionDirectoryCollection;
|
|
229
|
+
smartCollection?: SmartCollectionDirectoryEntry;
|
|
166
230
|
}
|
|
167
231
|
|
|
168
232
|
export interface CollectionsDirectoryData {
|
|
169
233
|
collections: CollectionDirectoryCollection[];
|
|
234
|
+
smartCollections: SmartCollectionDirectoryEntry[];
|
|
170
235
|
items: CollectionDirectoryItem[];
|
|
171
236
|
directoryItems: CollectionDirectoryEntry[];
|
|
172
237
|
}
|
|
@@ -183,9 +248,24 @@ export interface NavItem {
|
|
|
183
248
|
type: NavItemType;
|
|
184
249
|
systemKey?: SystemNavKey;
|
|
185
250
|
collectionId?: string;
|
|
251
|
+
smartCollectionId?: string;
|
|
186
252
|
postId?: string;
|
|
253
|
+
/**
|
|
254
|
+
* Author's override, or `""` when the item follows whatever it points at.
|
|
255
|
+
*
|
|
256
|
+
* Empty is the normal state: a page or collection item shows its target's
|
|
257
|
+
* current title, a built-in item shows its translated default. Only a label
|
|
258
|
+
* the author typed is stored here, and it then wins in every language view.
|
|
259
|
+
*/
|
|
187
260
|
label: string;
|
|
188
261
|
url: string;
|
|
262
|
+
/**
|
|
263
|
+
* Current title of the post or collection this item points at, resolved at
|
|
264
|
+
* read time. The display label when `label` is empty — see
|
|
265
|
+
* `getNavItemDisplayLabel`. Absent for `link` and `system` items, which have
|
|
266
|
+
* no target row to follow.
|
|
267
|
+
*/
|
|
268
|
+
targetTitle?: string;
|
|
189
269
|
placement: NavItemPlacement;
|
|
190
270
|
position: string;
|
|
191
271
|
createdAt: number;
|
|
@@ -210,6 +290,7 @@ export interface PathRecord {
|
|
|
210
290
|
kind: PathKind;
|
|
211
291
|
postId: string | null;
|
|
212
292
|
collectionId: string | null;
|
|
293
|
+
smartCollectionId: string | null;
|
|
213
294
|
redirectToPath: string | null;
|
|
214
295
|
redirectType: 301 | 302 | null;
|
|
215
296
|
archiveQuery: string | null;
|
|
@@ -241,5 +322,10 @@ export interface ThreadTimelineContext {
|
|
|
241
322
|
/** Published replies immediately before latest, in chronological order. */
|
|
242
323
|
trailingReplies: Post[];
|
|
243
324
|
latestReply: Post;
|
|
325
|
+
/**
|
|
326
|
+
* First reply the fold hides, which is where the gap link points. Null when
|
|
327
|
+
* nothing is hidden. Fetched for its address only — never rendered.
|
|
328
|
+
*/
|
|
329
|
+
firstHiddenReply: Post | null;
|
|
244
330
|
totalReplyCount: number;
|
|
245
331
|
}
|