@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
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The repository name Jant proposes for a site's GitHub Sync mirror.
|
|
3
|
+
*
|
|
4
|
+
* The settings page prefills it on github.com/new, and a site export with no
|
|
5
|
+
* repository behind it uses it as the Worker name in `wrangler.jsonc`.
|
|
6
|
+
* Cloudflare names a Worker imported from a repository after the repository,
|
|
7
|
+
* so an export and a repository created with the default name agree without
|
|
8
|
+
* anyone editing either.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Used when the site URL yields no host label. */
|
|
12
|
+
const FALLBACK_REPO_NAME = "jant-site-sync";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Derive the default repository name for a site's sync mirror.
|
|
16
|
+
*
|
|
17
|
+
* Uses the first DNS label of the site's host — a stable, URL-safe
|
|
18
|
+
* identifier tied to this Jant instance. A leading `www` is skipped: it
|
|
19
|
+
* names nothing about the site, and every `www.` site would otherwise
|
|
20
|
+
* propose the same `www-jant-sync`. It is kept when only a top-level domain
|
|
21
|
+
* follows it, since that label would say even less. The `-jant-sync` suffix
|
|
22
|
+
* tells the sync mirror apart from a user's own `{slug}-jant` source repo.
|
|
23
|
+
*
|
|
24
|
+
* @param siteUrl - The site's public URL.
|
|
25
|
+
* @returns A GitHub-safe repository name; `jant-site-sync` when the URL has
|
|
26
|
+
* no usable host.
|
|
27
|
+
* @example
|
|
28
|
+
* suggestSyncRepoName("https://notes.example.com"); // "notes-jant-sync"
|
|
29
|
+
* suggestSyncRepoName("https://www.example.com"); // "example-jant-sync"
|
|
30
|
+
*/
|
|
31
|
+
export function suggestSyncRepoName(siteUrl: string): string {
|
|
32
|
+
let labels: string[] = [];
|
|
33
|
+
try {
|
|
34
|
+
labels = new URL(siteUrl).host.split(".");
|
|
35
|
+
} catch {
|
|
36
|
+
/* fall through */
|
|
37
|
+
}
|
|
38
|
+
const [first = "", second = "", ...more] = labels;
|
|
39
|
+
const label = first === "www" && more.length > 0 ? second : first;
|
|
40
|
+
const slug = label
|
|
41
|
+
.toLowerCase()
|
|
42
|
+
.replace(/[^a-z0-9-_]+/g, "-")
|
|
43
|
+
.replace(/^-+|-+$/g, "");
|
|
44
|
+
return slug ? `${slug}-jant-sync` : FALLBACK_REPO_NAME;
|
|
45
|
+
}
|
|
@@ -29,10 +29,13 @@ export async function buildSyncSiteConfig(c: {
|
|
|
29
29
|
siteUrl: appConfig.siteUrl,
|
|
30
30
|
siteDescription: appConfig.siteDescription,
|
|
31
31
|
siteLanguage: appConfig.siteLanguage,
|
|
32
|
+
multilingualEnabled: appConfig.multilingualEnabled,
|
|
33
|
+
additionalLanguages: appConfig.additionalLanguages,
|
|
32
34
|
showJantBrandingOnHome: appConfig.showJantBrandingOnHome,
|
|
33
35
|
publicApiEnabled: appConfig.publicApiEnabled,
|
|
34
36
|
rssFeedsEnabled: appConfig.rssFeedsEnabled,
|
|
35
37
|
mainRssFeed: appConfig.mainRssFeed,
|
|
38
|
+
archiveDefaultLayout: appConfig.archiveDefaultLayout,
|
|
36
39
|
siteFooter: appConfig.siteFooter,
|
|
37
40
|
showHeaderAvatar: appConfig.showHeaderAvatar,
|
|
38
41
|
siteAvatarUrl: appConfig.siteAvatarUrl,
|
package/src/lib/hosted-signin.ts
CHANGED
|
@@ -60,7 +60,7 @@ export function getHostedControlPlaneResetUrl(
|
|
|
60
60
|
export function getHostedControlPlaneDashboardUrl(
|
|
61
61
|
env: object | undefined | null,
|
|
62
62
|
): string | null {
|
|
63
|
-
return buildHostedControlPlaneUrl(env, "/
|
|
63
|
+
return buildHostedControlPlaneUrl(env, "/app");
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
export function getHostedControlPlaneAccountUrl(
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conditional-request helpers.
|
|
3
|
+
*
|
|
4
|
+
* Transport-level only: deriving an entity tag for a response body, and
|
|
5
|
+
* deciding whether the request already holds it. Nothing here knows what the
|
|
6
|
+
* body represents.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const textEncoder = new TextEncoder();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* One entity tag inside an `If-None-Match` list, weak prefix included.
|
|
13
|
+
*
|
|
14
|
+
* Matching quoted runs rather than splitting on commas is deliberate: RFC 9110
|
|
15
|
+
* allows a comma inside an entity tag's opaque part, so a naive split can tear
|
|
16
|
+
* one tag into two that match nothing.
|
|
17
|
+
*/
|
|
18
|
+
const ENTITY_TAG = /(?:W\/)?"[^"]*"/g;
|
|
19
|
+
|
|
20
|
+
/** Drop the weak prefix, which the weak comparison in RFC 9110 ignores. */
|
|
21
|
+
function opaqueTag(tag: string): string {
|
|
22
|
+
return tag.startsWith("W/") ? tag.slice(2) : tag;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Derive a strong entity tag from a response body.
|
|
27
|
+
*
|
|
28
|
+
* The tag is a truncated SHA-256 of the exact bytes that would be sent, so
|
|
29
|
+
* two equal tags do mean two identical responses — which is what makes the
|
|
30
|
+
* tag strong rather than `W/`-weak. 128 bits is far past the point where a
|
|
31
|
+
* collision could plausibly pin a stale body in a cache.
|
|
32
|
+
*
|
|
33
|
+
* @param body - Serialized response body
|
|
34
|
+
* @returns Quoted entity tag, ready for an `ETag` header
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const etag = await strongETag(xml); // '"9f86d081884c7d65..."'
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export async function strongETag(body: string): Promise<string> {
|
|
41
|
+
const digest = await crypto.subtle.digest(
|
|
42
|
+
"SHA-256",
|
|
43
|
+
textEncoder.encode(body),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
let hex = "";
|
|
47
|
+
for (const byte of new Uint8Array(digest, 0, 16)) {
|
|
48
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return `"${hex}"`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Whether a request's `If-None-Match` already holds `etag`.
|
|
56
|
+
*
|
|
57
|
+
* Comparison is the weak one RFC 9110 requires for `If-None-Match`: the weak
|
|
58
|
+
* prefix is ignored on both sides, and any tag in the list can match. `*`
|
|
59
|
+
* matches whenever the resource exists, which it does by the time a response
|
|
60
|
+
* body has been rendered.
|
|
61
|
+
*
|
|
62
|
+
* @param header - Raw `If-None-Match` header value, absent when not sent
|
|
63
|
+
* @param etag - Entity tag of the response about to be sent
|
|
64
|
+
* @returns `true` when the response can be answered with `304`
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* matchesIfNoneMatch('W/"abc", "def"', '"def"'); // true
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export function matchesIfNoneMatch(
|
|
71
|
+
header: string | null | undefined,
|
|
72
|
+
etag: string,
|
|
73
|
+
): boolean {
|
|
74
|
+
if (!header) return false;
|
|
75
|
+
if (header.trim() === "*") return true;
|
|
76
|
+
|
|
77
|
+
const target = opaqueTag(etag);
|
|
78
|
+
for (const candidate of header.match(ENTITY_TAG) ?? []) {
|
|
79
|
+
if (opaqueTag(candidate) === target) return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Headers a `304` carries over from the response it stands in for.
|
|
87
|
+
*
|
|
88
|
+
* RFC 9110 asks for the fields a cache would otherwise have to invent or
|
|
89
|
+
* discard: the validator itself, the freshness the entry is stored under, and
|
|
90
|
+
* the identity of what was validated.
|
|
91
|
+
*/
|
|
92
|
+
const NOT_MODIFIED_HEADERS = [
|
|
93
|
+
"Cache-Control",
|
|
94
|
+
"Content-Location",
|
|
95
|
+
"Date",
|
|
96
|
+
"ETag",
|
|
97
|
+
"Expires",
|
|
98
|
+
"Vary",
|
|
99
|
+
] as const;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Answer a conditional request with `304` when the response is one the client
|
|
103
|
+
* already holds.
|
|
104
|
+
*
|
|
105
|
+
* This is the only place in the app that turns a validator into a `304`, and
|
|
106
|
+
* it runs at the outer edge of the request — outside the Worker response
|
|
107
|
+
* cache. Both halves of that matter: a cache hit is revalidated without
|
|
108
|
+
* reaching a route, and a cache miss still renders and stores the whole
|
|
109
|
+
* document before the body is dropped, so the next reader gets a cache entry
|
|
110
|
+
* rather than a `304` that cannot be stored.
|
|
111
|
+
*
|
|
112
|
+
* Anything without an entity tag passes through untouched, so a route opts in
|
|
113
|
+
* simply by emitting one.
|
|
114
|
+
*
|
|
115
|
+
* @param request - Incoming request, read for `If-None-Match`
|
|
116
|
+
* @param response - Response the app produced
|
|
117
|
+
* @returns A bare `304`, or the response unchanged
|
|
118
|
+
* @example
|
|
119
|
+
* ```ts
|
|
120
|
+
* return withConditionalResponse(request, await handle(request));
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export function withConditionalResponse(
|
|
124
|
+
request: Request,
|
|
125
|
+
response: Response,
|
|
126
|
+
): Response {
|
|
127
|
+
if (request.method !== "GET" && request.method !== "HEAD") return response;
|
|
128
|
+
if (response.status !== 200) return response;
|
|
129
|
+
|
|
130
|
+
// A 304 drops the body, and with it any header that only makes sense
|
|
131
|
+
// attached to one. A response setting a cookie is left alone rather than
|
|
132
|
+
// stripped down to a validator.
|
|
133
|
+
if (response.headers.has("Set-Cookie")) return response;
|
|
134
|
+
|
|
135
|
+
const etag = response.headers.get("ETag");
|
|
136
|
+
if (!etag) return response;
|
|
137
|
+
if (!matchesIfNoneMatch(request.headers.get("If-None-Match"), etag)) {
|
|
138
|
+
return response;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const headers = new Headers();
|
|
142
|
+
for (const name of NOT_MODIFIED_HEADERS) {
|
|
143
|
+
const value = response.headers.get(name);
|
|
144
|
+
if (value !== null) headers.set(name, value);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return new Response(null, { status: 304, headers });
|
|
148
|
+
}
|
package/src/lib/hugo-markdown.ts
CHANGED
|
@@ -34,6 +34,24 @@ export interface HugoCollectionRef {
|
|
|
34
34
|
pinned_at?: string | null;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* A smart collection's conditions on its section page (`type:
|
|
39
|
+
* smart_collection`). The stored selection, with two changes of spelling:
|
|
40
|
+
* `collection` is one collection's slug rather than a list of IDs, and
|
|
41
|
+
* `media` is `any`, `none`, or a list of kinds. The theme's
|
|
42
|
+
* `smart-collection-members` partial evaluates it; an import recreates the
|
|
43
|
+
* smart collection from it.
|
|
44
|
+
*/
|
|
45
|
+
export interface HugoSmartCollectionSelection {
|
|
46
|
+
collection?: string;
|
|
47
|
+
format?: string;
|
|
48
|
+
title?: boolean;
|
|
49
|
+
year?: number;
|
|
50
|
+
media?: "any" | "none" | string[];
|
|
51
|
+
replies?: boolean;
|
|
52
|
+
visibility?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
37
55
|
/**
|
|
38
56
|
* Jant media attachment descriptor. Stored flat inside the `media:` front-
|
|
39
57
|
* matter array. `src` is either:
|
|
@@ -90,6 +108,12 @@ export interface HugoFrontMatter {
|
|
|
90
108
|
id?: string;
|
|
91
109
|
title?: string;
|
|
92
110
|
date?: string;
|
|
111
|
+
/**
|
|
112
|
+
* When the post was created, written only when it differs from `date` (a
|
|
113
|
+
* published post dated before or after it was written). Restored on import
|
|
114
|
+
* so thread order and "last edited" survive a move.
|
|
115
|
+
*/
|
|
116
|
+
created?: string;
|
|
93
117
|
updated?: string;
|
|
94
118
|
slug?: string;
|
|
95
119
|
type?: string;
|
|
@@ -97,13 +121,31 @@ export interface HugoFrontMatter {
|
|
|
97
121
|
|
|
98
122
|
// Hugo routing
|
|
99
123
|
aliases?: string[];
|
|
124
|
+
/**
|
|
125
|
+
* Published root bundles only: the `<id>` Jant's feeds gave the Thread's
|
|
126
|
+
* entry — its permalink, absolute, with no trailing slash. The theme's feed
|
|
127
|
+
* writes it as the entry's `<id>` so readers don't show the post again after
|
|
128
|
+
* a move. Written by the export, never read by the import.
|
|
129
|
+
*/
|
|
130
|
+
feed_id?: string;
|
|
100
131
|
build?: HugoBuildOptions;
|
|
132
|
+
/**
|
|
133
|
+
* Reply bundles only: 1-based position in the Thread. Jant orders a Thread
|
|
134
|
+
* by creation time, then ID; replies written in the same second tie on
|
|
135
|
+
* `date`, so the theme and the importer order by this instead.
|
|
136
|
+
*/
|
|
137
|
+
weight?: number;
|
|
101
138
|
|
|
102
139
|
// Jant post payload (flat — no `extra` nesting)
|
|
103
140
|
format?: string;
|
|
104
141
|
status?: string;
|
|
105
142
|
visibility?: string;
|
|
106
143
|
summary_text?: string;
|
|
144
|
+
/**
|
|
145
|
+
* The timeline cuts this post's text short. Written only when true, so a
|
|
146
|
+
* consumer reads its presence rather than its value.
|
|
147
|
+
*/
|
|
148
|
+
truncated?: boolean;
|
|
107
149
|
link_url?: string;
|
|
108
150
|
source_name?: string;
|
|
109
151
|
source_url?: string;
|
|
@@ -111,6 +153,16 @@ export interface HugoFrontMatter {
|
|
|
111
153
|
rating?: number | null;
|
|
112
154
|
featured_at?: string | null;
|
|
113
155
|
pinned_at?: string | null;
|
|
156
|
+
/**
|
|
157
|
+
* BCP 47 content language. Root bundles only — replies take the root's.
|
|
158
|
+
*/
|
|
159
|
+
language?: string;
|
|
160
|
+
/**
|
|
161
|
+
* Opaque shared key linking posts that are translations of one another. Root
|
|
162
|
+
* bundles only. Meaningful within one export; carried across so a full-site
|
|
163
|
+
* restore keeps translated posts linked.
|
|
164
|
+
*/
|
|
165
|
+
translation_group?: string;
|
|
114
166
|
/**
|
|
115
167
|
* Reply bundles only: the reply was published without announcing the Thread
|
|
116
168
|
* on Latest. Round-tripped so a restore rebuilds the same ordering.
|
|
@@ -128,6 +180,14 @@ export interface HugoFrontMatter {
|
|
|
128
180
|
collections?: HugoCollectionRef[];
|
|
129
181
|
media?: JantMedia[];
|
|
130
182
|
|
|
183
|
+
// Smart collection section pages
|
|
184
|
+
selection?: HugoSmartCollectionSelection;
|
|
185
|
+
/**
|
|
186
|
+
* `list` or `grid`; absent when the smart collection follows the site's
|
|
187
|
+
* archive layout. Not `layout`, which Hugo reads as a template name.
|
|
188
|
+
*/
|
|
189
|
+
display_layout?: string;
|
|
190
|
+
|
|
131
191
|
// Escape hatch for page-specific export metadata.
|
|
132
192
|
[key: string]: unknown;
|
|
133
193
|
}
|
|
@@ -198,6 +258,7 @@ const FRONT_MATTER_KEY_ORDER: readonly string[] = [
|
|
|
198
258
|
"type",
|
|
199
259
|
"draft",
|
|
200
260
|
"aliases",
|
|
261
|
+
"feed_id",
|
|
201
262
|
"build",
|
|
202
263
|
|
|
203
264
|
// Post payload
|
|
@@ -205,6 +266,7 @@ const FRONT_MATTER_KEY_ORDER: readonly string[] = [
|
|
|
205
266
|
"status",
|
|
206
267
|
"visibility",
|
|
207
268
|
"summary_text",
|
|
269
|
+
"truncated",
|
|
208
270
|
"link_url",
|
|
209
271
|
"source_name",
|
|
210
272
|
"source_url",
|
package/src/lib/icons.ts
CHANGED
|
@@ -24,17 +24,24 @@ function toPascalCase(name: string): string {
|
|
|
24
24
|
* Get SVG markup for a Lucide icon by kebab-case name.
|
|
25
25
|
*
|
|
26
26
|
* @param name - Kebab-case icon name
|
|
27
|
+
* @param className - Extra classes for the `<svg>`, appended to Lucide's own.
|
|
28
|
+
* `icon-fine` is the project's lighter stroke.
|
|
27
29
|
* @returns SVG string or null when the icon is unknown
|
|
28
30
|
*
|
|
29
31
|
* @example
|
|
30
32
|
* ```ts
|
|
31
33
|
* getIconSvg("book-open");
|
|
34
|
+
* getIconSvg("funnel", "icon-fine");
|
|
32
35
|
* ```
|
|
33
36
|
*/
|
|
34
|
-
export function getIconSvg(name: string): string | null {
|
|
37
|
+
export function getIconSvg(name: string, className?: string): string | null {
|
|
35
38
|
const pascalName = toPascalCase(name);
|
|
36
39
|
const svg = (lucideIcons as Record<string, string>)[pascalName];
|
|
37
|
-
|
|
40
|
+
if (typeof svg !== "string") return null;
|
|
41
|
+
if (!className) return svg;
|
|
42
|
+
// Lucide always ships a `class="lucide lucide-…"`, so extend it rather than
|
|
43
|
+
// adding a second class attribute the parser would drop.
|
|
44
|
+
return svg.replace(/class="([^"]*)"/, `class="$1 ${className}"`);
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
/**
|
package/src/lib/ids.ts
CHANGED
|
@@ -5,9 +5,12 @@ export const ID_PREFIX = {
|
|
|
5
5
|
site: "sit",
|
|
6
6
|
siteDomain: "sdm",
|
|
7
7
|
post: "pst",
|
|
8
|
+
/** Shared key linking Posts that are translations of one another. */
|
|
9
|
+
translationGroup: "tgr",
|
|
8
10
|
media: "med",
|
|
9
11
|
uploadSession: "upl",
|
|
10
12
|
collection: "col",
|
|
13
|
+
smartCollection: "smc",
|
|
11
14
|
path: "pth",
|
|
12
15
|
collectionDirectoryItem: "cdi",
|
|
13
16
|
navItem: "nav",
|
package/src/lib/image.ts
CHANGED
|
@@ -34,7 +34,11 @@ export interface ImageOptions {
|
|
|
34
34
|
* - Cloudinary
|
|
35
35
|
* - Any service with similar URL-based transformation API
|
|
36
36
|
*
|
|
37
|
-
*
|
|
37
|
+
* A root-relative source is written without its leading slash, the form the
|
|
38
|
+
* transformation service resolves against its own host. Kept, it would produce
|
|
39
|
+
* `…/width=200//media/abc123`, which Cloudflare cannot fetch (`err=9404`).
|
|
40
|
+
*
|
|
41
|
+
* @param originalUrl - The original image URL, absolute or root-relative
|
|
38
42
|
* @param transformUrl - The base URL for transformations (e.g., `https://example.com/cdn-cgi/image`)
|
|
39
43
|
* @param options - Transformation options (width, height, quality, format, fit)
|
|
40
44
|
* @returns The transformed URL or original URL if transformations are not configured
|
|
@@ -45,9 +49,13 @@ export interface ImageOptions {
|
|
|
45
49
|
* getImageUrl("/media/abc123", undefined, { width: 200 });
|
|
46
50
|
* // Returns: "/media/abc123"
|
|
47
51
|
*
|
|
48
|
-
* // With transform URL -
|
|
52
|
+
* // With transform URL and a root-relative source
|
|
49
53
|
* getImageUrl("/media/abc123", "https://example.com/cdn-cgi/image", { width: 200, quality: 80 });
|
|
50
|
-
* // Returns: "https://example.com/cdn-cgi/image/width=200,quality=80/
|
|
54
|
+
* // Returns: "https://example.com/cdn-cgi/image/width=200,quality=80/media/abc123"
|
|
55
|
+
*
|
|
56
|
+
* // With transform URL and an absolute source
|
|
57
|
+
* getImageUrl("https://cdn.example.com/media/abc123", "https://example.com/cdn-cgi/image", { width: 200 });
|
|
58
|
+
* // Returns: "https://example.com/cdn-cgi/image/width=200/https://cdn.example.com/media/abc123"
|
|
51
59
|
* ```
|
|
52
60
|
*/
|
|
53
61
|
export function getImageUrl(
|
|
@@ -70,7 +78,12 @@ export function getImageUrl(
|
|
|
70
78
|
return originalUrl;
|
|
71
79
|
}
|
|
72
80
|
|
|
73
|
-
|
|
81
|
+
const base = transformUrl.replace(/\/+$/, "");
|
|
82
|
+
const source =
|
|
83
|
+
originalUrl.startsWith("/") && !originalUrl.startsWith("//")
|
|
84
|
+
? originalUrl.slice(1)
|
|
85
|
+
: originalUrl;
|
|
86
|
+
return `${base}/${params.join(",")}/${source}`;
|
|
74
87
|
}
|
|
75
88
|
|
|
76
89
|
/**
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Links into the published documentation
|
|
3
|
+
*
|
|
4
|
+
* Everything under `docs/` is published on the Jant site: `docs/multilingual.md`
|
|
5
|
+
* is served at `/docs/multilingual`, lowercased and without the extension.
|
|
6
|
+
*
|
|
7
|
+
* The host is resolved once, here, so no call site writes a domain. Development
|
|
8
|
+
* builds point at the control plane running locally, which serves the docs
|
|
9
|
+
* being edited rather than the released ones.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { IS_VITE_DEV } from "./build-env.js";
|
|
13
|
+
|
|
14
|
+
/** Docs host for released builds. */
|
|
15
|
+
const DOCS_ORIGIN = "https://jant.me";
|
|
16
|
+
|
|
17
|
+
/** Docs host during development. */
|
|
18
|
+
const DEV_DOCS_ORIGIN = "https://jant-cloud.localtest.me";
|
|
19
|
+
|
|
20
|
+
/** Documentation index for the current environment. */
|
|
21
|
+
export const JANT_DOCS_BASE_URL = `${
|
|
22
|
+
IS_VITE_DEV ? DEV_DOCS_ORIGIN : DOCS_ORIGIN
|
|
23
|
+
}/docs`;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Build a link to one documentation page.
|
|
27
|
+
*
|
|
28
|
+
* @param page - Published page slug, optionally with a `#section` anchor. It is
|
|
29
|
+
* the file name under `docs/`, lowercased and without `.md`. Omit it to link
|
|
30
|
+
* the documentation index.
|
|
31
|
+
* @returns Absolute URL to that page on the current environment's docs site
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* getJantDocsUrl("multilingual"); // "https://jant.me/docs/multilingual"
|
|
35
|
+
* getJantDocsUrl("configuration#required");
|
|
36
|
+
* // "https://jant.me/docs/configuration#required"
|
|
37
|
+
* getJantDocsUrl(); // "https://jant.me/docs"
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function getJantDocsUrl(page = ""): string {
|
|
41
|
+
return page ? `${JANT_DOCS_BASE_URL}/${page}` : JANT_DOCS_BASE_URL;
|
|
42
|
+
}
|