@jant/core 0.6.16 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/bin/commands/assets/upload.js +192 -15
- package/bin/commands/export.js +3 -1
- package/bin/commands/import-site.js +749 -220
- package/bin/commands/reset-password.js +21 -8
- package/bin/commands/setup.js +136 -0
- package/bin/commands/site/export.js +71 -34
- package/bin/commands/site/pull-media.js +2 -6
- package/bin/commands/site/snapshot/export.js +19 -61
- package/bin/commands/site/snapshot/import.js +23 -24
- package/bin/lib/d1-query.js +92 -18
- package/bin/lib/hugo-markdown.js +4 -0
- package/bin/lib/node-env.js +40 -4
- package/bin/lib/site-pull-media.js +21 -28
- package/bin/lib/site-selection.js +94 -29
- package/bin/lib/site-snapshot.js +338 -3
- package/bin/lib/sql-export.js +68 -5
- package/bin/lib/wrangler-cli.js +37 -2
- package/bin/lib/wrangler-json.js +35 -0
- package/bin/lib/zip-archive.js +187 -0
- package/dist/{app-C7_4oab9.js → app-ZcaI1kPN.js} +53190 -13529
- 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-B-m7X7S5.js +261 -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-BJyd-LXE.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-BkscJx69.js +2 -0
- package/dist/client/_assets/client-DAhoqPdr.css +2 -0
- package/dist/client/_assets/client-auth-CAMfTrKW.js +828 -0
- package/dist/client/_assets/client-author-DQ8R-8KR.css +2 -0
- package/dist/client/_assets/client-compose-D6rExKVK.js +2004 -0
- package/dist/client/_assets/client-knSEyUJO.js +295 -0
- package/dist/client/_assets/client-manage-sVOkqVbH.js +2170 -0
- package/dist/client/_assets/client-settings-CGR_nZ78.js +1112 -0
- package/dist/github-sync-Gw4orAHk.js +8398 -0
- package/dist/index.js +2 -5
- package/dist/node.js +11 -9
- package/package.json +15 -5
- 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__/dev-scripts.test.ts +203 -0
- package/src/__tests__/export-collection-order.test.ts +276 -0
- package/src/__tests__/export-feed-ids.test.ts +184 -0
- package/src/__tests__/export-feed-order.test.ts +294 -0
- package/src/__tests__/export-hugo-build.test.ts +390 -0
- package/src/__tests__/export-import-roundtrip.test.ts +94 -0
- package/src/__tests__/export-service.test.ts +842 -28
- package/src/__tests__/export-smart-collection.test.ts +329 -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__/helpers/hugo-site.ts +146 -0
- package/src/__tests__/import-site-command.test.ts +487 -1
- package/src/__tests__/mise-config.test.ts +75 -4
- package/src/__tests__/node-dev-tasks.test.ts +264 -0
- package/src/__tests__/site-export-canonical-import.test.ts +149 -0
- package/src/__tests__/snapshot-canonical-replay.test.ts +179 -0
- package/src/__tests__/snapshot-settings.test.ts +97 -0
- package/src/__tests__/snapshot-tables.test.ts +219 -0
- package/src/__tests__/sql-export.test.ts +173 -0
- package/src/__tests__/stylesheet-audience.test.ts +159 -0
- package/src/__tests__/stylesheet-breakpoints.test.ts +183 -0
- package/src/__tests__/zip-archive.test.ts +106 -0
- package/src/app.tsx +86 -22
- 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 +561 -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-types.ts +6 -1
- package/src/client/components/jant-repo-picker.ts +137 -108
- package/src/client/components/jant-settings-avatar.ts +31 -27
- package/src/client/components/jant-settings-general.ts +622 -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/__tests__/list-editing.test.ts +224 -86
- package/src/client/tiptap/__tests__/mark-exit.test.ts +1 -2
- package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +26 -0
- package/src/client/tiptap/bubble-menu.ts +47 -16
- package/src/client/tiptap/extensions.ts +0 -3
- 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/tiptap/structural-keymap.ts +158 -47
- 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__/d1-query.test.ts +56 -1
- 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 +555 -73
- package/src/i18n/locales/settings/en.ts +1 -1
- package/src/i18n/locales/settings/zh-Hans.po +601 -119
- package/src/i18n/locales/settings/zh-Hans.ts +1 -1
- package/src/i18n/locales/settings/zh-Hant.po +614 -132
- 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__/github-sync-repo-name.test.ts +40 -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__/image.test.ts +27 -1
- 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__/markdown-to-tiptap.test.ts +105 -0
- package/src/lib/__tests__/markdown.test.ts +10 -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 +131 -3
- package/src/lib/__tests__/schemas.test.ts +69 -5
- 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__/timeline.test.ts +87 -0
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +172 -4
- 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 +439 -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-repo-name.ts +45 -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 +62 -0
- package/src/lib/icons.ts +9 -2
- package/src/lib/ids.ts +3 -0
- package/src/lib/image.ts +17 -4
- package/src/lib/jant-docs.ts +42 -0
- package/src/lib/lang-detect.ts +308 -0
- package/src/lib/markdown-manager.ts +392 -2
- 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-body-html.ts +11 -4
- package/src/lib/post-meta.ts +19 -0
- package/src/lib/render.tsx +55 -7
- package/src/lib/resolve-config.ts +111 -6
- package/src/lib/schemas.ts +251 -15
- 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 +43 -2
- package/src/lib/tiptap-render.ts +1 -2
- package/src/lib/tiptap-to-markdown.ts +13 -7
- package/src/lib/upload.ts +16 -13
- package/src/lib/url.ts +159 -86
- package/src/lib/version.ts +10 -8
- package/src/lib/view-language.ts +436 -0
- package/src/lib/view.ts +53 -25
- 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-setup.test.ts +163 -0
- package/src/node/__tests__/cli-site-selection.test.ts +128 -38
- package/src/node/__tests__/cli-site-snapshot.test.ts +67 -0
- package/src/node/__tests__/cli-snapshot-meta.test.ts +20 -0
- package/src/node/__tests__/runtime.test.ts +75 -0
- package/src/node/index.ts +3 -0
- package/src/node/request-handler.ts +3 -1
- 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__/posts.test.ts +24 -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/__tests__/upload.test.ts +34 -0
- 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 +6 -3
- package/src/routes/api/internal/__tests__/sites.test.ts +42 -2
- package/src/routes/api/internal/sites.ts +0 -1
- package/src/routes/api/nav-items.ts +55 -8
- package/src/routes/api/posts.ts +161 -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 +44 -7
- package/src/routes/api/smart-collections.ts +132 -0
- package/src/routes/api/telegram.ts +2 -40
- package/src/routes/api/upload.ts +10 -3
- 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 +9 -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__/github-sync-app.test.ts +365 -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 +714 -111
- 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-page-round-trips.test.ts +211 -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/__tests__/thread-order.test.ts +179 -0
- package/src/routes/pages/archive.tsx +487 -379
- package/src/routes/pages/collection.tsx +94 -96
- package/src/routes/pages/collections.tsx +38 -50
- package/src/routes/pages/featured.tsx +43 -18
- 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 +228 -45
- package/src/routes/pages/partials.tsx +41 -0
- package/src/routes/pages/search.tsx +22 -5
- 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/__tests__/readiness.test.ts +23 -0
- package/src/runtime/cloudflare.ts +21 -2
- package/src/runtime/index.ts +27 -1
- package/src/runtime/node.ts +67 -1
- package/src/runtime/readiness.ts +15 -0
- package/src/runtime/site.ts +95 -24
- package/src/services/__tests__/auth.test.ts +116 -7
- package/src/services/__tests__/bootstrap-setup-instance.test.ts +230 -0
- package/src/services/__tests__/collection.test.ts +108 -4
- package/src/services/__tests__/custom-url.test.ts +34 -0
- package/src/services/__tests__/github-app-installations.test.ts +153 -0
- package/src/services/__tests__/github-sync-push.test.ts +46 -0
- package/src/services/__tests__/language.test.ts +495 -0
- package/src/services/__tests__/media.test.ts +31 -0
- package/src/services/__tests__/navigation.test.ts +213 -10
- package/src/services/__tests__/path.test.ts +236 -0
- package/src/services/__tests__/post-language.test.ts +830 -0
- package/src/services/__tests__/post-timeline.test.ts +127 -0
- package/src/services/__tests__/post.test.ts +120 -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 +408 -39
- package/src/services/collection-directory-position.ts +76 -0
- package/src/services/collection.ts +331 -46
- package/src/services/custom-url.ts +29 -24
- 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/alias.html +27 -1
- package/src/services/export-theme/layouts/_default/list.html +2 -63
- package/src/services/export-theme/layouts/_default/rss.xml +103 -38
- package/src/services/export-theme/layouts/collections/list.html +3 -3
- package/src/services/export-theme/layouts/featured/list.html +1 -4
- package/src/services/export-theme/layouts/index.html +40 -26
- package/src/services/export-theme/layouts/partials/collection-members.html +100 -0
- package/src/services/export-theme/layouts/partials/collection-threads.html +33 -0
- package/src/services/export-theme/layouts/partials/featured-members.html +35 -0
- package/src/services/export-theme/layouts/partials/featured-thread.html +1 -1
- package/src/services/export-theme/layouts/partials/feed-post-content.xml +105 -2
- package/src/services/export-theme/layouts/partials/footer.html +1 -1
- package/src/services/export-theme/layouts/partials/head.html +1 -1
- package/src/services/export-theme/layouts/partials/header.html +5 -3
- package/src/services/export-theme/layouts/partials/jant-data.html +23 -0
- package/src/services/export-theme/layouts/partials/latest-members.html +48 -0
- 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/smart-collection-members.html +129 -0
- package/src/services/export-theme/layouts/partials/thread-preview.html +41 -31
- package/src/services/export-theme/layouts/post/list.html +1 -1
- package/src/services/export-theme/layouts/smart_collection/list.html +24 -0
- package/src/services/export-theme/styles/main.css +13 -2
- package/src/services/export-theme/theme.toml +1 -1
- package/src/services/export.ts +880 -72
- package/src/services/github-app-installations.ts +171 -4
- package/src/services/github-sync.ts +69 -15
- package/src/services/index.ts +34 -3
- package/src/services/language.ts +412 -0
- package/src/services/mcp.ts +22 -2
- package/src/services/media.ts +22 -7
- package/src/services/navigation.ts +429 -67
- package/src/services/path.ts +254 -6
- package/src/services/post.ts +1234 -190
- package/src/services/search.ts +39 -2
- package/src/services/settings.ts +238 -43
- package/src/services/site-admin.ts +27 -14
- package/src/services/site.ts +160 -32
- 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 +1090 -7216
- package/src/types/app-context.ts +23 -0
- package/src/types/bindings.ts +8 -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 +74 -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 +277 -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/export-Dl5KCiEs.js +0 -5562
- 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/client/tiptap/exitable-marks.ts +0 -73
- package/src/ui/pages/CollectionEditorPage.tsx +0 -175
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* General Settings Component
|
|
3
3
|
*
|
|
4
|
-
* Main container for the General settings page.
|
|
5
|
-
*
|
|
4
|
+
* Main container for the General settings page. Typed fields are grouped
|
|
5
|
+
* behind a Save button and track their dirty state independently; every
|
|
6
|
+
* control whose value is complete the moment it is set — checkbox, radio,
|
|
7
|
+
* select — saves on change.
|
|
6
8
|
*
|
|
7
9
|
* Light DOM only — BaseCoat and Tailwind classes apply directly.
|
|
8
10
|
*/
|
|
@@ -11,40 +13,55 @@ import { LitElement, html, nothing } from "lit";
|
|
|
11
13
|
import type { Editor } from "@tiptap/core";
|
|
12
14
|
import { MAX_SITE_NAME_LENGTH } from "../../types.js";
|
|
13
15
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
type
|
|
17
|
-
} from "../../
|
|
16
|
+
discoverIntroRuns,
|
|
17
|
+
resolveDiscoverMode,
|
|
18
|
+
type DiscoverPageUrls,
|
|
19
|
+
} from "../../lib/discover.js";
|
|
18
20
|
import type {
|
|
19
21
|
SettingsInitialData,
|
|
20
22
|
SettingsLabels,
|
|
21
23
|
SettingsTimezone,
|
|
22
|
-
SettingsCjkFont,
|
|
23
|
-
SettingsDashboardLanguage,
|
|
24
24
|
SettingsAboutPageStatus,
|
|
25
25
|
} from "./settings-types.js";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
COPY_FIELD_BUTTON_CLASS,
|
|
28
|
+
COPY_FIELD_CLASS,
|
|
29
|
+
COPY_FIELD_CONTROL_CLASS,
|
|
30
|
+
COPY_FIELD_INPUT_CLASS,
|
|
31
|
+
} from "../../lib/copy-field.js";
|
|
27
32
|
import {
|
|
28
33
|
createSettingsEditor,
|
|
29
34
|
jsonToMarkdown,
|
|
30
35
|
} from "../tiptap/create-editor.js";
|
|
31
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Spacing every section shares, whatever its position.
|
|
39
|
+
*
|
|
40
|
+
* The container's `divide-y` draws the rule under each section but the last;
|
|
41
|
+
* this pads both sides of each rule and drops the padding at either end. The
|
|
42
|
+
* gap between a section's own children is left to the section.
|
|
43
|
+
*/
|
|
44
|
+
const SECTION_CLASS = "flex flex-col py-8 first:pt-0 last:pb-0";
|
|
45
|
+
|
|
32
46
|
export class JantSettingsGeneral extends LitElement {
|
|
33
47
|
static properties = {
|
|
34
48
|
labels: { type: Object },
|
|
35
49
|
timezones: { type: Array },
|
|
36
|
-
cjkFonts: { type: Array, attribute: "cjk-fonts" },
|
|
37
50
|
siteNameFallback: { type: String, attribute: "sitename-fallback" },
|
|
38
51
|
siteDescriptionFallback: {
|
|
39
52
|
type: String,
|
|
40
53
|
attribute: "sitedescription-fallback",
|
|
41
54
|
},
|
|
42
|
-
dashboardLanguages: { type: Array, attribute: "dashboard-languages" },
|
|
43
55
|
demoMode: { type: Boolean, attribute: "demo-mode" },
|
|
56
|
+
discoverDefault: { type: String, attribute: "discover-default" },
|
|
57
|
+
discoverPages: { type: Object, attribute: "discover-pages" },
|
|
58
|
+
discoverStatus: { type: String, attribute: "discover-status" },
|
|
59
|
+
feedsEnabled: { type: Boolean, attribute: "feeds-enabled" },
|
|
44
60
|
mainFeedUrl: { type: String, attribute: "main-feed-url" },
|
|
45
61
|
latestFeedUrl: { type: String, attribute: "latest-feed-url" },
|
|
46
62
|
featuredFeedUrl: { type: String, attribute: "featured-feed-url" },
|
|
47
63
|
archiveFeedUrl: { type: String, attribute: "archive-feed-url" },
|
|
64
|
+
feedsDocsUrl: { type: String, attribute: "feeds-docs-url" },
|
|
48
65
|
aboutPage: { type: Object, attribute: "about-page" },
|
|
49
66
|
aboutEditUrl: { type: String, attribute: "about-edit-url" },
|
|
50
67
|
aboutCreateUrl: {
|
|
@@ -60,12 +77,7 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
60
77
|
_siteDirty: { state: true },
|
|
61
78
|
_siteLoading: { state: true },
|
|
62
79
|
|
|
63
|
-
//
|
|
64
|
-
_siteLanguage: { state: true },
|
|
65
|
-
_dashboardLanguage: { state: true },
|
|
66
|
-
_localeOpen: { state: true },
|
|
67
|
-
_localeQuery: { state: true },
|
|
68
|
-
_cjkSerifFont: { state: true },
|
|
80
|
+
// Time group
|
|
69
81
|
_timeZone: { state: true },
|
|
70
82
|
_origLocale: { state: true },
|
|
71
83
|
_localeDirty: { state: true },
|
|
@@ -74,7 +86,6 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
74
86
|
// Feed group
|
|
75
87
|
_mainRssFeed: { state: true },
|
|
76
88
|
_origMainRssFeed: { state: true },
|
|
77
|
-
_feedDirty: { state: true },
|
|
78
89
|
_feedLoading: { state: true },
|
|
79
90
|
|
|
80
91
|
// Home auto-save
|
|
@@ -86,19 +97,40 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
86
97
|
_noindex: { state: true },
|
|
87
98
|
_origNoindex: { state: true },
|
|
88
99
|
_searchLoading: { state: true },
|
|
100
|
+
|
|
101
|
+
// Discover group
|
|
102
|
+
_discover: { state: true },
|
|
103
|
+
_origDiscover: { state: true },
|
|
104
|
+
_discoverLoading: { state: true },
|
|
89
105
|
};
|
|
90
106
|
|
|
91
107
|
declare labels: SettingsLabels;
|
|
92
108
|
declare timezones: SettingsTimezone[];
|
|
93
|
-
declare cjkFonts: SettingsCjkFont[];
|
|
94
|
-
declare dashboardLanguages: SettingsDashboardLanguage[];
|
|
95
109
|
declare siteNameFallback: string;
|
|
96
110
|
declare siteDescriptionFallback: string;
|
|
97
111
|
declare demoMode: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* The deployment's own answer, unresolved: `""` when it has none.
|
|
114
|
+
*
|
|
115
|
+
* What the site actually declares is derived from this and from the controls
|
|
116
|
+
* around it — see `_effectiveDiscoverMode`.
|
|
117
|
+
*/
|
|
118
|
+
declare discoverDefault: string;
|
|
119
|
+
/** The directory's own pages, or `null` when none is configured. */
|
|
120
|
+
declare discoverPages: DiscoverPageUrls | null;
|
|
121
|
+
/**
|
|
122
|
+
* JSON status block, already translated by the server.
|
|
123
|
+
*
|
|
124
|
+
* The sentences carry runtime numbers, so they are built where the values
|
|
125
|
+
* are. This component only decides where they go.
|
|
126
|
+
*/
|
|
127
|
+
declare discoverStatus: string;
|
|
128
|
+
declare feedsEnabled: boolean;
|
|
98
129
|
declare mainFeedUrl: string;
|
|
99
130
|
declare latestFeedUrl: string;
|
|
100
131
|
declare featuredFeedUrl: string;
|
|
101
132
|
declare archiveFeedUrl: string;
|
|
133
|
+
declare feedsDocsUrl: string;
|
|
102
134
|
declare aboutPage: SettingsAboutPageStatus;
|
|
103
135
|
declare aboutEditUrl: string;
|
|
104
136
|
declare aboutCreateUrl: string;
|
|
@@ -115,20 +147,10 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
115
147
|
declare _siteDirty: boolean;
|
|
116
148
|
declare _siteLoading: boolean;
|
|
117
149
|
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
/** Admin dashboard UI locale (one of the translated catalog locales). */
|
|
121
|
-
declare _dashboardLanguage: string;
|
|
122
|
-
/** Whether the locale combobox dropdown is currently open. */
|
|
123
|
-
declare _localeOpen: boolean;
|
|
124
|
-
/** Search query inside the locale combobox. */
|
|
125
|
-
declare _localeQuery: string;
|
|
126
|
-
declare _cjkSerifFont: string;
|
|
150
|
+
// Time
|
|
151
|
+
/** IANA time zone name, such as `"America/New_York"`. */
|
|
127
152
|
declare _timeZone: string;
|
|
128
153
|
declare _origLocale: {
|
|
129
|
-
siteLanguage: string;
|
|
130
|
-
dashboardLanguage: string;
|
|
131
|
-
cjkSerifFont: string;
|
|
132
154
|
timeZone: string;
|
|
133
155
|
};
|
|
134
156
|
declare _localeDirty: boolean;
|
|
@@ -137,7 +159,6 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
137
159
|
// Feed
|
|
138
160
|
declare _mainRssFeed: string;
|
|
139
161
|
declare _origMainRssFeed: string;
|
|
140
|
-
declare _feedDirty: boolean;
|
|
141
162
|
declare _feedLoading: boolean;
|
|
142
163
|
|
|
143
164
|
// Home
|
|
@@ -150,6 +171,12 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
150
171
|
declare _origNoindex: boolean;
|
|
151
172
|
declare _searchLoading: boolean;
|
|
152
173
|
|
|
174
|
+
// Discover. "" means the owner has never used the control, which is not the
|
|
175
|
+
// same as "off" — an untouched site still follows the default.
|
|
176
|
+
declare _discover: string;
|
|
177
|
+
declare _origDiscover: string;
|
|
178
|
+
declare _discoverLoading: boolean;
|
|
179
|
+
|
|
153
180
|
// TipTap editor instances
|
|
154
181
|
private _descEditor: Editor | null = null;
|
|
155
182
|
private _footerEditor: Editor | null = null;
|
|
@@ -163,8 +190,6 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
163
190
|
super();
|
|
164
191
|
this.labels = {} as SettingsLabels;
|
|
165
192
|
this.timezones = [];
|
|
166
|
-
this.cjkFonts = [];
|
|
167
|
-
this.dashboardLanguages = [];
|
|
168
193
|
this.siteNameFallback = "";
|
|
169
194
|
this.siteDescriptionFallback = "";
|
|
170
195
|
this.demoMode = false;
|
|
@@ -172,6 +197,7 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
172
197
|
this.latestFeedUrl = "/latest/feed";
|
|
173
198
|
this.featuredFeedUrl = "/featured/feed";
|
|
174
199
|
this.archiveFeedUrl = "/archive/feed";
|
|
200
|
+
this.feedsDocsUrl = "";
|
|
175
201
|
this.aboutPage = {
|
|
176
202
|
state: "missing",
|
|
177
203
|
path: "/about",
|
|
@@ -190,25 +216,14 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
190
216
|
this._siteDirty = false;
|
|
191
217
|
this._siteLoading = false;
|
|
192
218
|
|
|
193
|
-
this._siteLanguage = "en";
|
|
194
|
-
this._dashboardLanguage = "en";
|
|
195
|
-
this._localeOpen = false;
|
|
196
|
-
this._localeQuery = "";
|
|
197
|
-
this._cjkSerifFont = "off";
|
|
198
219
|
this._timeZone = "UTC";
|
|
199
|
-
this._origLocale = {
|
|
200
|
-
siteLanguage: "en",
|
|
201
|
-
dashboardLanguage: "en",
|
|
202
|
-
cjkSerifFont: "off",
|
|
203
|
-
timeZone: "UTC",
|
|
204
|
-
};
|
|
220
|
+
this._origLocale = { timeZone: "UTC" };
|
|
205
221
|
|
|
206
222
|
this._localeDirty = false;
|
|
207
223
|
this._localeLoading = false;
|
|
208
224
|
|
|
209
225
|
this._mainRssFeed = "featured";
|
|
210
226
|
this._origMainRssFeed = "featured";
|
|
211
|
-
this._feedDirty = false;
|
|
212
227
|
this._feedLoading = false;
|
|
213
228
|
|
|
214
229
|
this._noindex = false;
|
|
@@ -217,18 +232,22 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
217
232
|
this._origShowJantBrandingOnHome = false;
|
|
218
233
|
this._homeLoading = false;
|
|
219
234
|
this._searchLoading = false;
|
|
235
|
+
|
|
236
|
+
this.discoverDefault = "";
|
|
237
|
+
this.discoverPages = null;
|
|
238
|
+
this.discoverStatus = "";
|
|
239
|
+
this.feedsEnabled = false;
|
|
240
|
+
this._discover = "";
|
|
241
|
+
this._origDiscover = "";
|
|
242
|
+
this._discoverLoading = false;
|
|
220
243
|
}
|
|
221
244
|
|
|
222
245
|
connectedCallback() {
|
|
223
246
|
super.connectedCallback();
|
|
224
|
-
document.addEventListener("click", this._onLocalePickerDocumentClick);
|
|
225
|
-
document.addEventListener("keydown", this._onLocalePickerKeydown);
|
|
226
247
|
}
|
|
227
248
|
|
|
228
249
|
disconnectedCallback() {
|
|
229
250
|
super.disconnectedCallback();
|
|
230
|
-
document.removeEventListener("click", this._onLocalePickerDocumentClick);
|
|
231
|
-
document.removeEventListener("keydown", this._onLocalePickerKeydown);
|
|
232
251
|
this._descEditor?.destroy();
|
|
233
252
|
this._descEditor = null;
|
|
234
253
|
this._footerEditor?.destroy();
|
|
@@ -241,16 +260,8 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
241
260
|
this._siteDescription = data.siteDescription;
|
|
242
261
|
this._siteFooter = data.siteFooter;
|
|
243
262
|
|
|
244
|
-
this._siteLanguage = data.siteLanguage;
|
|
245
|
-
this._dashboardLanguage = data.dashboardLanguage;
|
|
246
|
-
this._cjkSerifFont = data.cjkSerifFont;
|
|
247
263
|
this._timeZone = data.timeZone;
|
|
248
|
-
this._origLocale = {
|
|
249
|
-
siteLanguage: data.siteLanguage,
|
|
250
|
-
dashboardLanguage: data.dashboardLanguage,
|
|
251
|
-
cjkSerifFont: data.cjkSerifFont,
|
|
252
|
-
timeZone: data.timeZone,
|
|
253
|
-
};
|
|
264
|
+
this._origLocale = { timeZone: data.timeZone };
|
|
254
265
|
|
|
255
266
|
this._mainRssFeed = data.mainRssFeed;
|
|
256
267
|
this._origMainRssFeed = data.mainRssFeed;
|
|
@@ -261,6 +272,9 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
261
272
|
this._noindex = data.noindex;
|
|
262
273
|
this._origNoindex = data.noindex;
|
|
263
274
|
|
|
275
|
+
this._discover = data.discover;
|
|
276
|
+
this._origDiscover = data.discover;
|
|
277
|
+
|
|
264
278
|
// Defer editor init to after Lit renders the containers
|
|
265
279
|
this.updateComplete.then(() => {
|
|
266
280
|
this._initEditors();
|
|
@@ -283,18 +297,12 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
283
297
|
};
|
|
284
298
|
this._siteDirty = false;
|
|
285
299
|
this._siteLoading = false;
|
|
286
|
-
} else if (section === "
|
|
287
|
-
this._origLocale = {
|
|
288
|
-
siteLanguage: this._siteLanguage,
|
|
289
|
-
dashboardLanguage: this._dashboardLanguage,
|
|
290
|
-
cjkSerifFont: this._cjkSerifFont,
|
|
291
|
-
timeZone: this._timeZone,
|
|
292
|
-
};
|
|
300
|
+
} else if (section === "time") {
|
|
301
|
+
this._origLocale = { timeZone: this._timeZone };
|
|
293
302
|
this._localeDirty = false;
|
|
294
303
|
this._localeLoading = false;
|
|
295
304
|
} else if (section === "feeds") {
|
|
296
305
|
this._origMainRssFeed = this._mainRssFeed;
|
|
297
|
-
this._feedDirty = false;
|
|
298
306
|
this._feedLoading = false;
|
|
299
307
|
} else if (section === "home") {
|
|
300
308
|
this._origShowJantBrandingOnHome = this._showJantBrandingOnHome;
|
|
@@ -302,6 +310,9 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
302
310
|
} else if (section === "search") {
|
|
303
311
|
this._origNoindex = this._noindex;
|
|
304
312
|
this._searchLoading = false;
|
|
313
|
+
} else if (section === "discover") {
|
|
314
|
+
this._origDiscover = this._discover;
|
|
315
|
+
this._discoverLoading = false;
|
|
305
316
|
}
|
|
306
317
|
}
|
|
307
318
|
|
|
@@ -309,9 +320,10 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
309
320
|
sectionError(section: string) {
|
|
310
321
|
if (section === "site") {
|
|
311
322
|
this._siteLoading = false;
|
|
312
|
-
} else if (section === "
|
|
323
|
+
} else if (section === "time") {
|
|
313
324
|
this._localeLoading = false;
|
|
314
325
|
} else if (section === "feeds") {
|
|
326
|
+
this._mainRssFeed = this._origMainRssFeed;
|
|
315
327
|
this._feedLoading = false;
|
|
316
328
|
} else if (section === "home") {
|
|
317
329
|
this._showJantBrandingOnHome = this._origShowJantBrandingOnHome;
|
|
@@ -319,6 +331,9 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
319
331
|
} else if (section === "search") {
|
|
320
332
|
this._noindex = this._origNoindex;
|
|
321
333
|
this._searchLoading = false;
|
|
334
|
+
} else if (section === "discover") {
|
|
335
|
+
this._discover = this._origDiscover;
|
|
336
|
+
this._discoverLoading = false;
|
|
322
337
|
}
|
|
323
338
|
}
|
|
324
339
|
|
|
@@ -408,14 +423,10 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
408
423
|
);
|
|
409
424
|
}
|
|
410
425
|
|
|
411
|
-
// ──
|
|
426
|
+
// ── Time group helpers ────────────────────────────────────────────
|
|
412
427
|
|
|
413
428
|
private _syncLocaleDirty() {
|
|
414
|
-
this._localeDirty =
|
|
415
|
-
this._siteLanguage !== this._origLocale.siteLanguage ||
|
|
416
|
-
this._dashboardLanguage !== this._origLocale.dashboardLanguage ||
|
|
417
|
-
this._cjkSerifFont !== this._origLocale.cjkSerifFont ||
|
|
418
|
-
this._timeZone !== this._origLocale.timeZone;
|
|
429
|
+
this._localeDirty = this._timeZone !== this._origLocale.timeZone;
|
|
419
430
|
}
|
|
420
431
|
|
|
421
432
|
private _saveLocale() {
|
|
@@ -425,157 +436,19 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
425
436
|
new CustomEvent("jant:settings-save", {
|
|
426
437
|
bubbles: true,
|
|
427
438
|
detail: {
|
|
428
|
-
endpoint: "/settings/general/
|
|
429
|
-
data: {
|
|
430
|
-
|
|
431
|
-
dashboardLanguage: this._dashboardLanguage,
|
|
432
|
-
cjkSerifFont: this._cjkSerifFont,
|
|
433
|
-
timeZone: this._timeZone,
|
|
434
|
-
},
|
|
435
|
-
section: "language-time",
|
|
439
|
+
endpoint: "/settings/general/time",
|
|
440
|
+
data: { timeZone: this._timeZone },
|
|
441
|
+
section: "time",
|
|
436
442
|
},
|
|
437
443
|
}),
|
|
438
444
|
);
|
|
439
445
|
}
|
|
440
446
|
|
|
441
|
-
// ──
|
|
442
|
-
|
|
443
|
-
private _filteredLocaleEntries(): LocaleEntry[] {
|
|
444
|
-
const all = getSupportedLocaleEntries();
|
|
445
|
-
const query = this._localeQuery.trim().toLowerCase();
|
|
446
|
-
if (!query) return all;
|
|
447
|
-
return all.filter(
|
|
448
|
-
(e) =>
|
|
449
|
-
e.tag.toLowerCase().includes(query) ||
|
|
450
|
-
e.native.toLowerCase().includes(query) ||
|
|
451
|
-
e.english.toLowerCase().includes(query),
|
|
452
|
-
);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
private _toggleLocalePicker = () => {
|
|
456
|
-
this._localeOpen = !this._localeOpen;
|
|
457
|
-
if (!this._localeOpen) {
|
|
458
|
-
this._localeQuery = "";
|
|
459
|
-
} else {
|
|
460
|
-
// Focus the search input on next paint.
|
|
461
|
-
this.updateComplete.then(() => {
|
|
462
|
-
const input = this.querySelector<HTMLInputElement>(
|
|
463
|
-
"[data-locale-search]",
|
|
464
|
-
);
|
|
465
|
-
input?.focus();
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
};
|
|
469
|
-
|
|
470
|
-
private _selectLocale(tag: string) {
|
|
471
|
-
this._siteLanguage = tag;
|
|
472
|
-
this._localeOpen = false;
|
|
473
|
-
this._localeQuery = "";
|
|
474
|
-
this._syncLocaleDirty();
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
private _onLocalePickerDocumentClick = (e: Event) => {
|
|
478
|
-
if (!this._localeOpen) return;
|
|
479
|
-
const target = e.target as Node | null;
|
|
480
|
-
const picker = this.querySelector("[data-locale-picker]");
|
|
481
|
-
if (picker && target && !picker.contains(target)) {
|
|
482
|
-
this._localeOpen = false;
|
|
483
|
-
}
|
|
484
|
-
};
|
|
485
|
-
|
|
486
|
-
private _onLocalePickerKeydown = (e: KeyboardEvent) => {
|
|
487
|
-
if (e.isComposing || e.keyCode === 229) return;
|
|
488
|
-
if (e.key === "Escape" && this._localeOpen) {
|
|
489
|
-
this._localeOpen = false;
|
|
490
|
-
this._localeQuery = "";
|
|
491
|
-
}
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
private _renderLanguagePicker() {
|
|
495
|
-
const current = getOrBuildEntry(this._siteLanguage || "en");
|
|
496
|
-
const filtered = this._filteredLocaleEntries();
|
|
497
|
-
const searchPlaceholder =
|
|
498
|
-
this.labels.siteLanguageSearchPlaceholder || "Search…";
|
|
499
|
-
const noMatches = this.labels.siteLanguageNoMatches || "No matches.";
|
|
500
|
-
|
|
501
|
-
return html`
|
|
502
|
-
<div class="relative w-fit max-w-full" data-locale-picker>
|
|
503
|
-
<button
|
|
504
|
-
type="button"
|
|
505
|
-
class="flex h-9 w-full cursor-pointer items-center rounded-md border border-input bg-transparent bg-[image:var(--chevron-down-icon-50)] bg-position-[center_right_0.75rem] bg-size-[1rem] bg-no-repeat py-2 pl-3 pr-9 text-left text-sm shadow-xs outline-none transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 dark:bg-input/30 dark:hover:bg-input/50"
|
|
506
|
-
aria-expanded=${this._localeOpen ? "true" : "false"}
|
|
507
|
-
aria-haspopup="listbox"
|
|
508
|
-
aria-labelledby="site-language-label"
|
|
509
|
-
@click=${this._toggleLocalePicker}
|
|
510
|
-
>
|
|
511
|
-
<span class="min-w-0 truncate">${current.native}</span>
|
|
512
|
-
</button>
|
|
513
|
-
|
|
514
|
-
${this._localeOpen
|
|
515
|
-
? html`
|
|
516
|
-
<div
|
|
517
|
-
class="absolute left-0 top-full z-10 mt-1 w-80 min-w-full max-w-[calc(100vw-2rem)] max-h-72 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md"
|
|
518
|
-
>
|
|
519
|
-
<div class="border-b p-2">
|
|
520
|
-
<input
|
|
521
|
-
type="text"
|
|
522
|
-
class="input w-full"
|
|
523
|
-
data-locale-search
|
|
524
|
-
placeholder=${searchPlaceholder}
|
|
525
|
-
autocomplete="off"
|
|
526
|
-
spellcheck="false"
|
|
527
|
-
.value=${this._localeQuery}
|
|
528
|
-
@input=${(e: Event) => {
|
|
529
|
-
this._localeQuery = (e.target as HTMLInputElement).value;
|
|
530
|
-
}}
|
|
531
|
-
/>
|
|
532
|
-
</div>
|
|
533
|
-
<div role="listbox" class="max-h-56 overflow-auto py-1">
|
|
534
|
-
${filtered.length === 0
|
|
535
|
-
? html`
|
|
536
|
-
<div class="px-3 py-2 text-sm text-muted-foreground">
|
|
537
|
-
${noMatches}
|
|
538
|
-
</div>
|
|
539
|
-
`
|
|
540
|
-
: filtered.map(
|
|
541
|
-
(entry) => html`
|
|
542
|
-
<button
|
|
543
|
-
type="button"
|
|
544
|
-
role="option"
|
|
545
|
-
aria-selected=${entry.tag === this._siteLanguage
|
|
546
|
-
? "true"
|
|
547
|
-
: "false"}
|
|
548
|
-
class=${[
|
|
549
|
-
"flex w-full flex-col px-3 py-2 text-left text-sm hover:bg-accent",
|
|
550
|
-
entry.tag === this._siteLanguage
|
|
551
|
-
? "bg-accent/60"
|
|
552
|
-
: "",
|
|
553
|
-
].join(" ")}
|
|
554
|
-
@click=${() => this._selectLocale(entry.tag)}
|
|
555
|
-
>
|
|
556
|
-
<span>${entry.native}</span>
|
|
557
|
-
<span class="text-xs text-muted-foreground">
|
|
558
|
-
${entry.english}
|
|
559
|
-
</span>
|
|
560
|
-
</button>
|
|
561
|
-
`,
|
|
562
|
-
)}
|
|
563
|
-
</div>
|
|
564
|
-
</div>
|
|
565
|
-
`
|
|
566
|
-
: nothing}
|
|
567
|
-
</div>
|
|
568
|
-
`;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
// ── Feed group helpers ────────────────────────────────────────────
|
|
447
|
+
// ── Feed auto-save helpers ────────────────────────────────────────
|
|
572
448
|
|
|
573
|
-
private
|
|
574
|
-
this.
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
private _saveFeeds() {
|
|
578
|
-
if (this._feedLoading || !this._feedDirty) return;
|
|
449
|
+
private _saveMainRssFeed(value: string) {
|
|
450
|
+
if (this._feedLoading || value === this._mainRssFeed) return;
|
|
451
|
+
this._mainRssFeed = value;
|
|
579
452
|
this._feedLoading = true;
|
|
580
453
|
this.dispatchEvent(
|
|
581
454
|
new CustomEvent("jant:settings-save", {
|
|
@@ -583,7 +456,7 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
583
456
|
detail: {
|
|
584
457
|
endpoint: "/settings/general/feeds",
|
|
585
458
|
data: {
|
|
586
|
-
mainRssFeed:
|
|
459
|
+
mainRssFeed: value,
|
|
587
460
|
},
|
|
588
461
|
section: "feeds",
|
|
589
462
|
},
|
|
@@ -631,6 +504,66 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
631
504
|
);
|
|
632
505
|
}
|
|
633
506
|
|
|
507
|
+
// ── Discover helpers ──────────────────────────────────────────────
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* What this site's feeds declare, as of the controls on screen right now.
|
|
511
|
+
*
|
|
512
|
+
* The same `resolveDiscoverMode` the server runs, over the same inputs, so
|
|
513
|
+
* the checkbox cannot disagree with the feed. It has to be computed here
|
|
514
|
+
* rather than sent down resolved because one of its inputs, search indexing,
|
|
515
|
+
* is a control on this page: a value resolved on the server is correct until
|
|
516
|
+
* the first click and wrong from then until the next page load, which is
|
|
517
|
+
* exactly the state an owner reads it in. The feed switch is not — it lives
|
|
518
|
+
* in the Config Editor, so `feedsEnabled` holds for the life of the page.
|
|
519
|
+
*/
|
|
520
|
+
private _effectiveDiscoverMode(overrides: { noindex?: boolean } = {}) {
|
|
521
|
+
return resolveDiscoverMode({
|
|
522
|
+
// "" is never chosen, which is what lets the rules below decide.
|
|
523
|
+
storedValue: this._discover || null,
|
|
524
|
+
defaultValue: this.discoverDefault || null,
|
|
525
|
+
demoMode: this.demoMode,
|
|
526
|
+
noindex: overrides.noindex ?? this._noindex,
|
|
527
|
+
rssFeedsEnabled: this.feedsEnabled,
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* On writes `latest`: the directory may read any public post, and decides
|
|
533
|
+
* itself which list each one reaches — featured posts on its home, links
|
|
534
|
+
* and quotes on their own lists.
|
|
535
|
+
*
|
|
536
|
+
* A site that stored `featured` under an older release still reads as
|
|
537
|
+
* ticked, and stays `featured` until its owner touches the box: widening
|
|
538
|
+
* what the directory may show is the owner's to do, not an upgrade's.
|
|
539
|
+
*/
|
|
540
|
+
private _onDiscoverToggle(enabled: boolean) {
|
|
541
|
+
this._saveDiscover(enabled ? "latest" : "off");
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Store the choice the owner just made.
|
|
546
|
+
*
|
|
547
|
+
* The control is disabled while a save is in flight, so a second call
|
|
548
|
+
* cannot arrive before the first has answered; the guard covers the event
|
|
549
|
+
* that is already queued when that happens.
|
|
550
|
+
*/
|
|
551
|
+
private _saveDiscover(value: "latest" | "off") {
|
|
552
|
+
if (this._discoverLoading) return;
|
|
553
|
+
this._discover = value;
|
|
554
|
+
this._discoverLoading = true;
|
|
555
|
+
this.dispatchEvent(
|
|
556
|
+
new CustomEvent("jant:settings-save", {
|
|
557
|
+
bubbles: true,
|
|
558
|
+
detail: {
|
|
559
|
+
endpoint: "/settings/general/discover",
|
|
560
|
+
data: { discover: value },
|
|
561
|
+
section: "discover",
|
|
562
|
+
},
|
|
563
|
+
}),
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
|
|
634
567
|
/** Submit on Enter from non-textarea fields */
|
|
635
568
|
private _onKeydown(
|
|
636
569
|
e: globalThis.KeyboardEvent,
|
|
@@ -666,21 +599,23 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
666
599
|
?disabled=${loading || !dirty}
|
|
667
600
|
@click=${onSave}
|
|
668
601
|
>
|
|
669
|
-
${
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
602
|
+
${
|
|
603
|
+
loading
|
|
604
|
+
? html`<svg
|
|
605
|
+
class="animate-spin size-4"
|
|
606
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
607
|
+
viewBox="0 0 24 24"
|
|
608
|
+
fill="none"
|
|
609
|
+
stroke="currentColor"
|
|
610
|
+
stroke-width="2"
|
|
611
|
+
stroke-linecap="round"
|
|
612
|
+
stroke-linejoin="round"
|
|
613
|
+
role="status"
|
|
614
|
+
>
|
|
615
|
+
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
616
|
+
</svg>`
|
|
617
|
+
: nothing
|
|
618
|
+
}
|
|
684
619
|
${this.labels.save}
|
|
685
620
|
</button>
|
|
686
621
|
</div>
|
|
@@ -711,10 +646,8 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
711
646
|
value=${value}
|
|
712
647
|
class="mt-1"
|
|
713
648
|
.checked=${checked}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
this._syncFeedDirty();
|
|
717
|
-
}}
|
|
649
|
+
?disabled=${this._feedLoading}
|
|
650
|
+
@change=${() => this._saveMainRssFeed(value)}
|
|
718
651
|
/>
|
|
719
652
|
<div>
|
|
720
653
|
<div class="font-medium">${title}</div>
|
|
@@ -724,47 +657,39 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
724
657
|
`;
|
|
725
658
|
}
|
|
726
659
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
private _renderFeedInfoRow(
|
|
741
|
-
label: string,
|
|
742
|
-
value: string,
|
|
743
|
-
description?: string,
|
|
744
|
-
) {
|
|
660
|
+
/**
|
|
661
|
+
* One feed address with a copy button.
|
|
662
|
+
*
|
|
663
|
+
* The same field `ui/shared/CopyField.tsx` renders on the public subscribe
|
|
664
|
+
* page, driven by the same `client/copy-field.ts` enhancer — hence the shared
|
|
665
|
+
* classes. lit-html cannot interpolate attribute names, so the enhancer's
|
|
666
|
+
* hooks are spelled out; `jant-settings-general.test.ts` asserts they match.
|
|
667
|
+
* Unlike the public page the button is not rendered hidden: settings is
|
|
668
|
+
* behind auth, where the client bundle is always present.
|
|
669
|
+
*/
|
|
670
|
+
private _renderFeedUrl(label: string, value: string, description?: string) {
|
|
745
671
|
return html`
|
|
746
|
-
<div class
|
|
672
|
+
<div class=${COPY_FIELD_CLASS} data-copy-field-root>
|
|
747
673
|
<p class="text-sm font-medium">${label}</p>
|
|
748
|
-
${
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
674
|
+
${
|
|
675
|
+
description
|
|
676
|
+
? html`<p class="text-sm text-muted-foreground">${description}</p>`
|
|
677
|
+
: ""
|
|
678
|
+
}
|
|
679
|
+
<div class=${COPY_FIELD_CONTROL_CLASS}>
|
|
752
680
|
<input
|
|
753
681
|
type="text"
|
|
754
|
-
class
|
|
682
|
+
class=${COPY_FIELD_INPUT_CLASS}
|
|
755
683
|
.value=${value}
|
|
756
684
|
readonly
|
|
757
685
|
aria-label=${label}
|
|
758
|
-
|
|
759
|
-
(e.currentTarget as HTMLInputElement).select()}
|
|
760
|
-
@focus=${(e: Event) =>
|
|
761
|
-
(e.currentTarget as HTMLInputElement).select()}
|
|
686
|
+
data-copy-field-value
|
|
762
687
|
/>
|
|
763
688
|
<button
|
|
764
689
|
type="button"
|
|
765
|
-
class
|
|
766
|
-
data-copy-
|
|
767
|
-
|
|
690
|
+
class=${COPY_FIELD_BUTTON_CLASS}
|
|
691
|
+
data-copy-field=${this.labels.feedUrlCopied}
|
|
692
|
+
data-copy-field-failed=${this.labels.copyFailed}
|
|
768
693
|
>
|
|
769
694
|
${this.labels.copy}
|
|
770
695
|
</button>
|
|
@@ -779,319 +704,473 @@ export class JantSettingsGeneral extends LitElement {
|
|
|
779
704
|
return html`
|
|
780
705
|
<div class="mt-2 text-sm text-muted-foreground" data-about-page-row>
|
|
781
706
|
${this.labels.aboutPagePrompt}
|
|
782
|
-
${
|
|
783
|
-
|
|
784
|
-
<a
|
|
785
|
-
class="font-medium text-foreground underline-offset-4 hover:underline"
|
|
786
|
-
href=${this.aboutEditUrl}
|
|
787
|
-
>
|
|
788
|
-
${this.labels.editAboutPage}
|
|
789
|
-
</a>
|
|
790
|
-
`
|
|
791
|
-
: status.state === "missing"
|
|
707
|
+
${
|
|
708
|
+
status.state === "ready"
|
|
792
709
|
? html`
|
|
793
|
-
<
|
|
794
|
-
class="
|
|
795
|
-
|
|
796
|
-
action=${this.aboutCreateUrl}
|
|
710
|
+
<a
|
|
711
|
+
class="font-medium text-foreground underline-offset-4 hover:underline"
|
|
712
|
+
href=${this.aboutEditUrl}
|
|
797
713
|
>
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
class="inline cursor-pointer border-0 bg-transparent p-0 font-medium text-foreground underline-offset-4 hover:underline"
|
|
801
|
-
>
|
|
802
|
-
${this.labels.createAboutPage}
|
|
803
|
-
</button>
|
|
804
|
-
</form>
|
|
714
|
+
${this.labels.editAboutPage}
|
|
715
|
+
</a>
|
|
805
716
|
`
|
|
806
|
-
:
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
717
|
+
: status.state === "missing"
|
|
718
|
+
? html`
|
|
719
|
+
<form
|
|
720
|
+
class="inline"
|
|
721
|
+
method="post"
|
|
722
|
+
action=${this.aboutCreateUrl}
|
|
723
|
+
>
|
|
724
|
+
<button
|
|
725
|
+
type="submit"
|
|
726
|
+
class="inline cursor-pointer border-0 bg-transparent p-0 font-medium text-foreground underline-offset-4 hover:underline"
|
|
727
|
+
>
|
|
728
|
+
${this.labels.createAboutPage}
|
|
729
|
+
</button>
|
|
730
|
+
</form>
|
|
731
|
+
`
|
|
732
|
+
: html`
|
|
733
|
+
<span class="text-destructive"
|
|
734
|
+
>${this.labels.aboutPageConflict}</span
|
|
735
|
+
>
|
|
736
|
+
`
|
|
737
|
+
}
|
|
811
738
|
</div>
|
|
812
739
|
`;
|
|
813
740
|
}
|
|
814
741
|
|
|
815
|
-
|
|
742
|
+
// ── Sections, in the order `render()` lists them ──────────────────
|
|
743
|
+
//
|
|
744
|
+
// Each section carries only its own spacing (`SECTION_CLASS`); the rules
|
|
745
|
+
// between them are drawn by the container, so reordering is a change to
|
|
746
|
+
// `render()` alone.
|
|
747
|
+
|
|
748
|
+
private _renderSiteSection() {
|
|
816
749
|
return html`
|
|
817
|
-
<
|
|
818
|
-
|
|
819
|
-
|
|
750
|
+
<section
|
|
751
|
+
class="${SECTION_CLASS} gap-4"
|
|
752
|
+
@keydown=${(e: globalThis.KeyboardEvent) =>
|
|
753
|
+
this._onKeydown(
|
|
754
|
+
e,
|
|
755
|
+
() => this._saveSite(),
|
|
756
|
+
this._siteDirty,
|
|
757
|
+
this._siteLoading,
|
|
758
|
+
)}
|
|
759
|
+
>
|
|
760
|
+
${this._renderSectionTitle(this.labels.site)}
|
|
761
|
+
<div class="field">
|
|
762
|
+
<label class="label">${this.labels.siteName}</label>
|
|
763
|
+
<input
|
|
764
|
+
type="text"
|
|
765
|
+
class="input"
|
|
766
|
+
maxlength=${MAX_SITE_NAME_LENGTH}
|
|
767
|
+
.value=${this._siteName}
|
|
768
|
+
placeholder=${this.siteNameFallback}
|
|
769
|
+
@input=${(e: Event) => {
|
|
770
|
+
this._siteName = (e.target as HTMLInputElement).value;
|
|
771
|
+
this._syncSiteDirty();
|
|
772
|
+
}}
|
|
773
|
+
/>
|
|
820
774
|
</div>
|
|
821
775
|
|
|
822
|
-
<
|
|
823
|
-
class="
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
)}
|
|
831
|
-
>
|
|
832
|
-
${this._renderSectionTitle(this.labels.site)}
|
|
833
|
-
<div class="field">
|
|
834
|
-
<label class="label">${this.labels.siteName}</label>
|
|
835
|
-
<input
|
|
836
|
-
type="text"
|
|
837
|
-
class="input"
|
|
838
|
-
maxlength=${MAX_SITE_NAME_LENGTH}
|
|
839
|
-
.value=${this._siteName}
|
|
840
|
-
placeholder=${this.siteNameFallback}
|
|
841
|
-
@input=${(e: Event) => {
|
|
842
|
-
this._siteName = (e.target as HTMLInputElement).value;
|
|
843
|
-
this._syncSiteDirty();
|
|
844
|
-
}}
|
|
845
|
-
/>
|
|
846
|
-
</div>
|
|
776
|
+
<div class="field">
|
|
777
|
+
<label class="label">${this.labels.aboutBlog}</label>
|
|
778
|
+
<div class="settings-tiptap-editor" data-settings-desc-editor></div>
|
|
779
|
+
<p class="text-sm text-muted-foreground mt-1">
|
|
780
|
+
${this.labels.aboutBlogHelp}
|
|
781
|
+
</p>
|
|
782
|
+
${this._renderAboutPageRow()}
|
|
783
|
+
</div>
|
|
847
784
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
</div>
|
|
785
|
+
<div class="field">
|
|
786
|
+
<label class="label">${this.labels.siteFooter}</label>
|
|
787
|
+
<div class="settings-tiptap-editor" data-settings-footer-editor></div>
|
|
788
|
+
<p class="text-sm text-muted-foreground mt-1">
|
|
789
|
+
${this.labels.footerHelp}
|
|
790
|
+
</p>
|
|
791
|
+
</div>
|
|
856
792
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
<p class="text-sm text-muted-foreground mt-1">
|
|
864
|
-
${this.labels.footerHelp}
|
|
865
|
-
</p>
|
|
866
|
-
</div>
|
|
793
|
+
${this._renderSaveAction(this._siteLoading, this._siteDirty, () =>
|
|
794
|
+
this._saveSite(),
|
|
795
|
+
)}
|
|
796
|
+
</section>
|
|
797
|
+
`;
|
|
798
|
+
}
|
|
867
799
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
<
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
800
|
+
/**
|
|
801
|
+
* Search engine indexing and Jant Discover.
|
|
802
|
+
*
|
|
803
|
+
* The two groups sit further apart than a section's heading sits from its
|
|
804
|
+
* first control, so the Discover help text does not read as belonging to
|
|
805
|
+
* the indexing checkbox.
|
|
806
|
+
*/
|
|
807
|
+
private _renderVisibilitySection() {
|
|
808
|
+
return html`
|
|
809
|
+
<section class="${SECTION_CLASS} gap-6">
|
|
810
|
+
${this._renderSectionTitle(this.labels.siteVisibility)}
|
|
811
|
+
<div class="flex flex-col gap-2">
|
|
812
|
+
<label class="flex items-center gap-2 cursor-pointer">
|
|
813
|
+
<input
|
|
814
|
+
type="checkbox"
|
|
815
|
+
class="checkbox"
|
|
816
|
+
.checked=${!this._noindex}
|
|
817
|
+
?disabled=${this.demoMode || this._searchLoading}
|
|
818
|
+
@change=${(e: Event) =>
|
|
819
|
+
this._saveSearchToggle((e.target as HTMLInputElement).checked)}
|
|
820
|
+
/>
|
|
821
|
+
<span>${this.labels.allowIndexing}</span>
|
|
822
|
+
</label>
|
|
823
|
+
${
|
|
824
|
+
this.demoMode
|
|
825
|
+
? html`<p class="text-sm text-muted-foreground">
|
|
826
|
+
${this.labels.demoSeoLocked}
|
|
827
|
+
</p>`
|
|
828
|
+
: nothing
|
|
829
|
+
}
|
|
830
|
+
</div>
|
|
831
|
+
${this._renderDiscoverForm()}
|
|
832
|
+
</section>
|
|
833
|
+
`;
|
|
834
|
+
}
|
|
899
835
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
`,
|
|
921
|
-
)}
|
|
922
|
-
</select>
|
|
923
|
-
<p class="text-sm text-muted-foreground mt-1">
|
|
924
|
-
${this.labels.dashboardLanguageHelp}
|
|
925
|
-
</p>
|
|
836
|
+
private _renderFeedsSection() {
|
|
837
|
+
return html`
|
|
838
|
+
<section class="${SECTION_CLASS} gap-4">
|
|
839
|
+
${this._renderSectionTitle(this.labels.feeds)}
|
|
840
|
+
<div class="field">
|
|
841
|
+
<p class="label">${this.labels.mainRssFeed}</p>
|
|
842
|
+
<p class="text-sm text-muted-foreground mt-1">
|
|
843
|
+
${this.labels.mainRssFeedHelp}
|
|
844
|
+
</p>
|
|
845
|
+
<div class="mt-3 flex flex-col gap-2">
|
|
846
|
+
${this._renderMainRssFeedOption(
|
|
847
|
+
"featured",
|
|
848
|
+
this.labels.featuredFeedOption,
|
|
849
|
+
this.labels.featuredFeedOptionDescription,
|
|
850
|
+
)}
|
|
851
|
+
${this._renderMainRssFeedOption(
|
|
852
|
+
"latest",
|
|
853
|
+
this.labels.latestFeedOption,
|
|
854
|
+
this.labels.latestFeedOptionDescription,
|
|
855
|
+
)}
|
|
926
856
|
</div>
|
|
857
|
+
<p class="text-sm text-muted-foreground mt-2">
|
|
858
|
+
${this.labels.mainRssFeedWarning}
|
|
859
|
+
</p>
|
|
860
|
+
</div>
|
|
927
861
|
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
<
|
|
931
|
-
class="
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
</
|
|
951
|
-
</div>
|
|
862
|
+
<div class="rounded-xl border border-border/70 bg-muted/30 p-4">
|
|
863
|
+
<div class="flex flex-col gap-4">
|
|
864
|
+
<div class="flex flex-col gap-1">
|
|
865
|
+
<p class="text-sm font-medium">
|
|
866
|
+
${this.labels.availableFeedUrls}
|
|
867
|
+
</p>
|
|
868
|
+
<p class="text-sm text-muted-foreground">
|
|
869
|
+
${this.labels.availableFeedUrlsHelp}
|
|
870
|
+
${
|
|
871
|
+
this.feedsDocsUrl
|
|
872
|
+
? html`
|
|
873
|
+
<a
|
|
874
|
+
href=${this.feedsDocsUrl}
|
|
875
|
+
target="_blank"
|
|
876
|
+
rel="noopener noreferrer"
|
|
877
|
+
class="underline hover:text-foreground transition-colors"
|
|
878
|
+
>${this.labels.feedsDocs}</a
|
|
879
|
+
>
|
|
880
|
+
`
|
|
881
|
+
: ""
|
|
882
|
+
}
|
|
883
|
+
</p>
|
|
884
|
+
</div>
|
|
952
885
|
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
>
|
|
968
|
-
${tz.label}
|
|
969
|
-
</option>
|
|
970
|
-
`,
|
|
971
|
-
)}
|
|
972
|
-
</select>
|
|
886
|
+
${this._renderFeedUrl(this.labels.mainFeedUrl, this.mainFeedUrl)}
|
|
887
|
+
${this._renderFeedUrl(
|
|
888
|
+
this.labels.latestFeedUrl,
|
|
889
|
+
this.latestFeedUrl,
|
|
890
|
+
)}
|
|
891
|
+
${this._renderFeedUrl(
|
|
892
|
+
this.labels.featuredFeedUrl,
|
|
893
|
+
this.featuredFeedUrl,
|
|
894
|
+
)}
|
|
895
|
+
${this._renderFeedUrl(
|
|
896
|
+
this.labels.archiveFeedUrl,
|
|
897
|
+
this.archiveFeedUrl,
|
|
898
|
+
this.labels.archiveFeedUrlHelp,
|
|
899
|
+
)}
|
|
973
900
|
</div>
|
|
901
|
+
</div>
|
|
902
|
+
</section>
|
|
903
|
+
`;
|
|
904
|
+
}
|
|
974
905
|
|
|
975
|
-
|
|
976
|
-
|
|
906
|
+
private _renderTimeSection() {
|
|
907
|
+
return html`
|
|
908
|
+
<section
|
|
909
|
+
class="${SECTION_CLASS} gap-4"
|
|
910
|
+
@keydown=${(e: globalThis.KeyboardEvent) =>
|
|
911
|
+
this._onKeydown(
|
|
912
|
+
e,
|
|
913
|
+
() => this._saveLocale(),
|
|
914
|
+
this._localeDirty,
|
|
915
|
+
this._localeLoading,
|
|
977
916
|
)}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
<
|
|
981
|
-
class="
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
(
|
|
986
|
-
this.
|
|
987
|
-
|
|
917
|
+
>
|
|
918
|
+
${this._renderSectionTitle(this.labels.timeSection)}
|
|
919
|
+
<div class="field">
|
|
920
|
+
<label class="label">${this.labels.timeZone}</label>
|
|
921
|
+
<select
|
|
922
|
+
class="select"
|
|
923
|
+
@change=${(e: Event) => {
|
|
924
|
+
this._timeZone = (e.target as HTMLSelectElement).value;
|
|
925
|
+
this._syncLocaleDirty();
|
|
926
|
+
}}
|
|
927
|
+
>
|
|
928
|
+
${this.timezones.map(
|
|
929
|
+
(tz) => html`
|
|
930
|
+
<option
|
|
931
|
+
value=${tz.value}
|
|
932
|
+
?selected=${this._timeZone === tz.value}
|
|
933
|
+
>
|
|
934
|
+
${tz.label}
|
|
935
|
+
</option>
|
|
936
|
+
`,
|
|
988
937
|
)}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
<div class="field">
|
|
992
|
-
<p class="label">${this.labels.mainRssFeed}</p>
|
|
993
|
-
<p class="text-sm text-muted-foreground mt-1">
|
|
994
|
-
${this.labels.mainRssFeedHelp}
|
|
995
|
-
</p>
|
|
996
|
-
<div class="mt-3 flex flex-col gap-2">
|
|
997
|
-
${this._renderMainRssFeedOption(
|
|
998
|
-
"featured",
|
|
999
|
-
this.labels.featuredFeedOption,
|
|
1000
|
-
this.labels.featuredFeedOptionDescription,
|
|
1001
|
-
)}
|
|
1002
|
-
${this._renderMainRssFeedOption(
|
|
1003
|
-
"latest",
|
|
1004
|
-
this.labels.latestFeedOption,
|
|
1005
|
-
this.labels.latestFeedOptionDescription,
|
|
1006
|
-
)}
|
|
1007
|
-
</div>
|
|
1008
|
-
<p class="text-sm text-muted-foreground mt-2">
|
|
1009
|
-
${this.labels.mainRssFeedWarning}
|
|
1010
|
-
</p>
|
|
1011
|
-
</div>
|
|
938
|
+
</select>
|
|
939
|
+
</div>
|
|
1012
940
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
<p class="text-sm text-muted-foreground">
|
|
1020
|
-
${this.labels.availableFeedUrlsHelp}
|
|
1021
|
-
</p>
|
|
1022
|
-
</div>
|
|
1023
|
-
|
|
1024
|
-
${this._renderFeedInfoRow(
|
|
1025
|
-
this.labels.mainFeedUrl,
|
|
1026
|
-
this.mainFeedUrl,
|
|
1027
|
-
)}
|
|
1028
|
-
${this._renderFeedInfoRow(
|
|
1029
|
-
this.labels.latestFeedUrl,
|
|
1030
|
-
this.latestFeedUrl,
|
|
1031
|
-
)}
|
|
1032
|
-
${this._renderFeedInfoRow(
|
|
1033
|
-
this.labels.featuredFeedUrl,
|
|
1034
|
-
this.featuredFeedUrl,
|
|
1035
|
-
)}
|
|
1036
|
-
${this._renderFeedInfoRow(
|
|
1037
|
-
this.labels.archiveFeedUrl,
|
|
1038
|
-
this.archiveFeedUrl,
|
|
1039
|
-
this.labels.archiveFeedUrlHelp,
|
|
1040
|
-
)}
|
|
1041
|
-
</div>
|
|
1042
|
-
</div>
|
|
941
|
+
${this._renderSaveAction(this._localeLoading, this._localeDirty, () =>
|
|
942
|
+
this._saveLocale(),
|
|
943
|
+
)}
|
|
944
|
+
</section>
|
|
945
|
+
`;
|
|
946
|
+
}
|
|
1043
947
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
948
|
+
private _renderHomeSection() {
|
|
949
|
+
return html`
|
|
950
|
+
<section class="${SECTION_CLASS} gap-4">
|
|
951
|
+
${this._renderSectionTitle(this.labels.home)}
|
|
952
|
+
<label class="flex items-center gap-2 cursor-pointer">
|
|
953
|
+
<input
|
|
954
|
+
type="checkbox"
|
|
955
|
+
class="checkbox"
|
|
956
|
+
.checked=${this._showJantBrandingOnHome}
|
|
957
|
+
?disabled=${this._homeLoading}
|
|
958
|
+
@change=${(e: Event) =>
|
|
959
|
+
this._saveHomeToggle((e.target as HTMLInputElement).checked)}
|
|
960
|
+
/>
|
|
961
|
+
<span>${this.labels.showJantBrandingOnHome}</span>
|
|
962
|
+
</label>
|
|
963
|
+
</section>
|
|
964
|
+
`;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Jant Discover.
|
|
969
|
+
*
|
|
970
|
+
* Saves on change, like the indexing checkbox next to it: the box is a
|
|
971
|
+
* complete answer on its own, so there is nothing a Save button would be
|
|
972
|
+
* waiting for. One box and no sub-choice — which of a blog's posts reach
|
|
973
|
+
* which list is the directory's own rule, stated on the directory.
|
|
974
|
+
*
|
|
975
|
+
* Ticking the box is also what announces a self-hosted site to the
|
|
976
|
+
* directory.
|
|
977
|
+
*/
|
|
978
|
+
private _renderDiscoverForm() {
|
|
979
|
+
const effective = this._effectiveDiscoverMode();
|
|
980
|
+
const enabled = effective !== "none";
|
|
981
|
+
const locked = this.demoMode || !this.feedsEnabled;
|
|
982
|
+
// The box unticks itself when search indexing goes off, and a control that
|
|
983
|
+
// moves on its own has to say why. Asked as a counterfactual rather than
|
|
984
|
+
// read off `noindex`, so the line appears only where it is the reason: a
|
|
985
|
+
// site the deployment would have listed, held back by that one setting.
|
|
986
|
+
// A site nobody would list either way is not being held back by anything,
|
|
987
|
+
// and an owner who ticked the box is not affected at all.
|
|
988
|
+
const heldBackBySearch =
|
|
989
|
+
!locked &&
|
|
990
|
+
!enabled &&
|
|
991
|
+
this._effectiveDiscoverMode({ noindex: false }) !== "none";
|
|
1048
992
|
|
|
1049
|
-
|
|
1050
|
-
|
|
993
|
+
return html`
|
|
994
|
+
<div class="flex flex-col gap-3">
|
|
995
|
+
<div class="flex flex-col gap-2">
|
|
1051
996
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
1052
997
|
<input
|
|
1053
998
|
type="checkbox"
|
|
1054
999
|
class="checkbox"
|
|
1055
|
-
.checked=${
|
|
1056
|
-
?disabled=${this.
|
|
1000
|
+
.checked=${enabled}
|
|
1001
|
+
?disabled=${locked || this._discoverLoading}
|
|
1057
1002
|
@change=${(e: Event) =>
|
|
1058
|
-
this.
|
|
1003
|
+
this._onDiscoverToggle((e.target as HTMLInputElement).checked)}
|
|
1059
1004
|
/>
|
|
1060
|
-
<span>${this.labels.
|
|
1005
|
+
<span>${this.labels.discoverEnabled}</span>
|
|
1061
1006
|
</label>
|
|
1062
|
-
|
|
1007
|
+
<p class="text-sm text-muted-foreground">
|
|
1008
|
+
${this._renderDiscoverHelp(
|
|
1009
|
+
locked
|
|
1010
|
+
? this.demoMode
|
|
1011
|
+
? this.labels.discoverDemoLocked
|
|
1012
|
+
: this.labels.discoverFeedsOffLocked
|
|
1013
|
+
: this.labels.discoverIntro,
|
|
1014
|
+
)}
|
|
1015
|
+
</p>
|
|
1016
|
+
${
|
|
1017
|
+
heldBackBySearch
|
|
1018
|
+
? html`<p class="text-sm text-muted-foreground">
|
|
1019
|
+
${this.labels.discoverSearchOff}
|
|
1020
|
+
</p>`
|
|
1021
|
+
: nothing
|
|
1022
|
+
}
|
|
1023
|
+
</div>
|
|
1024
|
+
${locked ? nothing : this._renderDiscoverStatus()}
|
|
1063
1025
|
</div>
|
|
1064
1026
|
`;
|
|
1065
1027
|
}
|
|
1066
1028
|
|
|
1067
|
-
|
|
1029
|
+
/**
|
|
1030
|
+
* The help line under the checkbox, with each page it names linking to that
|
|
1031
|
+
* page.
|
|
1032
|
+
*
|
|
1033
|
+
* Whichever line is showing goes through here, the usual one or the notice
|
|
1034
|
+
* that replaces it while the box is locked, so a demo site's notice links the
|
|
1035
|
+
* directory's name too. Which words link where is `discoverIntroRuns`'s to
|
|
1036
|
+
* decide, so the setup screen, which shows the same opening, links the same
|
|
1037
|
+
* words.
|
|
1038
|
+
*/
|
|
1039
|
+
private _renderDiscoverHelp(line: string) {
|
|
1040
|
+
const runs = discoverIntroRuns(
|
|
1041
|
+
line ?? "",
|
|
1042
|
+
{
|
|
1043
|
+
name: this.labels.discoverName ?? "",
|
|
1044
|
+
rules: this.labels.discoverRules ?? "",
|
|
1045
|
+
},
|
|
1046
|
+
this.discoverPages,
|
|
1047
|
+
);
|
|
1048
|
+
|
|
1049
|
+
return runs.map((run) =>
|
|
1050
|
+
run.href
|
|
1051
|
+
? html`<a
|
|
1052
|
+
href=${run.href}
|
|
1053
|
+
target="_blank"
|
|
1054
|
+
rel="noopener noreferrer"
|
|
1055
|
+
class="underline hover:text-foreground transition-colors"
|
|
1056
|
+
>${run.text}</a
|
|
1057
|
+
>`
|
|
1058
|
+
: run.text,
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* What is left to say once the controls above have spoken.
|
|
1064
|
+
*
|
|
1065
|
+
* Not a status report — a site whose Discover setting is doing exactly what
|
|
1066
|
+
* it says gets nothing here, and no heading announces a block that is
|
|
1067
|
+
* usually absent. The server sends a line only for a problem, a task, or an
|
|
1068
|
+
* answer still outstanding; deliberately all local evidence, because the
|
|
1069
|
+
* directory takes no status queries and cannot be asked whether a person has
|
|
1070
|
+
* moderated the site.
|
|
1071
|
+
*
|
|
1072
|
+
* The announce button appears when the directory has not heard from this
|
|
1073
|
+
* site; the manual form only when an announcement actually failed — beside a
|
|
1074
|
+
* working one it would read as a normal route in rather than the recovery it
|
|
1075
|
+
* is. The lines are what the server sent with the page, so ticking the box
|
|
1076
|
+
* does not rewrite them; the next load does.
|
|
1077
|
+
*/
|
|
1078
|
+
private _renderDiscoverStatus() {
|
|
1079
|
+
const status = this._parsedDiscoverStatus();
|
|
1080
|
+
if (!status) return nothing;
|
|
1081
|
+
// The server wrote these for the mode the page loaded with. A blog that
|
|
1082
|
+
// has just switched itself off is not the blog they describe.
|
|
1083
|
+
if (this._effectiveDiscoverMode() === "none") return nothing;
|
|
1084
|
+
if (status.lines.length === 0 && !status.showAnnounce) return nothing;
|
|
1085
|
+
|
|
1068
1086
|
return html`
|
|
1069
|
-
<
|
|
1070
|
-
${
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1087
|
+
<div class="flex flex-col gap-1 border-t pt-3">
|
|
1088
|
+
${status.lines.map(
|
|
1089
|
+
(line) => html`<p class="text-sm text-muted-foreground">${line}</p>`,
|
|
1090
|
+
)}
|
|
1091
|
+
${
|
|
1092
|
+
status.showAnnounce
|
|
1093
|
+
? html`
|
|
1094
|
+
<div class="flex flex-wrap items-center gap-3 mt-1">
|
|
1095
|
+
<button
|
|
1096
|
+
type="button"
|
|
1097
|
+
class="btn-outline"
|
|
1098
|
+
?disabled=${this._discoverLoading}
|
|
1099
|
+
@click=${() => this._announce()}
|
|
1100
|
+
>
|
|
1101
|
+
${this.labels.discoverAnnounce}
|
|
1102
|
+
</button>
|
|
1103
|
+
${
|
|
1104
|
+
status.submitUrl
|
|
1105
|
+
? html`<a
|
|
1106
|
+
class="text-sm underline hover:text-foreground transition-colors"
|
|
1107
|
+
href=${status.submitUrl}
|
|
1108
|
+
target="_blank"
|
|
1109
|
+
rel="noopener noreferrer"
|
|
1110
|
+
>${this.labels.discoverAnnounceManual}</a
|
|
1111
|
+
>`
|
|
1112
|
+
: nothing
|
|
1113
|
+
}
|
|
1114
|
+
</div>
|
|
1115
|
+
`
|
|
1116
|
+
: nothing
|
|
1117
|
+
}
|
|
1118
|
+
</div>
|
|
1088
1119
|
`;
|
|
1089
1120
|
}
|
|
1090
1121
|
|
|
1122
|
+
private _parsedDiscoverStatus(): {
|
|
1123
|
+
lines: string[];
|
|
1124
|
+
showAnnounce: boolean;
|
|
1125
|
+
submitUrl: string | null;
|
|
1126
|
+
} | null {
|
|
1127
|
+
if (!this.discoverStatus) return null;
|
|
1128
|
+
try {
|
|
1129
|
+
const parsed: unknown = JSON.parse(this.discoverStatus);
|
|
1130
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
1131
|
+
const value = parsed as Record<string, unknown>;
|
|
1132
|
+
const lines = Array.isArray(value["lines"])
|
|
1133
|
+
? value["lines"].filter(
|
|
1134
|
+
(line): line is string => typeof line === "string",
|
|
1135
|
+
)
|
|
1136
|
+
: [];
|
|
1137
|
+
return {
|
|
1138
|
+
lines,
|
|
1139
|
+
showAnnounce: value["showAnnounce"] === true,
|
|
1140
|
+
submitUrl:
|
|
1141
|
+
typeof value["submitUrl"] === "string" ? value["submitUrl"] : null,
|
|
1142
|
+
};
|
|
1143
|
+
} catch {
|
|
1144
|
+
// A settings page that renders without its status block is far better
|
|
1145
|
+
// than one that does not render.
|
|
1146
|
+
return null;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
private _announce() {
|
|
1151
|
+
if (this._discoverLoading) return;
|
|
1152
|
+
this._discoverLoading = true;
|
|
1153
|
+
this.dispatchEvent(
|
|
1154
|
+
new CustomEvent("jant:settings-save", {
|
|
1155
|
+
bubbles: true,
|
|
1156
|
+
detail: {
|
|
1157
|
+
endpoint: "/settings/general/discover/announce",
|
|
1158
|
+
data: {},
|
|
1159
|
+
section: "discover",
|
|
1160
|
+
},
|
|
1161
|
+
}),
|
|
1162
|
+
);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1091
1165
|
render() {
|
|
1092
1166
|
return html`
|
|
1093
|
-
<div class="flex flex-col">
|
|
1094
|
-
|
|
1167
|
+
<div class="flex flex-col gap-8">
|
|
1168
|
+
<h2 class="text-lg font-semibold">${this.labels.general}</h2>
|
|
1169
|
+
<div class="flex flex-col divide-y">
|
|
1170
|
+
${this._renderSiteSection()} ${this._renderVisibilitySection()}
|
|
1171
|
+
${this._renderFeedsSection()} ${this._renderTimeSection()}
|
|
1172
|
+
${this._renderHomeSection()}
|
|
1173
|
+
</div>
|
|
1095
1174
|
</div>
|
|
1096
1175
|
`;
|
|
1097
1176
|
}
|