@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
|
@@ -24,19 +24,25 @@ import {
|
|
|
24
24
|
responsiveSortableOptions,
|
|
25
25
|
revertSortableDomMove,
|
|
26
26
|
} from "../sortable-list.js";
|
|
27
|
+
import { isFeedNavKey } from "../../types/constants.js";
|
|
27
28
|
import { showConfirmDialog } from "../confirm.js";
|
|
28
29
|
import { showToast } from "../toast.js";
|
|
29
|
-
import { publicPath } from "../runtime-paths.js";
|
|
30
|
+
import { publicPath, sitePathPrefix } from "../runtime-paths.js";
|
|
30
31
|
import { applySiteHeaderHtml } from "../site-header-fragment.js";
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
|
|
34
|
-
getCollectionPagePath,
|
|
35
|
-
} from "../../lib/collection-paths.js";
|
|
32
|
+
import { getCollectionPagePath } from "../../lib/collection-paths.js";
|
|
33
|
+
import { FUNNEL_ICON_SVG } from "../icons.js";
|
|
34
|
+
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
36
35
|
import { getSlugValidationIssue } from "../../lib/slug-format.js";
|
|
36
|
+
import {
|
|
37
|
+
looksLikeAddress,
|
|
38
|
+
normalizePath,
|
|
39
|
+
toInternalPath,
|
|
40
|
+
} from "../../lib/url.js";
|
|
37
41
|
import type { CollectionSubmitDetail } from "./collection-types.js";
|
|
38
42
|
import "./jant-collection-form.js";
|
|
43
|
+
import { openCollectionDialog } from "../collection-dialog-host.js";
|
|
39
44
|
import type {
|
|
45
|
+
NavAddressResolution,
|
|
40
46
|
NavManagerCollection,
|
|
41
47
|
NavManagerItem,
|
|
42
48
|
NavManagerLabels,
|
|
@@ -101,6 +107,7 @@ export class JantNavManager extends LitElement {
|
|
|
101
107
|
_pageDialogView: { state: true },
|
|
102
108
|
_pageQuery: { state: true },
|
|
103
109
|
_pages: { state: true },
|
|
110
|
+
_pageAddress: { state: true },
|
|
104
111
|
_pageSearchLoading: { state: true },
|
|
105
112
|
_pageSearchError: { state: true },
|
|
106
113
|
_selectedPageIndex: { state: true },
|
|
@@ -148,6 +155,8 @@ export class JantNavManager extends LitElement {
|
|
|
148
155
|
declare _pageDialogView: PageDialogView;
|
|
149
156
|
declare _pageQuery: string;
|
|
150
157
|
declare _pages: NavManagerPage[];
|
|
158
|
+
/** What the last pasted address turned out to be, when one was pasted. */
|
|
159
|
+
declare _pageAddress: NavAddressResolution | null;
|
|
151
160
|
declare _pageSearchLoading: boolean;
|
|
152
161
|
declare _pageSearchError: boolean;
|
|
153
162
|
declare _selectedPageIndex: number;
|
|
@@ -253,6 +262,7 @@ export class JantNavManager extends LitElement {
|
|
|
253
262
|
this._pageDialogView = "picker";
|
|
254
263
|
this._pageQuery = "";
|
|
255
264
|
this._pages = [];
|
|
265
|
+
this._pageAddress = null;
|
|
256
266
|
this._pageSearchLoading = false;
|
|
257
267
|
this._pageSearchError = false;
|
|
258
268
|
this._selectedPageIndex = 0;
|
|
@@ -323,8 +333,16 @@ export class JantNavManager extends LitElement {
|
|
|
323
333
|
? this.systemNavItems.find((config) => config.key === item.systemKey)
|
|
324
334
|
?.label
|
|
325
335
|
: undefined;
|
|
336
|
+
// `targetTitle` comes back on mutation responses, which carry the raw nav
|
|
337
|
+
// item rather than the server-rendered `displayLabel` — without it a page
|
|
338
|
+
// or collection added with no label of its own would render blank until
|
|
339
|
+
// the next full load.
|
|
326
340
|
const displayLabel =
|
|
327
|
-
item.label.trim() ||
|
|
341
|
+
item.label.trim() ||
|
|
342
|
+
systemLabel ||
|
|
343
|
+
item.displayLabel ||
|
|
344
|
+
item.targetTitle?.trim() ||
|
|
345
|
+
item.label;
|
|
328
346
|
|
|
329
347
|
return { ...item, displayLabel };
|
|
330
348
|
}
|
|
@@ -359,6 +377,7 @@ export class JantNavManager extends LitElement {
|
|
|
359
377
|
this._pageDialogView = "picker";
|
|
360
378
|
this._pageQuery = "";
|
|
361
379
|
this._pages = [];
|
|
380
|
+
this._pageAddress = null;
|
|
362
381
|
this._pageSearchError = false;
|
|
363
382
|
this._selectedPageIndex = 0;
|
|
364
383
|
this._addingPageId = null;
|
|
@@ -387,6 +406,45 @@ export class JantNavManager extends LitElement {
|
|
|
387
406
|
void this.updateComplete.then(() => trigger?.focus());
|
|
388
407
|
}
|
|
389
408
|
|
|
409
|
+
/**
|
|
410
|
+
* Look up the page at an address the author pasted.
|
|
411
|
+
*
|
|
412
|
+
* Searching titles cannot answer someone holding a URL, and the answer is
|
|
413
|
+
* often a reason rather than a result — a draft, a private page, a page that
|
|
414
|
+
* is already in the menu.
|
|
415
|
+
*/
|
|
416
|
+
async #lookUpPageAddress(address: string) {
|
|
417
|
+
const requestId = ++this.#pageSearchRequestId;
|
|
418
|
+
const params = new URLSearchParams({ url: address });
|
|
419
|
+
|
|
420
|
+
this._pageSearchLoading = true;
|
|
421
|
+
this._pageSearchError = false;
|
|
422
|
+
try {
|
|
423
|
+
const res = await fetch(`/api/nav-items/resolve?${params.toString()}`, {
|
|
424
|
+
headers: { Accept: "application/json" },
|
|
425
|
+
});
|
|
426
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
427
|
+
const json = (await res.json()) as {
|
|
428
|
+
resolution?: NavAddressResolution;
|
|
429
|
+
};
|
|
430
|
+
if (requestId !== this.#pageSearchRequestId) return;
|
|
431
|
+
|
|
432
|
+
this._pages = [];
|
|
433
|
+
this._pageAddress = json.resolution ?? null;
|
|
434
|
+
this._selectedPageIndex = 0;
|
|
435
|
+
} catch {
|
|
436
|
+
if (requestId === this.#pageSearchRequestId) {
|
|
437
|
+
this._pages = [];
|
|
438
|
+
this._pageAddress = null;
|
|
439
|
+
this._pageSearchError = true;
|
|
440
|
+
}
|
|
441
|
+
} finally {
|
|
442
|
+
if (requestId === this.#pageSearchRequestId) {
|
|
443
|
+
this._pageSearchLoading = false;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
390
448
|
async #loadPageCandidates() {
|
|
391
449
|
const requestId = ++this.#pageSearchRequestId;
|
|
392
450
|
const params = new URLSearchParams({ limit: "20" });
|
|
@@ -395,6 +453,7 @@ export class JantNavManager extends LitElement {
|
|
|
395
453
|
|
|
396
454
|
this._pageSearchLoading = true;
|
|
397
455
|
this._pageSearchError = false;
|
|
456
|
+
this._pageAddress = null;
|
|
398
457
|
try {
|
|
399
458
|
const res = await fetch(`/api/nav-items/pages?${params.toString()}`, {
|
|
400
459
|
headers: { Accept: "application/json" },
|
|
@@ -430,7 +489,10 @@ export class JantNavManager extends LitElement {
|
|
|
430
489
|
}
|
|
431
490
|
this.#pageSearchTimer = setTimeout(() => {
|
|
432
491
|
this.#pageSearchTimer = null;
|
|
433
|
-
|
|
492
|
+
const query = this._pageQuery.trim();
|
|
493
|
+
void (looksLikeAddress(query)
|
|
494
|
+
? this.#lookUpPageAddress(query)
|
|
495
|
+
: this.#loadPageCandidates());
|
|
434
496
|
}, 200);
|
|
435
497
|
}
|
|
436
498
|
|
|
@@ -753,8 +815,7 @@ export class JantNavManager extends LitElement {
|
|
|
753
815
|
body: JSON.stringify(submitEvent.detail.data),
|
|
754
816
|
});
|
|
755
817
|
const created = (await res.json().catch(() => null)) as
|
|
756
|
-
|
|
757
|
-
| null;
|
|
818
|
+
(CreatedCollection & { error?: string }) | null;
|
|
758
819
|
if (!res.ok || !created?.id || !created.title || !created.slug) {
|
|
759
820
|
throw new Error("Invalid collection response");
|
|
760
821
|
}
|
|
@@ -944,7 +1005,9 @@ export class JantNavManager extends LitElement {
|
|
|
944
1005
|
|
|
945
1006
|
async #handleUpdate(item: NavManagerItem) {
|
|
946
1007
|
const label = this._editLabel.trim();
|
|
947
|
-
|
|
1008
|
+
// Clearing the field hands the item back to its target's title, so only a
|
|
1009
|
+
// free-form link — which points at nothing that has one — still needs one.
|
|
1010
|
+
if (!label && item.type === "link") {
|
|
948
1011
|
showToast(this.labels.labelRequired, "error");
|
|
949
1012
|
return;
|
|
950
1013
|
}
|
|
@@ -1054,7 +1117,7 @@ export class JantNavManager extends LitElement {
|
|
|
1054
1117
|
// Add collection handler
|
|
1055
1118
|
// ===========================================================================
|
|
1056
1119
|
|
|
1057
|
-
async #handleAddCollection(collectionId: string) {
|
|
1120
|
+
async #handleAddCollection(collectionId: string, isSmart = false) {
|
|
1058
1121
|
if (!collectionId || this._addingCollectionId) return;
|
|
1059
1122
|
|
|
1060
1123
|
this._addingCollectionId = collectionId;
|
|
@@ -1062,11 +1125,22 @@ export class JantNavManager extends LitElement {
|
|
|
1062
1125
|
const res = await fetch("/api/nav-items", {
|
|
1063
1126
|
method: "POST",
|
|
1064
1127
|
headers: this.#jsonMutationHeaders(),
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1128
|
+
// Both kinds land in the same menu and behave identically once there:
|
|
1129
|
+
// the label follows the target's title, and the URL follows its
|
|
1130
|
+
// address. Only which column holds the key differs.
|
|
1131
|
+
body: JSON.stringify(
|
|
1132
|
+
isSmart
|
|
1133
|
+
? {
|
|
1134
|
+
type: "smart_collection",
|
|
1135
|
+
smartCollectionId: collectionId,
|
|
1136
|
+
placement: "header",
|
|
1137
|
+
}
|
|
1138
|
+
: {
|
|
1139
|
+
type: "collection",
|
|
1140
|
+
collectionId,
|
|
1141
|
+
placement: "header",
|
|
1142
|
+
},
|
|
1143
|
+
),
|
|
1070
1144
|
});
|
|
1071
1145
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
1072
1146
|
|
|
@@ -1092,29 +1166,22 @@ export class JantNavManager extends LitElement {
|
|
|
1092
1166
|
// Suggested link handlers
|
|
1093
1167
|
// ===========================================================================
|
|
1094
1168
|
|
|
1169
|
+
/**
|
|
1170
|
+
* The internal path two nav URLs have to share to be the same destination.
|
|
1171
|
+
*
|
|
1172
|
+
* The same comparison the server makes when it decides which suggested links
|
|
1173
|
+
* are already in the menu, so both sides agree that `/about`, `/blog/about`
|
|
1174
|
+
* on a prefixed deployment, and this site's own absolute URL are one page.
|
|
1175
|
+
*/
|
|
1095
1176
|
#getComparableInternalPath(url: string): string | null {
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
url.startsWith("tel:") ||
|
|
1102
|
-
url.startsWith("#")
|
|
1103
|
-
) {
|
|
1104
|
-
return null;
|
|
1105
|
-
}
|
|
1177
|
+
const path = toInternalPath(url, {
|
|
1178
|
+
siteOrigins: [window.location.origin],
|
|
1179
|
+
sitePathPrefix: sitePathPrefix(),
|
|
1180
|
+
});
|
|
1181
|
+
if (path === null) return null;
|
|
1106
1182
|
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
const normalized = pathname
|
|
1110
|
-
.trim()
|
|
1111
|
-
.toLowerCase()
|
|
1112
|
-
.replace(/^\/+|\/+$/g, "")
|
|
1113
|
-
.replace(/\/+/g, "/");
|
|
1114
|
-
return normalized ? `/${normalized}` : "/";
|
|
1115
|
-
} catch {
|
|
1116
|
-
return null;
|
|
1117
|
-
}
|
|
1183
|
+
const normalized = normalizePath(path);
|
|
1184
|
+
return normalized ? `/${normalized}` : "/";
|
|
1118
1185
|
}
|
|
1119
1186
|
|
|
1120
1187
|
#isSuggestedLinkAdded(link: NavManagerSuggestedLink): boolean {
|
|
@@ -1264,9 +1331,13 @@ export class JantNavManager extends LitElement {
|
|
|
1264
1331
|
return this._items.filter((i) => i.placement === "more");
|
|
1265
1332
|
}
|
|
1266
1333
|
|
|
1334
|
+
// Mirrors the server's projection in `lib/navigation.ts`: with feeds off,
|
|
1335
|
+
// neither feed entry renders, so neither may show in the preview either.
|
|
1267
1336
|
#isVisibleInPreview(item: NavManagerItem): boolean {
|
|
1268
1337
|
return (
|
|
1269
|
-
this.rssFeedsEnabled ||
|
|
1338
|
+
this.rssFeedsEnabled ||
|
|
1339
|
+
item.type !== "system" ||
|
|
1340
|
+
!isFeedNavKey(item.systemKey)
|
|
1270
1341
|
);
|
|
1271
1342
|
}
|
|
1272
1343
|
|
|
@@ -1321,60 +1392,64 @@ export class JantNavManager extends LitElement {
|
|
|
1321
1392
|
(item) => item.id,
|
|
1322
1393
|
(item, index) =>
|
|
1323
1394
|
html`<a
|
|
1324
|
-
class=${
|
|
1325
|
-
|
|
1326
|
-
|
|
1395
|
+
class=${
|
|
1396
|
+
index === 0
|
|
1397
|
+
? "site-header-link site-header-link-active"
|
|
1398
|
+
: "site-header-link"
|
|
1399
|
+
}
|
|
1327
1400
|
>
|
|
1328
1401
|
${item.displayLabel ?? item.label}
|
|
1329
1402
|
</a>`,
|
|
1330
1403
|
)}
|
|
1331
|
-
${
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
<
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
${this.labels.moreSection}
|
|
1345
|
-
<svg
|
|
1346
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
1347
|
-
width="12"
|
|
1348
|
-
height="12"
|
|
1349
|
-
viewBox="0 0 24 24"
|
|
1350
|
-
fill="none"
|
|
1351
|
-
stroke="currentColor"
|
|
1352
|
-
stroke-width="2.5"
|
|
1353
|
-
stroke-linecap="round"
|
|
1354
|
-
stroke-linejoin="round"
|
|
1355
|
-
aria-hidden="true"
|
|
1404
|
+
${
|
|
1405
|
+
moreItems.length > 0
|
|
1406
|
+
? html`
|
|
1407
|
+
<div class="site-header-more" data-preview-more>
|
|
1408
|
+
<button
|
|
1409
|
+
type="button"
|
|
1410
|
+
class="site-header-more-btn"
|
|
1411
|
+
data-preview-more-trigger
|
|
1412
|
+
aria-haspopup="menu"
|
|
1413
|
+
aria-expanded=${
|
|
1414
|
+
this._showPreviewMore ? "true" : "false"
|
|
1415
|
+
}
|
|
1416
|
+
@click=${this.#togglePreviewMore}
|
|
1356
1417
|
>
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1418
|
+
${this.labels.previewMore}
|
|
1419
|
+
<svg
|
|
1420
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1421
|
+
width="12"
|
|
1422
|
+
height="12"
|
|
1423
|
+
viewBox="0 0 24 24"
|
|
1424
|
+
fill="none"
|
|
1425
|
+
stroke="currentColor"
|
|
1426
|
+
stroke-width="2.5"
|
|
1427
|
+
stroke-linecap="round"
|
|
1428
|
+
stroke-linejoin="round"
|
|
1429
|
+
aria-hidden="true"
|
|
1430
|
+
>
|
|
1431
|
+
<path d="m6 9 6 6 6-6" />
|
|
1432
|
+
</svg>
|
|
1433
|
+
</button>
|
|
1434
|
+
<div
|
|
1435
|
+
class="site-header-more-popover"
|
|
1436
|
+
aria-hidden=${this._showPreviewMore ? "false" : "true"}
|
|
1437
|
+
@click=${(event: Event) => event.stopPropagation()}
|
|
1438
|
+
>
|
|
1439
|
+
${repeat(
|
|
1440
|
+
moreItems,
|
|
1441
|
+
(item) => item.id,
|
|
1442
|
+
(item) => html`
|
|
1443
|
+
<span class="site-header-more-link">
|
|
1444
|
+
${item.displayLabel ?? item.label}
|
|
1445
|
+
</span>
|
|
1446
|
+
`,
|
|
1447
|
+
)}
|
|
1448
|
+
</div>
|
|
1374
1449
|
</div>
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1450
|
+
`
|
|
1451
|
+
: nothing
|
|
1452
|
+
}
|
|
1378
1453
|
</nav>
|
|
1379
1454
|
</div>
|
|
1380
1455
|
</div>
|
|
@@ -1448,7 +1523,8 @@ export class JantNavManager extends LitElement {
|
|
|
1448
1523
|
<input
|
|
1449
1524
|
type="text"
|
|
1450
1525
|
class="input"
|
|
1451
|
-
|
|
1526
|
+
maxlength="100"
|
|
1527
|
+
placeholder=${item.displayLabel ?? ""}
|
|
1452
1528
|
.value=${this._editLabel}
|
|
1453
1529
|
@input=${(e: Event) => {
|
|
1454
1530
|
this._editLabel = (e.target as HTMLInputElement).value;
|
|
@@ -1484,8 +1560,8 @@ export class JantNavManager extends LitElement {
|
|
|
1484
1560
|
<input
|
|
1485
1561
|
type="text"
|
|
1486
1562
|
class="input"
|
|
1487
|
-
required
|
|
1488
1563
|
maxlength="100"
|
|
1564
|
+
placeholder=${item.displayLabel ?? ""}
|
|
1489
1565
|
.value=${this._editLabel}
|
|
1490
1566
|
@input=${(e: Event) => {
|
|
1491
1567
|
this._editLabel = (e.target as HTMLInputElement).value;
|
|
@@ -1630,9 +1706,9 @@ export class JantNavManager extends LitElement {
|
|
|
1630
1706
|
stroke-width="2"
|
|
1631
1707
|
stroke-linecap="round"
|
|
1632
1708
|
stroke-linejoin="round"
|
|
1633
|
-
style="transition: transform 0.15s; ${
|
|
1634
|
-
? "transform: rotate(180deg);"
|
|
1635
|
-
|
|
1709
|
+
style="transition: transform 0.15s; ${
|
|
1710
|
+
isEditing ? "transform: rotate(180deg);" : ""
|
|
1711
|
+
}"
|
|
1636
1712
|
>
|
|
1637
1713
|
<path d="m6 9 6 6 6-6" />
|
|
1638
1714
|
</svg>
|
|
@@ -1703,6 +1779,180 @@ export class JantNavManager extends LitElement {
|
|
|
1703
1779
|
`;
|
|
1704
1780
|
}
|
|
1705
1781
|
|
|
1782
|
+
/** Whether navigation already points at what an address resolved to. */
|
|
1783
|
+
#isAddressAlreadyAdded(resolution: NavAddressResolution): boolean {
|
|
1784
|
+
if (resolution.kind === "page") {
|
|
1785
|
+
return this._items.some((item) => item.postId === resolution.page.id);
|
|
1786
|
+
}
|
|
1787
|
+
if (resolution.kind === "collection") {
|
|
1788
|
+
return this._items.some(
|
|
1789
|
+
(item) => item.collectionId === resolution.collection.id,
|
|
1790
|
+
);
|
|
1791
|
+
}
|
|
1792
|
+
if (resolution.kind === "smart_collection") {
|
|
1793
|
+
return this._items.some(
|
|
1794
|
+
(item) => item.smartCollectionId === resolution.smartCollection.id,
|
|
1795
|
+
);
|
|
1796
|
+
}
|
|
1797
|
+
return false;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
/** The line to show when a pasted address cannot become a page item. */
|
|
1801
|
+
#addressStatus(resolution: NavAddressResolution): string {
|
|
1802
|
+
if (this.#isAddressAlreadyAdded(resolution)) {
|
|
1803
|
+
return this.labels.addressAlreadyAdded;
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
switch (resolution.kind) {
|
|
1807
|
+
case "not_found":
|
|
1808
|
+
return this.labels.addressNotFound.replace(
|
|
1809
|
+
"{address}",
|
|
1810
|
+
resolution.address,
|
|
1811
|
+
);
|
|
1812
|
+
case "unpublished":
|
|
1813
|
+
return this.labels.addressUnpublished;
|
|
1814
|
+
case "private":
|
|
1815
|
+
return this.labels.addressPrivate;
|
|
1816
|
+
case "untitled":
|
|
1817
|
+
return this.labels.addressUntitled;
|
|
1818
|
+
case "external":
|
|
1819
|
+
return this.labels.addressExternal;
|
|
1820
|
+
case "link_only":
|
|
1821
|
+
return this.labels.addressLinkOnly;
|
|
1822
|
+
default:
|
|
1823
|
+
return "";
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
/**
|
|
1828
|
+
* Hand an address the picker cannot use over to the link form, filled in.
|
|
1829
|
+
*
|
|
1830
|
+
* Off-site addresses are a normal thing to want in navigation; they just
|
|
1831
|
+
* belong in the other kind of item, and retyping the URL is a poor way to
|
|
1832
|
+
* find that out.
|
|
1833
|
+
*/
|
|
1834
|
+
#startLinkFromAddress(address: string) {
|
|
1835
|
+
this._newLinkUrl = address;
|
|
1836
|
+
this.#closePageDialog();
|
|
1837
|
+
this._showLinkForm = true;
|
|
1838
|
+
setTimeout(() => {
|
|
1839
|
+
document.addEventListener("click", this.#closeLinkForm);
|
|
1840
|
+
});
|
|
1841
|
+
void this.updateComplete.then(() => {
|
|
1842
|
+
this.querySelector<HTMLInputElement>("#nav-link-label")?.focus();
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
#renderPageAddress(resolution: NavAddressResolution) {
|
|
1847
|
+
const alreadyAdded = this.#isAddressAlreadyAdded(resolution);
|
|
1848
|
+
|
|
1849
|
+
if (resolution.kind === "page" && !alreadyAdded) {
|
|
1850
|
+
const page = resolution.page;
|
|
1851
|
+
return this.#renderPageAddressResult(
|
|
1852
|
+
page.title,
|
|
1853
|
+
publicPath(`/${page.slug}`),
|
|
1854
|
+
this._addingPageId === page.id,
|
|
1855
|
+
() => void this.#addPageToNavigation(page),
|
|
1856
|
+
);
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
if (resolution.kind === "collection" && !alreadyAdded) {
|
|
1860
|
+
const collection = resolution.collection;
|
|
1861
|
+
return this.#renderPageAddressResult(
|
|
1862
|
+
collection.title,
|
|
1863
|
+
publicPath(getCollectionPagePath(collection.slug)),
|
|
1864
|
+
this._addingCollectionId === collection.id,
|
|
1865
|
+
() => {
|
|
1866
|
+
void this.#handleAddCollection(collection.id).then(() => {
|
|
1867
|
+
this.#closePageDialog();
|
|
1868
|
+
});
|
|
1869
|
+
},
|
|
1870
|
+
);
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
if (resolution.kind === "smart_collection" && !alreadyAdded) {
|
|
1874
|
+
const smartCollection = resolution.smartCollection;
|
|
1875
|
+
return this.#renderPageAddressResult(
|
|
1876
|
+
smartCollection.title,
|
|
1877
|
+
publicPath(getCollectionPagePath(smartCollection.slug)),
|
|
1878
|
+
this._addingCollectionId === smartCollection.id,
|
|
1879
|
+
() => {
|
|
1880
|
+
void this.#handleAddCollection(smartCollection.id, true).then(() => {
|
|
1881
|
+
this.#closePageDialog();
|
|
1882
|
+
});
|
|
1883
|
+
},
|
|
1884
|
+
);
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
const status = this.#addressStatus(resolution);
|
|
1888
|
+
const offerLink =
|
|
1889
|
+
resolution.kind === "external" || resolution.kind === "link_only";
|
|
1890
|
+
|
|
1891
|
+
return html`
|
|
1892
|
+
<p class="nav-page-status" role="status">${status}</p>
|
|
1893
|
+
${
|
|
1894
|
+
offerLink
|
|
1895
|
+
? html`
|
|
1896
|
+
<button
|
|
1897
|
+
type="button"
|
|
1898
|
+
class="btn-outline nav-page-address-link"
|
|
1899
|
+
@click=${() => this.#startLinkFromAddress(resolution.address)}
|
|
1900
|
+
>
|
|
1901
|
+
${this.labels.addressAddAsLink}
|
|
1902
|
+
</button>
|
|
1903
|
+
`
|
|
1904
|
+
: nothing
|
|
1905
|
+
}
|
|
1906
|
+
`;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
#renderPageAddressResult(
|
|
1910
|
+
title: string,
|
|
1911
|
+
path: string,
|
|
1912
|
+
busy: boolean,
|
|
1913
|
+
add: () => void,
|
|
1914
|
+
) {
|
|
1915
|
+
return html`
|
|
1916
|
+
<p class="nav-page-results-label">${this.labels.addressMatch}</p>
|
|
1917
|
+
<div class="nav-page-results" role="listbox">
|
|
1918
|
+
<button
|
|
1919
|
+
type="button"
|
|
1920
|
+
role="option"
|
|
1921
|
+
aria-selected="true"
|
|
1922
|
+
class="nav-page-result nav-page-result-selected"
|
|
1923
|
+
?disabled=${busy}
|
|
1924
|
+
@click=${add}
|
|
1925
|
+
>
|
|
1926
|
+
<span class="nav-page-result-copy">
|
|
1927
|
+
<span class="nav-page-result-title">${title}</span>
|
|
1928
|
+
<span class="nav-page-result-path">${path}</span>
|
|
1929
|
+
</span>
|
|
1930
|
+
${
|
|
1931
|
+
busy
|
|
1932
|
+
? html`<span class="nav-page-spinner" aria-hidden="true"></span>`
|
|
1933
|
+
: html`
|
|
1934
|
+
<svg
|
|
1935
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1936
|
+
width="16"
|
|
1937
|
+
height="16"
|
|
1938
|
+
viewBox="0 0 24 24"
|
|
1939
|
+
fill="none"
|
|
1940
|
+
stroke="currentColor"
|
|
1941
|
+
stroke-width="2"
|
|
1942
|
+
stroke-linecap="round"
|
|
1943
|
+
stroke-linejoin="round"
|
|
1944
|
+
aria-hidden="true"
|
|
1945
|
+
>
|
|
1946
|
+
<path d="M12 5v14" />
|
|
1947
|
+
<path d="M5 12h14" />
|
|
1948
|
+
</svg>
|
|
1949
|
+
`
|
|
1950
|
+
}
|
|
1951
|
+
</button>
|
|
1952
|
+
</div>
|
|
1953
|
+
`;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1706
1956
|
#renderPagePicker() {
|
|
1707
1957
|
const status = this._pageSearchLoading
|
|
1708
1958
|
? this.labels.searchingPages
|
|
@@ -1740,14 +1990,16 @@ export class JantNavManager extends LitElement {
|
|
|
1740
1990
|
id="nav-page-search"
|
|
1741
1991
|
type="search"
|
|
1742
1992
|
class="input nav-page-search-input"
|
|
1743
|
-
placeholder=${this.labels.
|
|
1993
|
+
placeholder=${this.labels.searchPagesHint}
|
|
1744
1994
|
autocomplete="off"
|
|
1745
1995
|
role="combobox"
|
|
1746
1996
|
aria-expanded="true"
|
|
1747
1997
|
aria-controls="nav-page-results"
|
|
1748
|
-
aria-activedescendant=${
|
|
1749
|
-
|
|
1750
|
-
|
|
1998
|
+
aria-activedescendant=${
|
|
1999
|
+
this._pages.length > 0
|
|
2000
|
+
? `nav-page-result-${this._selectedPageIndex}`
|
|
2001
|
+
: ""
|
|
2002
|
+
}
|
|
1751
2003
|
.value=${this._pageQuery}
|
|
1752
2004
|
@input=${(event: Event) => {
|
|
1753
2005
|
this._pageQuery = (event.target as HTMLInputElement).value;
|
|
@@ -1757,70 +2009,82 @@ export class JantNavManager extends LitElement {
|
|
|
1757
2009
|
/>
|
|
1758
2010
|
</div>
|
|
1759
2011
|
|
|
1760
|
-
${
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
type="button"
|
|
1779
|
-
role="option"
|
|
1780
|
-
aria-selected=${selected ? "true" : "false"}
|
|
1781
|
-
class=${`nav-page-result${
|
|
1782
|
-
selected ? " nav-page-result-selected" : ""
|
|
1783
|
-
}`}
|
|
1784
|
-
?disabled=${this._addingPageId !== null}
|
|
1785
|
-
@mouseenter=${() => {
|
|
1786
|
-
this._selectedPageIndex = index;
|
|
1787
|
-
}}
|
|
1788
|
-
@click=${() => void this.#addPageToNavigation(page)}
|
|
2012
|
+
${
|
|
2013
|
+
this._pageSearchLoading || this._pageSearchError
|
|
2014
|
+
? html`<p class="nav-page-status" role="status">${status}</p>`
|
|
2015
|
+
: this._pageAddress
|
|
2016
|
+
? this.#renderPageAddress(this._pageAddress)
|
|
2017
|
+
: this._pages.length > 0
|
|
2018
|
+
? html`
|
|
2019
|
+
<p class="nav-page-results-label">
|
|
2020
|
+
${
|
|
2021
|
+
this._pageQuery.trim()
|
|
2022
|
+
? this.labels.searchPages
|
|
2023
|
+
: this.labels.recentPages
|
|
2024
|
+
}
|
|
2025
|
+
</p>
|
|
2026
|
+
<div
|
|
2027
|
+
id="nav-page-results"
|
|
2028
|
+
class="nav-page-results"
|
|
2029
|
+
role="listbox"
|
|
1789
2030
|
>
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
2031
|
+
${this._pages.map((page, index) => {
|
|
2032
|
+
const selected = index === this._selectedPageIndex;
|
|
2033
|
+
const adding = this._addingPageId === page.id;
|
|
2034
|
+
return html`
|
|
2035
|
+
<button
|
|
2036
|
+
id=${`nav-page-result-${index}`}
|
|
2037
|
+
type="button"
|
|
2038
|
+
role="option"
|
|
2039
|
+
aria-selected=${selected ? "true" : "false"}
|
|
2040
|
+
class=${`nav-page-result${
|
|
2041
|
+
selected ? " nav-page-result-selected" : ""
|
|
2042
|
+
}`}
|
|
2043
|
+
?disabled=${this._addingPageId !== null}
|
|
2044
|
+
@mouseenter=${() => {
|
|
2045
|
+
this._selectedPageIndex = index;
|
|
2046
|
+
}}
|
|
2047
|
+
@click=${() => void this.#addPageToNavigation(page)}
|
|
2048
|
+
>
|
|
2049
|
+
<span class="nav-page-result-copy">
|
|
2050
|
+
<span class="nav-page-result-title"
|
|
2051
|
+
>${page.title}</span
|
|
2052
|
+
>
|
|
2053
|
+
<span class="nav-page-result-path"
|
|
2054
|
+
>${publicPath(`/${page.slug}`)}</span
|
|
2055
|
+
>
|
|
2056
|
+
</span>
|
|
2057
|
+
${
|
|
2058
|
+
adding
|
|
2059
|
+
? html`<span
|
|
2060
|
+
class="nav-page-spinner"
|
|
2061
|
+
aria-hidden="true"
|
|
2062
|
+
></span>`
|
|
2063
|
+
: html`
|
|
2064
|
+
<svg
|
|
2065
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
2066
|
+
width="16"
|
|
2067
|
+
height="16"
|
|
2068
|
+
viewBox="0 0 24 24"
|
|
2069
|
+
fill="none"
|
|
2070
|
+
stroke="currentColor"
|
|
2071
|
+
stroke-width="2"
|
|
2072
|
+
stroke-linecap="round"
|
|
2073
|
+
stroke-linejoin="round"
|
|
2074
|
+
aria-hidden="true"
|
|
2075
|
+
>
|
|
2076
|
+
<path d="M12 5v14" />
|
|
2077
|
+
<path d="M5 12h14" />
|
|
2078
|
+
</svg>
|
|
2079
|
+
`
|
|
2080
|
+
}
|
|
2081
|
+
</button>
|
|
2082
|
+
`;
|
|
2083
|
+
})}
|
|
2084
|
+
</div>
|
|
2085
|
+
`
|
|
2086
|
+
: html`<p class="nav-page-status" role="status">${status}</p>`
|
|
2087
|
+
}
|
|
1824
2088
|
</section>
|
|
1825
2089
|
<footer class="nav-page-dialog-footer">
|
|
1826
2090
|
<button
|
|
@@ -1921,15 +2185,19 @@ export class JantNavManager extends LitElement {
|
|
|
1921
2185
|
</div>
|
|
1922
2186
|
<p
|
|
1923
2187
|
id="nav-new-page-slug-help"
|
|
1924
|
-
class=${
|
|
1925
|
-
|
|
1926
|
-
|
|
2188
|
+
class=${
|
|
2189
|
+
slugError
|
|
2190
|
+
? "text-xs text-destructive"
|
|
2191
|
+
: "text-xs text-muted-foreground"
|
|
2192
|
+
}
|
|
1927
2193
|
>
|
|
1928
|
-
${
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
2194
|
+
${
|
|
2195
|
+
slugError
|
|
2196
|
+
? slugError
|
|
2197
|
+
: this._slugCheckLoading
|
|
2198
|
+
? this.labels.checkingAddress
|
|
2199
|
+
: pagePath
|
|
2200
|
+
}
|
|
1933
2201
|
</p>
|
|
1934
2202
|
</div>
|
|
1935
2203
|
<p class="nav-page-visibility-note">
|
|
@@ -1951,11 +2219,13 @@ export class JantNavManager extends LitElement {
|
|
|
1951
2219
|
</svg>
|
|
1952
2220
|
${this.labels.pageVisibilityHint}
|
|
1953
2221
|
</p>
|
|
1954
|
-
${
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
2222
|
+
${
|
|
2223
|
+
this._createPageError
|
|
2224
|
+
? html`<p class="text-sm text-destructive" role="alert">
|
|
2225
|
+
${this._createPageError}
|
|
2226
|
+
</p>`
|
|
2227
|
+
: nothing
|
|
2228
|
+
}
|
|
1959
2229
|
</form>
|
|
1960
2230
|
</section>
|
|
1961
2231
|
<footer>
|
|
@@ -1971,13 +2241,15 @@ export class JantNavManager extends LitElement {
|
|
|
1971
2241
|
type="submit"
|
|
1972
2242
|
class="btn"
|
|
1973
2243
|
form="nav-create-page-form"
|
|
1974
|
-
?disabled=${
|
|
1975
|
-
|
|
1976
|
-
|
|
2244
|
+
?disabled=${
|
|
2245
|
+
this._creatingPage || this._slugCheckLoading || Boolean(slugError)
|
|
2246
|
+
}
|
|
1977
2247
|
>
|
|
1978
|
-
${
|
|
1979
|
-
|
|
1980
|
-
|
|
2248
|
+
${
|
|
2249
|
+
this._creatingPage
|
|
2250
|
+
? this.labels.creatingPage
|
|
2251
|
+
: this.labels.createPage
|
|
2252
|
+
}
|
|
1981
2253
|
</button>
|
|
1982
2254
|
</footer>
|
|
1983
2255
|
`;
|
|
@@ -2080,11 +2352,13 @@ export class JantNavManager extends LitElement {
|
|
|
2080
2352
|
<path d="m6 6 12 12" />
|
|
2081
2353
|
</svg>
|
|
2082
2354
|
</button>
|
|
2083
|
-
${
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2355
|
+
${
|
|
2356
|
+
this._pageDialogView === "picker"
|
|
2357
|
+
? this.#renderPagePicker()
|
|
2358
|
+
: this._pageDialogView === "create"
|
|
2359
|
+
? this.#renderCreatePage()
|
|
2360
|
+
: this.#renderCreatedPage()
|
|
2361
|
+
}
|
|
2088
2362
|
</div>
|
|
2089
2363
|
</dialog>
|
|
2090
2364
|
`;
|
|
@@ -2117,78 +2391,82 @@ export class JantNavManager extends LitElement {
|
|
|
2117
2391
|
>
|
|
2118
2392
|
${this.labels.addLink}
|
|
2119
2393
|
</button>
|
|
2120
|
-
${
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
<
|
|
2133
|
-
<
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
e
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
<div class="grid grid-cols-3 items-center gap-4">
|
|
2148
|
-
<label for="nav-link-label">${this.labels.label}</label>
|
|
2149
|
-
<input
|
|
2150
|
-
type="text"
|
|
2151
|
-
id="nav-link-label"
|
|
2152
|
-
class="col-span-2 h-8"
|
|
2153
|
-
placeholder="Home"
|
|
2154
|
-
required
|
|
2155
|
-
.value=${this._newLinkLabel}
|
|
2156
|
-
@input=${(e: Event) => {
|
|
2157
|
-
this._newLinkLabel = (
|
|
2158
|
-
e.target as HTMLInputElement
|
|
2159
|
-
).value;
|
|
2160
|
-
}}
|
|
2161
|
-
autofocus
|
|
2162
|
-
/>
|
|
2163
|
-
</div>
|
|
2164
|
-
<div class="grid grid-cols-3 items-center gap-4">
|
|
2165
|
-
<label for="nav-link-url">${this.labels.url}</label>
|
|
2166
|
-
<input
|
|
2167
|
-
type="text"
|
|
2168
|
-
id="nav-link-url"
|
|
2169
|
-
class="col-span-2 h-8"
|
|
2170
|
-
placeholder=${this.labels.urlPlaceholder}
|
|
2171
|
-
required
|
|
2172
|
-
.value=${this._newLinkUrl}
|
|
2173
|
-
@input=${(e: Event) => {
|
|
2174
|
-
this._newLinkUrl = (
|
|
2175
|
-
e.target as HTMLInputElement
|
|
2176
|
-
).value;
|
|
2177
|
-
}}
|
|
2178
|
-
/>
|
|
2179
|
-
</div>
|
|
2180
|
-
<button
|
|
2181
|
-
type="submit"
|
|
2182
|
-
class="btn-sm mt-2"
|
|
2183
|
-
?disabled=${this._addingLink}
|
|
2394
|
+
${
|
|
2395
|
+
this._showLinkForm
|
|
2396
|
+
? html`
|
|
2397
|
+
<div
|
|
2398
|
+
id="nav-link-popover-content"
|
|
2399
|
+
data-popover
|
|
2400
|
+
data-side="top"
|
|
2401
|
+
aria-hidden="false"
|
|
2402
|
+
class="w-80"
|
|
2403
|
+
style="bottom: 100%; margin-bottom: 0.5rem;"
|
|
2404
|
+
@click=${(e: Event) => e.stopPropagation()}
|
|
2405
|
+
>
|
|
2406
|
+
<div class="grid gap-4">
|
|
2407
|
+
<header class="grid gap-1.5">
|
|
2408
|
+
<h4 class="leading-none font-medium">
|
|
2409
|
+
${this.labels.addLink}
|
|
2410
|
+
</h4>
|
|
2411
|
+
<p class="text-muted-foreground text-sm">
|
|
2412
|
+
${this.labels.addLinkDescription}
|
|
2413
|
+
</p>
|
|
2414
|
+
</header>
|
|
2415
|
+
<form
|
|
2416
|
+
class="form grid gap-2"
|
|
2417
|
+
@submit=${(e: Event) => {
|
|
2418
|
+
e.preventDefault();
|
|
2419
|
+
this.#handleAddLink();
|
|
2420
|
+
}}
|
|
2184
2421
|
>
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2422
|
+
<div class="grid grid-cols-3 items-center gap-4">
|
|
2423
|
+
<label for="nav-link-label"
|
|
2424
|
+
>${this.labels.label}</label
|
|
2425
|
+
>
|
|
2426
|
+
<input
|
|
2427
|
+
type="text"
|
|
2428
|
+
id="nav-link-label"
|
|
2429
|
+
class="col-span-2 h-8"
|
|
2430
|
+
placeholder="Home"
|
|
2431
|
+
required
|
|
2432
|
+
.value=${this._newLinkLabel}
|
|
2433
|
+
@input=${(e: Event) => {
|
|
2434
|
+
this._newLinkLabel = (
|
|
2435
|
+
e.target as HTMLInputElement
|
|
2436
|
+
).value;
|
|
2437
|
+
}}
|
|
2438
|
+
autofocus
|
|
2439
|
+
/>
|
|
2440
|
+
</div>
|
|
2441
|
+
<div class="grid grid-cols-3 items-center gap-4">
|
|
2442
|
+
<label for="nav-link-url">${this.labels.url}</label>
|
|
2443
|
+
<input
|
|
2444
|
+
type="text"
|
|
2445
|
+
id="nav-link-url"
|
|
2446
|
+
class="col-span-2 h-8"
|
|
2447
|
+
placeholder=${this.labels.urlPlaceholder}
|
|
2448
|
+
required
|
|
2449
|
+
.value=${this._newLinkUrl}
|
|
2450
|
+
@input=${(e: Event) => {
|
|
2451
|
+
this._newLinkUrl = (
|
|
2452
|
+
e.target as HTMLInputElement
|
|
2453
|
+
).value;
|
|
2454
|
+
}}
|
|
2455
|
+
/>
|
|
2456
|
+
</div>
|
|
2457
|
+
<button
|
|
2458
|
+
type="submit"
|
|
2459
|
+
class="btn-sm mt-2"
|
|
2460
|
+
?disabled=${this._addingLink}
|
|
2461
|
+
>
|
|
2462
|
+
${this.labels.addLink}
|
|
2463
|
+
</button>
|
|
2464
|
+
</form>
|
|
2465
|
+
</div>
|
|
2188
2466
|
</div>
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2467
|
+
`
|
|
2468
|
+
: nothing
|
|
2469
|
+
}
|
|
2192
2470
|
</div>
|
|
2193
2471
|
</section>
|
|
2194
2472
|
`;
|
|
@@ -2217,9 +2495,13 @@ export class JantNavManager extends LitElement {
|
|
|
2217
2495
|
}
|
|
2218
2496
|
|
|
2219
2497
|
const addedCollectionIds = new Set(
|
|
2220
|
-
this._items
|
|
2221
|
-
|
|
2222
|
-
|
|
2498
|
+
this._items.flatMap((i) =>
|
|
2499
|
+
i.type === "collection" && i.collectionId
|
|
2500
|
+
? [i.collectionId]
|
|
2501
|
+
: i.type === "smart_collection" && i.smartCollectionId
|
|
2502
|
+
? [i.smartCollectionId]
|
|
2503
|
+
: [],
|
|
2504
|
+
),
|
|
2223
2505
|
);
|
|
2224
2506
|
const available = this.collections.filter(
|
|
2225
2507
|
(c) => !addedCollectionIds.has(c.id),
|
|
@@ -2311,89 +2593,107 @@ export class JantNavManager extends LitElement {
|
|
|
2311
2593
|
stroke-linejoin="round"
|
|
2312
2594
|
class="ml-1.5 -mr-0.5"
|
|
2313
2595
|
aria-hidden="true"
|
|
2314
|
-
style="transition: transform 0.15s; ${
|
|
2315
|
-
? "transform: rotate(180deg);"
|
|
2316
|
-
|
|
2596
|
+
style="transition: transform 0.15s; ${
|
|
2597
|
+
this._showCollectionPicker ? "transform: rotate(180deg);" : ""
|
|
2598
|
+
}"
|
|
2317
2599
|
>
|
|
2318
2600
|
<path d="m6 9 6 6 6-6" />
|
|
2319
2601
|
</svg>
|
|
2320
2602
|
</button>
|
|
2321
|
-
${
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
(
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2603
|
+
${
|
|
2604
|
+
this._showCollectionPicker
|
|
2605
|
+
? html`
|
|
2606
|
+
<div
|
|
2607
|
+
class="collection-picker"
|
|
2608
|
+
role="menu"
|
|
2609
|
+
@click=${(e: Event) => e.stopPropagation()}
|
|
2610
|
+
>
|
|
2611
|
+
${groups.map(
|
|
2612
|
+
(group) => html`
|
|
2613
|
+
${
|
|
2614
|
+
group.label
|
|
2615
|
+
? html`<div class="collection-picker-group">
|
|
2616
|
+
${group.label}
|
|
2617
|
+
</div>`
|
|
2618
|
+
: nothing
|
|
2619
|
+
}
|
|
2620
|
+
${group.items.map((c) => {
|
|
2621
|
+
const adding = this._addingCollectionId === c.id;
|
|
2622
|
+
return html`
|
|
2623
|
+
<button
|
|
2624
|
+
type="button"
|
|
2625
|
+
role="menuitem"
|
|
2626
|
+
class="collection-picker-item"
|
|
2627
|
+
?disabled=${
|
|
2628
|
+
adding || this._addingCollectionId !== null
|
|
2629
|
+
}
|
|
2630
|
+
@click=${() =>
|
|
2631
|
+
this.#handleAddCollection(c.id, c.isSmart)}
|
|
2632
|
+
>
|
|
2633
|
+
<span class="collection-picker-title">
|
|
2634
|
+
${c.title}
|
|
2635
|
+
</span>
|
|
2636
|
+
${
|
|
2637
|
+
c.isSmart
|
|
2638
|
+
? html`<span
|
|
2639
|
+
class="collection-directory-smart-icon"
|
|
2640
|
+
role="img"
|
|
2641
|
+
aria-label=${
|
|
2642
|
+
this.labels.smartCollectionLabel ?? ""
|
|
2643
|
+
}
|
|
2644
|
+
title=${
|
|
2645
|
+
this.labels.smartCollectionLabel ?? ""
|
|
2646
|
+
}
|
|
2647
|
+
>${unsafeHTML(FUNNEL_ICON_SVG)}</span
|
|
2648
|
+
>`
|
|
2649
|
+
: nothing
|
|
2650
|
+
}
|
|
2651
|
+
${
|
|
2652
|
+
adding
|
|
2653
|
+
? html`<svg
|
|
2654
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
2655
|
+
width="14"
|
|
2656
|
+
height="14"
|
|
2657
|
+
viewBox="0 0 24 24"
|
|
2658
|
+
fill="none"
|
|
2659
|
+
stroke="currentColor"
|
|
2660
|
+
stroke-width="2"
|
|
2661
|
+
stroke-linecap="round"
|
|
2662
|
+
stroke-linejoin="round"
|
|
2663
|
+
class="animate-spin shrink-0 text-muted-foreground"
|
|
2664
|
+
>
|
|
2665
|
+
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
2666
|
+
</svg>`
|
|
2667
|
+
: nothing
|
|
2668
|
+
}
|
|
2669
|
+
</button>
|
|
2670
|
+
`;
|
|
2671
|
+
})}
|
|
2672
|
+
`,
|
|
2673
|
+
)}
|
|
2674
|
+
<div class="collection-picker-footer">
|
|
2675
|
+
<button
|
|
2676
|
+
type="button"
|
|
2677
|
+
role="menuitem"
|
|
2678
|
+
class="collection-picker-create"
|
|
2679
|
+
data-create-collection-trigger
|
|
2680
|
+
@click=${() => void this.#openCollectionDialog()}
|
|
2681
|
+
>
|
|
2682
|
+
${this.labels.createNewCollection}
|
|
2683
|
+
<span aria-hidden="true">+</span>
|
|
2684
|
+
</button>
|
|
2685
|
+
</div>
|
|
2381
2686
|
</div>
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2687
|
+
`
|
|
2688
|
+
: nothing
|
|
2689
|
+
}
|
|
2385
2690
|
</div>
|
|
2386
2691
|
</section>
|
|
2387
2692
|
`;
|
|
2388
2693
|
}
|
|
2389
2694
|
|
|
2390
2695
|
#renderCreateCollection() {
|
|
2391
|
-
const initial = {
|
|
2392
|
-
title: "",
|
|
2393
|
-
slug: "",
|
|
2394
|
-
description: "",
|
|
2395
|
-
sortOrder: "newest" as const,
|
|
2396
|
-
};
|
|
2696
|
+
const initial = { title: "", slug: "" };
|
|
2397
2697
|
|
|
2398
2698
|
return html`
|
|
2399
2699
|
<header>
|
|
@@ -2404,19 +2704,19 @@ export class JantNavManager extends LitElement {
|
|
|
2404
2704
|
</header>
|
|
2405
2705
|
<section>
|
|
2406
2706
|
<jant-collection-form
|
|
2407
|
-
variant="quick"
|
|
2408
2707
|
.labels=${this.labels.collectionFormLabels}
|
|
2409
2708
|
.initial=${initial}
|
|
2410
2709
|
action=${publicPath("/api/collections")}
|
|
2411
|
-
cancel-href=${publicPath(NAVIGATION_SETTINGS_PATH)}
|
|
2412
2710
|
@jant:collection-submit=${(event: Event) =>
|
|
2413
2711
|
void this.#handleCreateCollectionSubmit(event)}
|
|
2414
2712
|
></jant-collection-form>
|
|
2415
|
-
${
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2713
|
+
${
|
|
2714
|
+
this._createCollectionError
|
|
2715
|
+
? html`<p class="mt-3 text-sm text-destructive" role="alert">
|
|
2716
|
+
${this._createCollectionError}
|
|
2717
|
+
</p>`
|
|
2718
|
+
: nothing
|
|
2719
|
+
}
|
|
2420
2720
|
</section>
|
|
2421
2721
|
<footer>
|
|
2422
2722
|
<button
|
|
@@ -2433,23 +2733,40 @@ export class JantNavManager extends LitElement {
|
|
|
2433
2733
|
?disabled=${this._creatingCollection}
|
|
2434
2734
|
@click=${this.#submitCollectionForm}
|
|
2435
2735
|
>
|
|
2436
|
-
${
|
|
2437
|
-
|
|
2438
|
-
|
|
2736
|
+
${
|
|
2737
|
+
this._creatingCollection
|
|
2738
|
+
? this.labels.creatingCollection
|
|
2739
|
+
: this.labels.createCollection
|
|
2740
|
+
}
|
|
2439
2741
|
</button>
|
|
2440
2742
|
</footer>
|
|
2441
2743
|
`;
|
|
2442
2744
|
}
|
|
2443
2745
|
|
|
2746
|
+
/**
|
|
2747
|
+
* Edit the collection just created, without leaving the navigation step.
|
|
2748
|
+
*
|
|
2749
|
+
* Its title and address can both move, so the picker's copy of it and the
|
|
2750
|
+
* card showing it are re-read from what the dialog saved.
|
|
2751
|
+
*/
|
|
2752
|
+
async #editCreatedCollection(collectionId: string) {
|
|
2753
|
+
const { changed, collection } = await openCollectionDialog({
|
|
2754
|
+
collectionId,
|
|
2755
|
+
});
|
|
2756
|
+
if (!changed || !collection) return;
|
|
2757
|
+
|
|
2758
|
+
this._createdCollection = collection;
|
|
2759
|
+
this.collections = this.collections.map((entry) =>
|
|
2760
|
+
entry.id === collection.id
|
|
2761
|
+
? { ...entry, title: collection.title, slug: collection.slug }
|
|
2762
|
+
: entry,
|
|
2763
|
+
);
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2444
2766
|
#renderCreatedCollection() {
|
|
2445
2767
|
const collection = this._createdCollection;
|
|
2446
2768
|
if (!collection) return nothing;
|
|
2447
2769
|
|
|
2448
|
-
const returnTo = publicPath(NAVIGATION_SETTINGS_PATH);
|
|
2449
|
-
const editHref = `${publicPath(
|
|
2450
|
-
getCollectionEditPath(collection.slug),
|
|
2451
|
-
)}?returnTo=${encodeURIComponent(returnTo)}`;
|
|
2452
|
-
|
|
2453
2770
|
return html`
|
|
2454
2771
|
<header class="nav-create-success-header">
|
|
2455
2772
|
<span class="nav-create-success-icon" aria-hidden="true">
|
|
@@ -2481,15 +2798,16 @@ export class JantNavManager extends LitElement {
|
|
|
2481
2798
|
</div>
|
|
2482
2799
|
</section>
|
|
2483
2800
|
<footer>
|
|
2484
|
-
|
|
2801
|
+
<!-- The collection dialog opens on top of this one rather than in a
|
|
2802
|
+
new tab: there is no editor page to send the author to, and the
|
|
2803
|
+
navigation step they came for is still waiting underneath. -->
|
|
2804
|
+
<button
|
|
2805
|
+
type="button"
|
|
2485
2806
|
class="btn-outline"
|
|
2486
|
-
|
|
2487
|
-
target="_blank"
|
|
2488
|
-
rel="noopener noreferrer"
|
|
2807
|
+
@click=${() => void this.#editCreatedCollection(collection.id)}
|
|
2489
2808
|
>
|
|
2490
2809
|
${this.labels.editCollection}
|
|
2491
|
-
|
|
2492
|
-
</a>
|
|
2810
|
+
</button>
|
|
2493
2811
|
<button
|
|
2494
2812
|
type="button"
|
|
2495
2813
|
class="btn"
|
|
@@ -2546,9 +2864,11 @@ export class JantNavManager extends LitElement {
|
|
|
2546
2864
|
<path d="m6 6 12 12" />
|
|
2547
2865
|
</svg>
|
|
2548
2866
|
</button>
|
|
2549
|
-
${
|
|
2550
|
-
|
|
2551
|
-
|
|
2867
|
+
${
|
|
2868
|
+
this._collectionDialogView === "create"
|
|
2869
|
+
? this.#renderCreateCollection()
|
|
2870
|
+
: this.#renderCreatedCollection()
|
|
2871
|
+
}
|
|
2552
2872
|
</div>
|
|
2553
2873
|
</dialog>
|
|
2554
2874
|
`;
|
|
@@ -2603,11 +2923,13 @@ export class JantNavManager extends LitElement {
|
|
|
2603
2923
|
|
|
2604
2924
|
<section class="mt-8">
|
|
2605
2925
|
<h2 class="text-lg font-semibold mb-3">${this.labels.headerSection}</h2>
|
|
2606
|
-
${
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2926
|
+
${
|
|
2927
|
+
this.#headerItems.length === 0
|
|
2928
|
+
? html`<p class="text-sm text-muted-foreground py-4">
|
|
2929
|
+
${this.labels.emptyState}
|
|
2930
|
+
</p>`
|
|
2931
|
+
: nothing
|
|
2932
|
+
}
|
|
2611
2933
|
<div id="nav-items-header" class="nav-items-list">
|
|
2612
2934
|
${repeat(
|
|
2613
2935
|
this.#headerItems,
|
|
@@ -2620,15 +2942,17 @@ export class JantNavManager extends LitElement {
|
|
|
2620
2942
|
<section class="mt-8">
|
|
2621
2943
|
<h2 class="text-lg font-semibold mb-3">${this.labels.moreSection}</h2>
|
|
2622
2944
|
<div id="nav-items-more" class="nav-items-list nav-items-list-drop">
|
|
2623
|
-
${
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2945
|
+
${
|
|
2946
|
+
this.#moreItems.length > 0
|
|
2947
|
+
? repeat(
|
|
2948
|
+
this.#moreItems,
|
|
2949
|
+
(item) => item.id,
|
|
2950
|
+
(item) => this.#renderItem(item),
|
|
2951
|
+
)
|
|
2952
|
+
: html`<p class="nav-items-empty-hint">
|
|
2953
|
+
${this.labels.moreEmptyHint}
|
|
2954
|
+
</p>`
|
|
2955
|
+
}
|
|
2632
2956
|
</div>
|
|
2633
2957
|
</section>
|
|
2634
2958
|
|