@jant/core 0.6.14 → 0.6.16
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/bin/commands/import-site.js +88 -13
- package/bin/commands/migrate.js +55 -0
- package/bin/commands/posts/rebuild-html.js +184 -0
- package/bin/commands/site/snapshot/import.js +14 -10
- package/bin/lib/d1-query.js +23 -5
- package/bin/lib/migration-runner.js +29 -7
- package/bin/lib/node-database.js +32 -0
- package/bin/lib/site-snapshot.js +347 -46
- package/bin/lib/sql-export.js +1 -1
- package/bin/lib/thread-collection-migration.js +326 -0
- package/dist/app-C75Zc8dD.js +6 -0
- package/dist/{app-2i7-WQHg.js → app-C7_4oab9.js} +7774 -6425
- package/dist/client/.vite/manifest.json +8 -8
- package/dist/client/_assets/chunks/{url-pLre2DM_.js → url-CU9pEbk5.js} +1 -1
- package/dist/client/_assets/{client-BvID5ucz.js → client-Bq0vre8Y.js} +59 -38
- package/dist/client/_assets/client-D3VZyG4L.css +2 -0
- package/dist/client/_assets/client-auth-F2rb9WfF.js +5571 -0
- package/dist/{env-OHRKGcMj.js → env-BPYViDJJ.js} +1 -1
- package/dist/{export-DWn149b7.js → export-Dl5KCiEs.js} +1348 -128
- package/dist/{github-api-BgSiE71w.js → github-api-BNF35Lkx.js} +1 -1
- package/dist/{github-app-BbklkFmU.js → github-app-DdiD-bC4.js} +1 -1
- package/dist/{github-sync-C0zyF6XS.js → github-sync-Bqg62IvV.js} +3 -3
- package/dist/{github-sync-Ckk0sJxK.js → github-sync-HdK2EgvJ.js} +3 -3
- package/dist/index.js +5 -5
- package/dist/node.js +7 -7
- package/dist/{url-BMYO-Zlt.js → url-CbLAtlZe.js} +517 -12
- package/package.json +11 -10
- package/src/__tests__/bin/posts-rebuild-html.test.ts +137 -0
- package/src/__tests__/dev-navigation-defaults.test.ts +22 -0
- package/src/__tests__/export-hugo-build.test.ts +63 -4
- package/src/__tests__/export-import-roundtrip.test.ts +6 -4
- package/src/__tests__/export-service.test.ts +142 -9
- package/src/__tests__/helpers/app.ts +3 -0
- package/src/__tests__/helpers/export-fixtures.ts +2 -0
- package/src/__tests__/import-site-command.test.ts +65 -0
- package/src/__tests__/mise-config.test.ts +22 -0
- package/src/__tests__/vite-config.test.ts +45 -0
- package/src/app.tsx +16 -3
- package/src/client/__tests__/collection-created-notice.test.ts +36 -0
- package/src/client/__tests__/collection-form-bridge.test.ts +10 -1
- package/src/client/__tests__/collection-page-actions.test.ts +71 -2
- package/src/client/__tests__/compose-bridge.test.ts +316 -2
- package/src/client/__tests__/compose-shortcuts.test.ts +20 -0
- package/src/client/__tests__/feed-video-player.test.ts +16 -1
- package/src/client/__tests__/footnote-rail.test.ts +472 -0
- package/src/client/__tests__/hydrate-partial.test.ts +3 -0
- package/src/client/__tests__/toast.test.ts +82 -1
- package/src/client/collection-created-notice.ts +66 -0
- package/src/client/collection-form-bridge.ts +17 -0
- package/src/client/collection-navigation.ts +44 -0
- package/src/client/collection-page-actions.ts +57 -1
- package/src/client/components/__tests__/compose-format-convert.test.ts +0 -19
- package/src/client/components/__tests__/jant-collection-directory.test.ts +155 -6
- package/src/client/components/__tests__/jant-command-palette.test.ts +51 -0
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +2268 -206
- package/src/client/components/__tests__/jant-compose-editor.test.ts +147 -59
- package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +114 -64
- package/src/client/components/__tests__/jant-config-editor.test.ts +405 -0
- package/src/client/components/__tests__/jant-media-lightbox.test.ts +121 -0
- package/src/client/components/__tests__/jant-nav-manager.test.ts +538 -2
- package/src/client/components/__tests__/jant-post-form.test.ts +2 -2
- package/src/client/components/__tests__/jant-post-menu.test.ts +47 -5
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +2 -2
- package/src/client/components/__tests__/jant-settings-general.test.ts +3 -3
- package/src/client/components/collection-manager-types.ts +9 -3
- package/src/client/components/compose-format-convert.ts +5 -1
- package/src/client/components/compose-types.ts +55 -3
- package/src/client/components/jant-collection-directory.ts +165 -30
- package/src/client/components/jant-command-palette.ts +14 -7
- package/src/client/components/jant-compose-dialog.ts +1542 -694
- package/src/client/components/jant-compose-editor.ts +237 -99
- package/src/client/components/jant-compose-fullscreen.ts +27 -9
- package/src/client/components/jant-config-editor.ts +722 -0
- package/src/client/components/jant-media-lightbox.ts +100 -18
- package/src/client/components/jant-nav-manager.ts +1283 -54
- package/src/client/components/jant-post-form.ts +3 -3
- package/src/client/components/jant-post-menu.ts +177 -95
- package/src/client/components/nav-manager-types.ts +51 -2
- package/src/client/components/post-form-types.ts +1 -1
- package/src/client/compose-bridge.ts +147 -123
- package/src/client/compose-launch.ts +13 -0
- package/src/client/compose-shortcuts.ts +5 -1
- package/src/client/feed-video-player.ts +62 -2
- package/src/client/footnote-rail.ts +478 -0
- package/src/client/hydrate-partial.ts +2 -0
- package/src/client/media-lightbox-events.ts +63 -0
- package/src/client/post-refresh.ts +135 -0
- package/src/client/site-header-fragment.ts +38 -0
- package/src/client/tiptap/__tests__/blockquote-editing.test.ts +201 -0
- package/src/client/tiptap/__tests__/clear-formatting.test.ts +168 -0
- package/src/client/tiptap/__tests__/link-input-rules.test.ts +121 -0
- package/src/client/tiptap/__tests__/list-editing.test.ts +447 -0
- package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +727 -4
- package/src/client/tiptap/__tests__/table-controls.test.ts +263 -0
- package/src/client/tiptap/__tests__/table-size-picker.test.ts +179 -0
- package/src/client/tiptap/bubble-menu.ts +88 -10
- package/src/client/tiptap/continuous-lists.ts +102 -0
- package/src/client/tiptap/create-editor.ts +3 -0
- package/src/client/tiptap/extensions.ts +14 -5
- package/src/client/tiptap/link-input-rules.ts +4 -4
- package/src/client/tiptap/markdown-clipboard.ts +608 -11
- package/src/client/tiptap/slash-commands.ts +8 -1
- package/src/client/tiptap/structural-keymap.ts +211 -0
- package/src/client/tiptap/tab-indent.ts +8 -22
- package/src/client/tiptap/table-control-labels.ts +30 -0
- package/src/client/tiptap/table-controls.ts +537 -0
- package/src/client/tiptap/table-size-picker.ts +254 -0
- package/src/client/toast.ts +133 -39
- package/src/client-auth.ts +1 -0
- package/src/client.ts +1 -0
- package/src/db/__tests__/backfill-thread-activity.test.ts +234 -0
- package/src/db/__tests__/d1-query.test.ts +43 -1
- package/src/db/__tests__/migration-runner.test.ts +63 -0
- package/src/db/__tests__/migrations.test.ts +469 -0
- package/src/db/__tests__/thread-activity.test.ts +113 -0
- package/src/db/__tests__/thread-collection-migration.test.ts +364 -0
- package/src/db/backfills/0005_split_thread_activity_from_quiet_replies.sql +68 -0
- package/src/db/migrations/0027_old_blue_blade.sql +39 -0
- package/src/db/migrations/0028_superb_post.sql +2 -0
- package/src/db/migrations/0029_boring_magma.sql +1 -0
- package/src/db/migrations/0030_typical_vivisector.sql +50 -0
- package/src/db/migrations/0031_many_ego.sql +3 -0
- package/src/db/migrations/meta/0027_snapshot.json +2511 -0
- package/src/db/migrations/meta/0028_snapshot.json +2511 -0
- package/src/db/migrations/meta/0029_snapshot.json +2519 -0
- package/src/db/migrations/meta/0030_snapshot.json +2541 -0
- package/src/db/migrations/meta/0031_snapshot.json +2562 -0
- package/src/db/migrations/meta/_journal.json +35 -0
- package/src/db/migrations/pg/0025_moaning_epoch.sql +39 -0
- package/src/db/migrations/pg/0026_talented_killer_shrike.sql +2 -0
- package/src/db/migrations/pg/0027_short_cannonball.sql +1 -0
- package/src/db/migrations/pg/0028_perfect_sharon_carter.sql +27 -0
- package/src/db/migrations/pg/0029_rare_hairball.sql +3 -0
- package/src/db/migrations/pg/meta/0025_snapshot.json +3204 -0
- package/src/db/migrations/pg/meta/0026_snapshot.json +3204 -0
- package/src/db/migrations/pg/meta/0027_snapshot.json +3211 -0
- package/src/db/migrations/pg/meta/0028_snapshot.json +3248 -0
- package/src/db/migrations/pg/meta/0029_snapshot.json +3289 -0
- package/src/db/migrations/pg/meta/_journal.json +35 -0
- package/src/db/pg/schema.ts +46 -20
- package/src/db/rehearsal-fixtures/demo-current.json +39 -4
- package/src/db/rehearsal-fixtures/demo-current.sql +121 -6
- package/src/db/schema.ts +48 -20
- package/src/db/thread-activity.ts +101 -0
- package/src/i18n/__tests__/detect.test.ts +8 -2
- package/src/i18n/detect.ts +14 -5
- package/src/i18n/locales/public/en.po +222 -51
- package/src/i18n/locales/public/en.ts +1 -1
- package/src/i18n/locales/public/zh-Hans.po +216 -45
- package/src/i18n/locales/public/zh-Hans.ts +1 -1
- package/src/i18n/locales/public/zh-Hant.po +216 -45
- package/src/i18n/locales/public/zh-Hant.ts +1 -1
- package/src/i18n/locales/settings/en.po +513 -10
- package/src/i18n/locales/settings/en.ts +1 -1
- package/src/i18n/locales/settings/zh-Hans.po +515 -12
- package/src/i18n/locales/settings/zh-Hans.ts +1 -1
- package/src/i18n/locales/settings/zh-Hant.po +515 -12
- package/src/i18n/locales/settings/zh-Hant.ts +1 -1
- package/src/lib/__tests__/api-settings.test.ts +299 -0
- package/src/lib/__tests__/constants.test.ts +5 -0
- package/src/lib/__tests__/feed-policy.test.ts +25 -0
- package/src/lib/__tests__/feed.test.ts +168 -0
- package/src/lib/__tests__/footnotes-upgrade.test.ts +181 -0
- package/src/lib/__tests__/markdown-roundtrip-embed.test.ts +61 -0
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +32 -4
- package/src/lib/__tests__/markdown.test.ts +24 -6
- package/src/lib/__tests__/navigation.test.ts +59 -2
- package/src/lib/__tests__/post-body-html.test.ts +176 -0
- package/src/lib/__tests__/post-display.test.ts +64 -0
- package/src/lib/__tests__/resolve-config.test.ts +113 -1
- package/src/lib/__tests__/slug.test.ts +18 -0
- package/src/lib/__tests__/summary.test.ts +82 -0
- package/src/lib/__tests__/timeline.test.ts +109 -70
- package/src/lib/__tests__/timezones.test.ts +31 -3
- package/src/lib/__tests__/tiptap-render.test.ts +268 -9
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +6 -0
- package/src/lib/__tests__/translit.test.ts +87 -0
- package/src/lib/__tests__/url.test.ts +53 -0
- package/src/lib/__tests__/view.test.ts +53 -0
- package/src/lib/__tests__/worker-response-cache.test.ts +18 -1
- package/src/lib/api-settings.ts +239 -12
- package/src/lib/collection-sort.ts +7 -5
- package/src/lib/constants.ts +2 -0
- package/src/lib/feed-path.ts +25 -0
- package/src/lib/feed-policy.ts +64 -0
- package/src/lib/feed.ts +113 -11
- package/src/lib/footnotes.ts +264 -0
- package/src/lib/github-sync-site-config.ts +2 -0
- package/src/lib/hugo-markdown.ts +15 -3
- package/src/lib/jant-branding.ts +1 -0
- package/src/lib/link-preview.ts +25 -0
- package/src/lib/markdown-manager.ts +193 -5
- package/src/lib/markdown.ts +10 -3
- package/src/lib/navigation.ts +16 -3
- package/src/lib/post-body-html.ts +131 -0
- package/src/lib/post-display.ts +47 -8
- package/src/lib/render.tsx +9 -2
- package/src/lib/resolve-config.ts +100 -16
- package/src/lib/schemas.ts +133 -1
- package/src/lib/settings-paths.ts +5 -0
- package/src/lib/site-skill.ts +78 -0
- package/src/lib/summary.ts +16 -5
- package/src/lib/timeline.ts +137 -131
- package/src/lib/timezones.ts +54 -14
- package/src/lib/tiptap-render.ts +359 -83
- package/src/lib/translit.ts +288 -0
- package/src/lib/url.ts +123 -12
- package/src/lib/view.ts +58 -20
- package/src/lib/worker-response-cache.ts +11 -18
- package/src/middleware/__tests__/public-content-access.test.ts +91 -0
- package/src/middleware/config.ts +2 -2
- package/src/middleware/public-content-access.ts +87 -0
- package/src/node/__tests__/cli-export.test.ts +14 -0
- package/src/node/__tests__/cli-migrate.test.ts +6 -0
- package/src/node/__tests__/cli-site-snapshot.test.ts +205 -2
- package/src/node/__tests__/cli-snapshot-meta.test.ts +100 -0
- package/src/node/index.ts +2 -1
- package/src/preset.css +16 -2
- package/src/routes/__tests__/compose.test.ts +51 -0
- package/src/routes/__tests__/site-skill.test.ts +112 -0
- package/src/routes/api/__tests__/collections.test.ts +91 -19
- package/src/routes/api/__tests__/mcp.test.ts +65 -1
- package/src/routes/api/__tests__/nav-items.test.ts +91 -0
- package/src/routes/api/__tests__/palette.test.ts +44 -0
- package/src/routes/api/__tests__/public-access.test.ts +81 -0
- package/src/routes/api/__tests__/settings.test.ts +217 -0
- package/src/routes/api/collections.ts +22 -26
- package/src/routes/api/export.ts +2 -0
- package/src/routes/api/internal/__tests__/post-body-html.test.ts +163 -0
- package/src/routes/api/internal/__tests__/sites.test.ts +35 -2
- package/src/routes/api/internal/post-body-html.ts +43 -0
- package/src/routes/api/internal/sites.ts +24 -0
- package/src/routes/api/nav-items.ts +24 -1
- package/src/routes/api/posts.ts +14 -10
- package/src/routes/api/public/__tests__/archive.test.ts +35 -0
- package/src/routes/api/public/__tests__/posts.test.ts +246 -0
- package/src/routes/api/public/archive.ts +4 -0
- package/src/routes/api/public/posts.ts +48 -24
- package/src/routes/api/search.ts +3 -0
- package/src/routes/api/settings.ts +33 -0
- package/src/routes/auth/__tests__/setup.test.ts +7 -4
- package/src/routes/compose.tsx +8 -3
- package/src/routes/dash/settings.tsx +45 -2
- package/src/routes/feed/__tests__/feed.test.ts +210 -6
- package/src/routes/feed/__tests__/sitemap.test.ts +68 -1
- package/src/routes/feed/feed.ts +23 -14
- package/src/routes/pages/__tests__/archive-params.test.ts +384 -3
- package/src/routes/pages/__tests__/collection-routing.test.ts +63 -5
- package/src/routes/pages/__tests__/collections.test.ts +9 -9
- package/src/routes/pages/__tests__/featured.test.ts +6 -2
- package/src/routes/pages/__tests__/preview.test.ts +176 -0
- package/src/routes/pages/__tests__/search.test.ts +69 -0
- package/src/routes/pages/archive.tsx +90 -22
- package/src/routes/pages/collection.tsx +32 -19
- package/src/routes/pages/collections.tsx +6 -1
- package/src/routes/pages/page.tsx +86 -11
- package/src/routes/site-skill.ts +18 -0
- package/src/services/__tests__/auth.test.ts +7 -5
- package/src/services/__tests__/collection.test.ts +190 -44
- package/src/services/__tests__/navigation.test.ts +229 -21
- package/src/services/__tests__/post-timeline.test.ts +249 -39
- package/src/services/__tests__/post.test.ts +844 -5
- package/src/services/__tests__/search.test.ts +48 -1
- package/src/services/__tests__/settings.test.ts +8 -0
- package/src/services/__tests__/site-admin.test.ts +121 -1
- package/src/services/auth.ts +2 -2
- package/src/services/bootstrap.ts +3 -2
- package/src/services/collection.ts +224 -167
- package/src/services/export-theme/assets/client-site.css +1 -1
- package/src/services/export-theme/assets/client-site.js +46 -25
- package/src/services/export-theme/layouts/_default/list.html +18 -4
- package/src/services/export-theme/layouts/_default/rss.xml +8 -12
- package/src/services/export-theme/layouts/featured/list.html +5 -4
- package/src/services/export-theme/layouts/partials/featured-thread.html +56 -0
- package/src/services/export-theme/layouts/partials/footer.html +1 -1
- package/src/services/export-theme/layouts/partials/media-gallery.html +1 -0
- package/src/services/export-theme/styles/main.css +30 -2
- package/src/services/export.ts +94 -85
- package/src/services/mcp.ts +37 -27
- package/src/services/media.ts +1 -1
- package/src/services/navigation.ts +185 -46
- package/src/services/path.ts +4 -1
- package/src/services/post.ts +1142 -342
- package/src/services/search.ts +17 -2
- package/src/services/settings.ts +2 -10
- package/src/services/site-admin.ts +63 -43
- package/src/style-cjk-jp.css +1 -1
- package/src/style-cjk-kr.css +1 -1
- package/src/style-cjk-tc.css +1 -1
- package/src/style-cjk.css +1 -1
- package/src/styles/components.css +540 -14
- package/src/styles/site-media.css +111 -19
- package/src/styles/tokens.css +76 -10
- package/src/styles/ui.css +2227 -1347
- package/src/types/bindings.ts +3 -0
- package/src/types/config.ts +243 -12
- package/src/types/constants.ts +22 -1
- package/src/types/entities.ts +14 -8
- package/src/types/operations.ts +18 -10
- package/src/types/props.ts +20 -2
- package/src/types/raw-assets.d.ts +5 -0
- package/src/types/views.ts +22 -4
- package/src/ui/__tests__/font-themes.test.ts +42 -30
- package/src/ui/compose/ComposeDialog.tsx +109 -19
- package/src/ui/dash/appearance/NavigationContent.tsx +332 -4
- package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +23 -0
- package/src/ui/dash/settings/ConfigEditorContent.tsx +578 -0
- package/src/ui/dash/settings/GeneralContent.tsx +15 -5
- package/src/ui/dash/settings/SettingsRootContent.tsx +21 -1
- package/src/ui/dash/settings/__tests__/ConfigEditorContent.test.tsx +98 -0
- package/src/ui/dash/settings/__tests__/SettingsRootContent.test.tsx +15 -0
- package/src/ui/feed/CuratedThreadPreview.tsx +16 -4
- package/src/ui/feed/LinkCard.tsx +2 -12
- package/src/ui/feed/LinkPreview.tsx +2 -7
- package/src/ui/feed/NoteCard.tsx +10 -20
- package/src/ui/feed/PostStatusBadges.tsx +42 -3
- package/src/ui/feed/QuoteCard.tsx +2 -12
- package/src/ui/feed/ThreadPreview.tsx +26 -34
- package/src/ui/feed/TimelineFeed.tsx +2 -2
- package/src/ui/feed/__tests__/thread-preview.test.ts +197 -56
- package/src/ui/feed/__tests__/timeline-cards.test.ts +178 -3
- package/src/ui/feed/thread-preview-state.ts +5 -7
- package/src/ui/font-themes.ts +74 -26
- package/src/ui/layouts/BaseLayout.tsx +22 -8
- package/src/ui/layouts/__tests__/BaseLayout.test.tsx +64 -1
- package/src/ui/pages/ArchivePage.tsx +131 -24
- package/src/ui/pages/CollectionPage.tsx +101 -22
- package/src/ui/pages/CollectionsPage.tsx +3 -1
- package/src/ui/pages/PostPage.tsx +39 -12
- package/src/ui/pages/SearchPage.tsx +6 -14
- package/src/ui/pages/__tests__/ArchivePage.test.tsx +70 -0
- package/src/ui/pages/__tests__/PostPage.test.tsx +88 -0
- package/src/ui/shared/CollectionDirectory.tsx +13 -9
- package/src/ui/shared/CollectionsManager.tsx +17 -7
- package/src/ui/shared/DraftPreviewBar.tsx +49 -0
- package/src/ui/shared/HomePageBranding.tsx +2 -2
- package/src/ui/shared/MediaGallery.tsx +1 -0
- package/src/ui/shared/PostFooter.tsx +38 -13
- package/src/ui/shared/__tests__/home-page-branding.test.tsx +1 -1
- package/src/ui/shared/__tests__/media-gallery.test.ts +1 -0
- package/src/ui/shared/__tests__/navigation-labels.test.ts +2 -2
- package/src/ui/shared/__tests__/post-footer.test.ts +15 -0
- package/src/ui/shared/collection-management-labels.ts +33 -2
- package/src/ui/shared/custom-icons.ts +5 -0
- package/src/ui/shared/navigation-labels.ts +2 -4
- package/src/ui/shared/post-article-attributes.ts +52 -0
- package/dist/app-DZDlmh7C.js +0 -6
- package/dist/client/_assets/client-Cevfpm8H.css +0 -2
- package/dist/client/_assets/client-auth-v3yvhbgZ.js +0 -4736
- package/src/client/tiptap/wrapping-input-rules.ts +0 -102
package/src/services/export.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
getDefaultJantAppleTouchIconBytes,
|
|
28
28
|
getDefaultJantFaviconIcoBytes,
|
|
29
29
|
} from "../lib/jant-branding.js";
|
|
30
|
+
import { getRootActivityAt } from "../db/thread-activity.js";
|
|
30
31
|
import { tiptapJsonToMarkdown } from "../lib/tiptap-to-markdown.js";
|
|
31
32
|
import { extractBodyText } from "../lib/summary.js";
|
|
32
33
|
import { getMediaUrl, getPublicUrlForProvider } from "../lib/image.js";
|
|
@@ -76,12 +77,20 @@ import PARTIAL_POST_CARD from "./export-theme/layouts/partials/post-card.html?ra
|
|
|
76
77
|
import PARTIAL_MEDIA_GALLERY from "./export-theme/layouts/partials/media-gallery.html?raw";
|
|
77
78
|
import PARTIAL_REPLY from "./export-theme/layouts/partials/reply.html?raw";
|
|
78
79
|
import PARTIAL_THREAD_PREVIEW from "./export-theme/layouts/partials/thread-preview.html?raw";
|
|
80
|
+
import PARTIAL_FEATURED_THREAD from "./export-theme/layouts/partials/featured-thread.html?raw";
|
|
79
81
|
import LAYOUT_RSS from "./export-theme/layouts/_default/rss.xml?raw";
|
|
80
82
|
import PARTIAL_FEED_POST_CONTENT from "./export-theme/layouts/partials/feed-post-content.xml?raw";
|
|
81
83
|
|
|
82
84
|
import type { StorageDriver } from "../lib/storage.js";
|
|
83
85
|
import { base64ToUint8Array } from "../lib/favicon.js";
|
|
84
|
-
import
|
|
86
|
+
import {
|
|
87
|
+
SYSTEM_NAV_KEYS,
|
|
88
|
+
type Collection,
|
|
89
|
+
type Media,
|
|
90
|
+
type NavItem,
|
|
91
|
+
type Post,
|
|
92
|
+
type SystemNavKey,
|
|
93
|
+
} from "../types.js";
|
|
85
94
|
|
|
86
95
|
/** A file entry in the exported Hugo site. */
|
|
87
96
|
export interface ExportFile {
|
|
@@ -102,6 +111,10 @@ export interface SiteConfig {
|
|
|
102
111
|
siteDescription: string;
|
|
103
112
|
siteLanguage: string;
|
|
104
113
|
showJantBrandingOnHome: boolean;
|
|
114
|
+
/** Whether anonymous JSON reads are enabled on the source Jant site. */
|
|
115
|
+
publicApiEnabled: boolean;
|
|
116
|
+
/** Whether the exported Hugo site should emit Atom feeds. */
|
|
117
|
+
rssFeedsEnabled: boolean;
|
|
105
118
|
/** "latest" or "featured" — drives the default RSS nav link in the exported site. */
|
|
106
119
|
mainRssFeed: string;
|
|
107
120
|
siteFooter: string;
|
|
@@ -171,7 +184,7 @@ interface ExportCollectionDirectorySourceItem {
|
|
|
171
184
|
slug: string;
|
|
172
185
|
title: string;
|
|
173
186
|
description?: string | null;
|
|
174
|
-
|
|
187
|
+
threadCount?: number;
|
|
175
188
|
recentActivityAt?: number;
|
|
176
189
|
};
|
|
177
190
|
}
|
|
@@ -184,14 +197,14 @@ interface SiteIconAssets {
|
|
|
184
197
|
}
|
|
185
198
|
|
|
186
199
|
interface ExportedCollectionMetrics {
|
|
187
|
-
|
|
200
|
+
threadCount: number;
|
|
188
201
|
recentActivityAt: number;
|
|
189
202
|
}
|
|
190
203
|
|
|
191
204
|
/**
|
|
192
205
|
* A single `collections:` front-matter entry, already resolved to its
|
|
193
206
|
* Hugo-visible slug. Assembled from
|
|
194
|
-
* `collectionService.
|
|
207
|
+
* `collectionService.getCollectionEntriesByThreadIds` + `collectionSlugMap`.
|
|
195
208
|
*/
|
|
196
209
|
interface ExportedCollectionEntry {
|
|
197
210
|
slug: string;
|
|
@@ -262,15 +275,15 @@ export function createExportService(
|
|
|
262
275
|
const rootPostIds = roots.map((p) => p.id);
|
|
263
276
|
|
|
264
277
|
const [
|
|
265
|
-
|
|
266
|
-
|
|
278
|
+
collectionsByRoot,
|
|
279
|
+
collectionEntriesByThread,
|
|
267
280
|
rawMediaByPost,
|
|
268
281
|
slugMap,
|
|
269
282
|
aliasMap,
|
|
270
283
|
collectionSlugMap,
|
|
271
284
|
] = await Promise.all([
|
|
272
|
-
services.collections.getCollectionsByPostIds(
|
|
273
|
-
services.collections.
|
|
285
|
+
services.collections.getCollectionsByPostIds(rootPostIds),
|
|
286
|
+
services.collections.getCollectionEntriesByThreadIds(rootPostIds),
|
|
274
287
|
services.media.getByPostIds(allPostIds),
|
|
275
288
|
services.paths.getPostSlugMap(allPostIds),
|
|
276
289
|
services.paths.getPostAliases(rootPostIds),
|
|
@@ -289,7 +302,7 @@ export function createExportService(
|
|
|
289
302
|
const collectionMetrics = buildExportedCollectionMetrics(
|
|
290
303
|
allCollections,
|
|
291
304
|
allPosts,
|
|
292
|
-
|
|
305
|
+
collectionsByRoot,
|
|
293
306
|
);
|
|
294
307
|
const exportedCollectionDirectoryItems =
|
|
295
308
|
buildExportedCollectionDirectoryItems(
|
|
@@ -325,9 +338,9 @@ export function createExportService(
|
|
|
325
338
|
const threadReplies = repliesByThread.get(root.id) ?? [];
|
|
326
339
|
const rootAliases = [...(aliasMap.get(root.id) ?? [])];
|
|
327
340
|
|
|
328
|
-
const rootCollectionEntries =
|
|
341
|
+
const rootCollectionEntries = buildExportedCollectionEntriesForThread(
|
|
329
342
|
root.id,
|
|
330
|
-
|
|
343
|
+
collectionEntriesByThread,
|
|
331
344
|
collectionSlugMap,
|
|
332
345
|
collectionTitleMap,
|
|
333
346
|
);
|
|
@@ -339,9 +352,6 @@ export function createExportService(
|
|
|
339
352
|
rootAliases,
|
|
340
353
|
rootCollectionEntries,
|
|
341
354
|
slugMap,
|
|
342
|
-
collectionEntriesByPost,
|
|
343
|
-
collectionSlugMap,
|
|
344
|
-
collectionTitleMap,
|
|
345
355
|
rawMediaByPost,
|
|
346
356
|
siteConfig,
|
|
347
357
|
deps.storage ?? null,
|
|
@@ -353,10 +363,16 @@ export function createExportService(
|
|
|
353
363
|
// Collection landing pages (`content/{slug}/_index.md`).
|
|
354
364
|
for (const collection of allCollections) {
|
|
355
365
|
const slug = collectionSlugMap.get(collection.id) ?? collection.slug;
|
|
356
|
-
const entryCount =
|
|
366
|
+
const entryCount =
|
|
367
|
+
collectionMetrics.get(collection.id)?.threadCount ?? 0;
|
|
357
368
|
exportFiles.push({
|
|
358
369
|
path: `content/${slug}/_index.md`,
|
|
359
|
-
content: await buildCollectionSection(
|
|
370
|
+
content: await buildCollectionSection(
|
|
371
|
+
collection,
|
|
372
|
+
slug,
|
|
373
|
+
entryCount,
|
|
374
|
+
siteConfig.rssFeedsEnabled,
|
|
375
|
+
),
|
|
360
376
|
});
|
|
361
377
|
}
|
|
362
378
|
|
|
@@ -375,7 +391,7 @@ export function createExportService(
|
|
|
375
391
|
});
|
|
376
392
|
exportFiles.push({
|
|
377
393
|
path: "content/archive/_index.md",
|
|
378
|
-
content: await buildArchiveSection(),
|
|
394
|
+
content: await buildArchiveSection(siteConfig.rssFeedsEnabled),
|
|
379
395
|
});
|
|
380
396
|
|
|
381
397
|
const usedSlugs = new Set<string>();
|
|
@@ -384,7 +400,7 @@ export function createExportService(
|
|
|
384
400
|
if (!usedSlugs.has("featured")) {
|
|
385
401
|
exportFiles.push({
|
|
386
402
|
path: "content/featured/_index.md",
|
|
387
|
-
content: await buildFeaturedSection(),
|
|
403
|
+
content: await buildFeaturedSection(siteConfig.rssFeedsEnabled),
|
|
388
404
|
});
|
|
389
405
|
}
|
|
390
406
|
|
|
@@ -477,6 +493,10 @@ export function createExportService(
|
|
|
477
493
|
path: "themes/jant/layouts/partials/thread-preview.html",
|
|
478
494
|
content: PARTIAL_THREAD_PREVIEW,
|
|
479
495
|
});
|
|
496
|
+
exportFiles.push({
|
|
497
|
+
path: "themes/jant/layouts/partials/featured-thread.html",
|
|
498
|
+
content: PARTIAL_FEATURED_THREAD,
|
|
499
|
+
});
|
|
480
500
|
exportFiles.push({
|
|
481
501
|
path: "themes/jant/layouts/_default/rss.xml",
|
|
482
502
|
content: LAYOUT_RSS,
|
|
@@ -625,9 +645,9 @@ async function buildSiteIconAssets(
|
|
|
625
645
|
// Thread bundle generation
|
|
626
646
|
// ---------------------------------------------------------------------------
|
|
627
647
|
|
|
628
|
-
function
|
|
629
|
-
|
|
630
|
-
|
|
648
|
+
function buildExportedCollectionEntriesForThread(
|
|
649
|
+
threadId: string,
|
|
650
|
+
collectionEntriesByThread: Map<
|
|
631
651
|
string,
|
|
632
652
|
{
|
|
633
653
|
collectionId: string;
|
|
@@ -639,7 +659,7 @@ function buildExportedCollectionEntriesForPost(
|
|
|
639
659
|
collectionSlugMap: Map<string, string>,
|
|
640
660
|
collectionTitleMap: Map<string, string>,
|
|
641
661
|
): ExportedCollectionEntry[] {
|
|
642
|
-
const entries =
|
|
662
|
+
const entries = collectionEntriesByThread.get(threadId) ?? [];
|
|
643
663
|
const resolved: ExportedCollectionEntry[] = [];
|
|
644
664
|
for (const entry of entries) {
|
|
645
665
|
const slug = collectionSlugMap.get(entry.collectionId);
|
|
@@ -805,23 +825,21 @@ async function buildThreadBundle(
|
|
|
805
825
|
rootAliases: string[],
|
|
806
826
|
rootCollectionEntries: ExportedCollectionEntry[],
|
|
807
827
|
slugMap: Map<string, string>,
|
|
808
|
-
collectionEntriesByPost: Map<
|
|
809
|
-
string,
|
|
810
|
-
{
|
|
811
|
-
collectionId: string;
|
|
812
|
-
createdAt: number;
|
|
813
|
-
position: number;
|
|
814
|
-
pinnedAt: number | null;
|
|
815
|
-
}[]
|
|
816
|
-
>,
|
|
817
|
-
collectionSlugMap: Map<string, string>,
|
|
818
|
-
collectionTitleMap: Map<string, string>,
|
|
819
828
|
mediaByPost: Map<string, Media[]>,
|
|
820
829
|
siteConfig: SiteConfig,
|
|
821
830
|
storage: StorageDriver | null,
|
|
822
831
|
bundleMedia: boolean,
|
|
823
832
|
): Promise<ExportFile[]> {
|
|
824
833
|
const files: ExportFile[] = [];
|
|
834
|
+
const featuredPosts = [root, ...threadReplies].filter(
|
|
835
|
+
(post) => post.status === "published" && post.featuredAt !== null,
|
|
836
|
+
);
|
|
837
|
+
const featuredPostIds = featuredPosts.map((post) => post.id);
|
|
838
|
+
const featuredSortAt = featuredPosts.reduce<number | null>(
|
|
839
|
+
(latest, post) =>
|
|
840
|
+
Math.max(latest ?? -1, post.publishedAt ?? post.createdAt),
|
|
841
|
+
null,
|
|
842
|
+
);
|
|
825
843
|
|
|
826
844
|
// Root aliases = historical root slugs + every reply slug (so
|
|
827
845
|
// /{reply-slug}/ gets a Hugo alias page that redirects/anchors to
|
|
@@ -876,6 +894,9 @@ async function buildThreadBundle(
|
|
|
876
894
|
rating: root.rating ?? undefined,
|
|
877
895
|
featured_at:
|
|
878
896
|
root.featuredAt !== null ? toISOString(root.featuredAt) : undefined,
|
|
897
|
+
featured_post_ids: featuredPostIds.length > 0 ? featuredPostIds : undefined,
|
|
898
|
+
featured_sort_at:
|
|
899
|
+
featuredSortAt !== null ? toISOString(featuredSortAt) : undefined,
|
|
879
900
|
pinned_at: root.pinnedAt !== null ? toISOString(root.pinnedAt) : undefined,
|
|
880
901
|
root_aliases: rootAliases.length > 0 ? rootAliases : undefined,
|
|
881
902
|
collections:
|
|
@@ -926,13 +947,6 @@ async function buildThreadBundle(
|
|
|
926
947
|
buildMediaEmission(m, siteConfig, bundleMedia),
|
|
927
948
|
);
|
|
928
949
|
const replyMediaList = replyEmissions.map((e) => e.entry);
|
|
929
|
-
const replyCollectionEntries = buildExportedCollectionEntriesForPost(
|
|
930
|
-
reply.id,
|
|
931
|
-
collectionEntriesByPost,
|
|
932
|
-
collectionSlugMap,
|
|
933
|
-
collectionTitleMap,
|
|
934
|
-
);
|
|
935
|
-
|
|
936
950
|
const replyFrontMatter: HugoFrontMatter = {
|
|
937
951
|
id: reply.id,
|
|
938
952
|
title: reply.format !== "quote" ? (reply.title ?? undefined) : undefined,
|
|
@@ -961,14 +975,11 @@ async function buildThreadBundle(
|
|
|
961
975
|
source_url: reply.format === "quote" && reply.url ? reply.url : undefined,
|
|
962
976
|
quote_text: reply.quoteText ?? undefined,
|
|
963
977
|
rating: reply.rating ?? undefined,
|
|
978
|
+
quiet_reply: reply.quietReply ? true : undefined,
|
|
964
979
|
featured_at:
|
|
965
980
|
reply.featuredAt !== null ? toISOString(reply.featuredAt) : undefined,
|
|
966
981
|
pinned_at:
|
|
967
982
|
reply.pinnedAt !== null ? toISOString(reply.pinnedAt) : undefined,
|
|
968
|
-
collections:
|
|
969
|
-
replyCollectionEntries.length > 0
|
|
970
|
-
? collectionEntriesToRefs(replyCollectionEntries)
|
|
971
|
-
: undefined,
|
|
972
983
|
media: replyMediaList.length > 0 ? replyMediaList : undefined,
|
|
973
984
|
};
|
|
974
985
|
|
|
@@ -1046,22 +1057,22 @@ async function buildCollectionsSection(): Promise<string> {
|
|
|
1046
1057
|
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
1047
1058
|
}
|
|
1048
1059
|
|
|
1049
|
-
async function buildArchiveSection(): Promise<string> {
|
|
1060
|
+
async function buildArchiveSection(rssFeedsEnabled: boolean): Promise<string> {
|
|
1050
1061
|
const frontMatter: HugoFrontMatter = {
|
|
1051
1062
|
title: "Archive",
|
|
1052
1063
|
type: "archive",
|
|
1053
1064
|
// Opt into Atom output at /archive/index.xml.
|
|
1054
|
-
outputs: ["html", "rss"],
|
|
1065
|
+
outputs: rssFeedsEnabled ? ["html", "rss"] : ["html"],
|
|
1055
1066
|
};
|
|
1056
1067
|
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
1057
1068
|
}
|
|
1058
1069
|
|
|
1059
|
-
async function buildFeaturedSection(): Promise<string> {
|
|
1070
|
+
async function buildFeaturedSection(rssFeedsEnabled: boolean): Promise<string> {
|
|
1060
1071
|
const frontMatter: HugoFrontMatter = {
|
|
1061
1072
|
title: "Featured",
|
|
1062
1073
|
type: "featured",
|
|
1063
1074
|
// Opt into Atom output at /featured/index.xml.
|
|
1064
|
-
outputs: ["html", "rss"],
|
|
1075
|
+
outputs: rssFeedsEnabled ? ["html", "rss"] : ["html"],
|
|
1065
1076
|
};
|
|
1066
1077
|
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
1067
1078
|
}
|
|
@@ -1070,6 +1081,7 @@ async function buildCollectionSection(
|
|
|
1070
1081
|
collection: Collection,
|
|
1071
1082
|
slug: string,
|
|
1072
1083
|
entryCount: number,
|
|
1084
|
+
rssFeedsEnabled: boolean,
|
|
1073
1085
|
): Promise<string> {
|
|
1074
1086
|
const frontMatter: HugoFrontMatter = {
|
|
1075
1087
|
title: collection.title,
|
|
@@ -1079,7 +1091,7 @@ async function buildCollectionSection(
|
|
|
1079
1091
|
sort_order: collection.sortOrder,
|
|
1080
1092
|
entry_count: entryCount,
|
|
1081
1093
|
// Opt into Atom output at /{slug}/index.xml.
|
|
1082
|
-
outputs: ["html", "rss"],
|
|
1094
|
+
outputs: rssFeedsEnabled ? ["html", "rss"] : ["html"],
|
|
1083
1095
|
};
|
|
1084
1096
|
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
1085
1097
|
}
|
|
@@ -1248,7 +1260,11 @@ function buildExportedCollectionDirectoryItems(
|
|
|
1248
1260
|
sequence: sequenceLabels[index] ?? "",
|
|
1249
1261
|
label: item.label,
|
|
1250
1262
|
url: item.url,
|
|
1251
|
-
descriptionHtml: description
|
|
1263
|
+
descriptionHtml: description
|
|
1264
|
+
? renderMarkdown(description, {
|
|
1265
|
+
namespace: `collection-directory-link-${sequenceLabels[index] ?? index}`,
|
|
1266
|
+
})
|
|
1267
|
+
: null,
|
|
1252
1268
|
});
|
|
1253
1269
|
return;
|
|
1254
1270
|
}
|
|
@@ -1273,12 +1289,12 @@ function buildExportedCollectionDirectoryItems(
|
|
|
1273
1289
|
slug,
|
|
1274
1290
|
title: collection.title || slug,
|
|
1275
1291
|
descriptionHtml: collectionDescription
|
|
1276
|
-
? renderMarkdown(collectionDescription)
|
|
1292
|
+
? renderMarkdown(collectionDescription, { namespace: collection.id })
|
|
1277
1293
|
: null,
|
|
1278
1294
|
entryCount:
|
|
1279
|
-
metrics?.
|
|
1280
|
-
(typeof collection.
|
|
1281
|
-
? collection.
|
|
1295
|
+
metrics?.threadCount ??
|
|
1296
|
+
(typeof collection.threadCount === "number"
|
|
1297
|
+
? collection.threadCount
|
|
1282
1298
|
: undefined),
|
|
1283
1299
|
recentActivityLabel: formatCollectionActivityLabel(activityTimestamp),
|
|
1284
1300
|
recentActivityIso: formatCollectionActivityIso(activityTimestamp),
|
|
@@ -1291,13 +1307,13 @@ function buildExportedCollectionDirectoryItems(
|
|
|
1291
1307
|
function buildExportedCollectionMetrics(
|
|
1292
1308
|
collections: readonly Collection[],
|
|
1293
1309
|
posts: readonly Post[],
|
|
1294
|
-
|
|
1310
|
+
collectionsByThread: ReadonlyMap<string, readonly Collection[]>,
|
|
1295
1311
|
): Map<string, ExportedCollectionMetrics> {
|
|
1296
1312
|
const metrics = new Map<string, ExportedCollectionMetrics>();
|
|
1297
1313
|
|
|
1298
1314
|
for (const collection of collections) {
|
|
1299
1315
|
metrics.set(collection.id, {
|
|
1300
|
-
|
|
1316
|
+
threadCount: 0,
|
|
1301
1317
|
recentActivityAt: collection.updatedAt,
|
|
1302
1318
|
});
|
|
1303
1319
|
}
|
|
@@ -1312,20 +1328,16 @@ function buildExportedCollectionMetrics(
|
|
|
1312
1328
|
continue;
|
|
1313
1329
|
}
|
|
1314
1330
|
|
|
1315
|
-
const activityAt =
|
|
1316
|
-
|
|
1317
|
-
post.publishedAt ??
|
|
1318
|
-
post.updatedAt ??
|
|
1319
|
-
post.createdAt;
|
|
1320
|
-
const postCollections = collectionsByPost.get(post.id) ?? [];
|
|
1331
|
+
const activityAt = getRootActivityAt(post);
|
|
1332
|
+
const threadCollections = collectionsByThread.get(post.id) ?? [];
|
|
1321
1333
|
|
|
1322
|
-
for (const collection of
|
|
1334
|
+
for (const collection of threadCollections) {
|
|
1323
1335
|
const current = metrics.get(collection.id);
|
|
1324
1336
|
if (!current) {
|
|
1325
1337
|
continue;
|
|
1326
1338
|
}
|
|
1327
1339
|
|
|
1328
|
-
if (current.
|
|
1340
|
+
if (current.threadCount === 0) {
|
|
1329
1341
|
current.recentActivityAt = activityAt;
|
|
1330
1342
|
} else {
|
|
1331
1343
|
current.recentActivityAt = Math.max(
|
|
@@ -1333,7 +1345,7 @@ function buildExportedCollectionMetrics(
|
|
|
1333
1345
|
activityAt,
|
|
1334
1346
|
);
|
|
1335
1347
|
}
|
|
1336
|
-
current.
|
|
1348
|
+
current.threadCount += 1;
|
|
1337
1349
|
}
|
|
1338
1350
|
}
|
|
1339
1351
|
|
|
@@ -1344,27 +1356,11 @@ function buildExportedCollectionMetrics(
|
|
|
1344
1356
|
// Nav item resolution
|
|
1345
1357
|
// ---------------------------------------------------------------------------
|
|
1346
1358
|
|
|
1347
|
-
/**
|
|
1348
|
-
* System nav items on the main site store an empty `label` in the DB and
|
|
1349
|
-
* resolve their display text at render time through i18n
|
|
1350
|
-
* (`getNavItemDisplayLabel`). The Hugo export has no i18n runtime, so fall
|
|
1351
|
-
* back to these English defaults when serializing to `data/jant.toml`.
|
|
1352
|
-
* Users can still override by setting a custom label on the nav item.
|
|
1353
|
-
*/
|
|
1354
|
-
const SYSTEM_NAV_FALLBACK_LABELS: Record<string, string> = {
|
|
1355
|
-
latest: "Latest",
|
|
1356
|
-
featured: "Featured",
|
|
1357
|
-
collections: "Collections",
|
|
1358
|
-
archive: "Archive",
|
|
1359
|
-
rss: "RSS",
|
|
1360
|
-
settings: "Settings",
|
|
1361
|
-
};
|
|
1362
|
-
|
|
1363
1359
|
function resolveNavItemLabel(item: SiteConfig["navItems"][number]): string {
|
|
1364
1360
|
if (item.label) return item.label;
|
|
1365
1361
|
if (item.systemKey) {
|
|
1366
|
-
const
|
|
1367
|
-
if (
|
|
1362
|
+
const definition = SYSTEM_NAV_KEYS[item.systemKey as SystemNavKey];
|
|
1363
|
+
if (definition) return definition.defaultLabel;
|
|
1368
1364
|
}
|
|
1369
1365
|
return item.label;
|
|
1370
1366
|
}
|
|
@@ -1447,7 +1443,7 @@ function buildHugoToml(config: SiteConfig): string {
|
|
|
1447
1443
|
// Override the built-in RSS output format to emit Atom instead of
|
|
1448
1444
|
// RSS 2.0 so the wire format mirrors the main site's `lib/feed.ts`.
|
|
1449
1445
|
"[outputs]",
|
|
1450
|
-
|
|
1446
|
+
` home = ["html"${config.rssFeedsEnabled ? ', "rss"' : ""}]`,
|
|
1451
1447
|
// Hugo's default for sections is ["html", "rss"] — without overriding
|
|
1452
1448
|
// it here every root post (which is a section) would get its own
|
|
1453
1449
|
// /{slug}/index.xml. Turn sections off by default and re-enable RSS
|
|
@@ -1472,6 +1468,8 @@ function buildHugoToml(config: SiteConfig): string {
|
|
|
1472
1468
|
"[params]",
|
|
1473
1469
|
` description = "${escapeTomlString(config.siteDescription)}"`,
|
|
1474
1470
|
` main_rss_feed = "${escapeTomlString(config.mainRssFeed)}"`,
|
|
1471
|
+
` public_api_enabled = ${config.publicApiEnabled}`,
|
|
1472
|
+
` rss_feeds_enabled = ${config.rssFeedsEnabled}`,
|
|
1475
1473
|
` show_jant_branding_on_home = ${config.showJantBrandingOnHome}`,
|
|
1476
1474
|
` show_header_avatar = ${config.showHeaderAvatar}`,
|
|
1477
1475
|
` noindex = ${config.noindex}`,
|
|
@@ -1502,7 +1500,9 @@ function buildJantDataToml(
|
|
|
1502
1500
|
iconAssets: SiteIconAssets,
|
|
1503
1501
|
directoryItems: readonly ExportedCollectionDirectoryItem[],
|
|
1504
1502
|
): string {
|
|
1505
|
-
const footerHtml = config.siteFooter
|
|
1503
|
+
const footerHtml = config.siteFooter
|
|
1504
|
+
? renderMarkdown(config.siteFooter, { namespace: "site-footer" })
|
|
1505
|
+
: "";
|
|
1506
1506
|
const parts: string[] = [
|
|
1507
1507
|
'format = "jant-site"',
|
|
1508
1508
|
"version = 1",
|
|
@@ -1511,6 +1511,8 @@ function buildJantDataToml(
|
|
|
1511
1511
|
`site_description = "${escapeTomlString(config.siteDescription)}"`,
|
|
1512
1512
|
`site_language = "${escapeTomlString(config.siteLanguage)}"`,
|
|
1513
1513
|
`main_rss_feed = "${escapeTomlString(config.mainRssFeed)}"`,
|
|
1514
|
+
`public_api_enabled = ${config.publicApiEnabled}`,
|
|
1515
|
+
`rss_feeds_enabled = ${config.rssFeedsEnabled}`,
|
|
1514
1516
|
`show_jant_branding_on_home = ${config.showJantBrandingOnHome}`,
|
|
1515
1517
|
`show_header_avatar = ${config.showHeaderAvatar}`,
|
|
1516
1518
|
`noindex = ${config.noindex}`,
|
|
@@ -1548,6 +1550,13 @@ function buildJantDataToml(
|
|
|
1548
1550
|
// `settings` is authenticated-only and has no corresponding page in the
|
|
1549
1551
|
// static Hugo site — drop it at export time so it never shows up in nav.
|
|
1550
1552
|
if (item.systemKey === "settings") continue;
|
|
1553
|
+
if (
|
|
1554
|
+
!config.rssFeedsEnabled &&
|
|
1555
|
+
item.type === "system" &&
|
|
1556
|
+
item.systemKey === "rss"
|
|
1557
|
+
) {
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1551
1560
|
parts.push("");
|
|
1552
1561
|
parts.push("[[nav]]");
|
|
1553
1562
|
parts.push(`type = "${escapeTomlString(item.type)}"`);
|
package/src/services/mcp.ts
CHANGED
|
@@ -132,12 +132,12 @@ const UploadMediaToolSchema = z.object({
|
|
|
132
132
|
posterBase64: z.string().min(1).optional(),
|
|
133
133
|
});
|
|
134
134
|
|
|
135
|
-
const
|
|
135
|
+
const AddCollectionThreadToolSchema = z.object({
|
|
136
136
|
collectionId: CollectionIdSchema,
|
|
137
|
-
|
|
137
|
+
threadId: PostIdSchema,
|
|
138
138
|
});
|
|
139
139
|
|
|
140
|
-
const
|
|
140
|
+
const RemoveCollectionThreadToolSchema = AddCollectionThreadToolSchema;
|
|
141
141
|
|
|
142
142
|
const mcpTools: McpToolDefinition[] = [
|
|
143
143
|
{
|
|
@@ -174,7 +174,8 @@ const mcpTools: McpToolDefinition[] = [
|
|
|
174
174
|
},
|
|
175
175
|
{
|
|
176
176
|
name: "jant_posts_get",
|
|
177
|
-
description:
|
|
177
|
+
description:
|
|
178
|
+
"Get one post, including attachments and shared Thread collection IDs.",
|
|
178
179
|
inputSchema: {
|
|
179
180
|
type: "object",
|
|
180
181
|
properties: {
|
|
@@ -687,55 +688,64 @@ const mcpTools: McpToolDefinition[] = [
|
|
|
687
688
|
},
|
|
688
689
|
},
|
|
689
690
|
{
|
|
690
|
-
name: "
|
|
691
|
-
description: "Add a
|
|
691
|
+
name: "jant_collections_add_thread",
|
|
692
|
+
description: "Add a thread to a collection.",
|
|
692
693
|
inputSchema: {
|
|
693
694
|
type: "object",
|
|
694
695
|
properties: {
|
|
695
696
|
collectionId: { type: "string", description: "Collection TypeID" },
|
|
696
|
-
|
|
697
|
+
threadId: {
|
|
698
|
+
type: "string",
|
|
699
|
+
description: "Root or child Post TypeID in the Thread",
|
|
700
|
+
},
|
|
697
701
|
},
|
|
698
|
-
required: ["collectionId", "
|
|
702
|
+
required: ["collectionId", "threadId"],
|
|
699
703
|
additionalProperties: false,
|
|
700
704
|
},
|
|
701
705
|
async execute(args, context) {
|
|
702
|
-
const input =
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
]);
|
|
706
|
+
const input = AddCollectionThreadToolSchema.parse(args ?? {});
|
|
707
|
+
const collection = await context.services.collections.getById(
|
|
708
|
+
input.collectionId,
|
|
709
|
+
);
|
|
707
710
|
|
|
708
711
|
if (!collection) {
|
|
709
712
|
throw new NotFoundError("Collection");
|
|
710
713
|
}
|
|
711
|
-
if (!post) {
|
|
712
|
-
throw new NotFoundError("Post");
|
|
713
|
-
}
|
|
714
714
|
|
|
715
|
-
await context.services.collections.
|
|
715
|
+
await context.services.collections.addThread(
|
|
716
716
|
input.collectionId,
|
|
717
|
-
input.
|
|
717
|
+
input.threadId,
|
|
718
718
|
);
|
|
719
719
|
return { success: true };
|
|
720
720
|
},
|
|
721
721
|
},
|
|
722
722
|
{
|
|
723
|
-
name: "
|
|
724
|
-
description: "Remove a
|
|
723
|
+
name: "jant_collections_remove_thread",
|
|
724
|
+
description: "Remove a thread from a collection.",
|
|
725
725
|
inputSchema: {
|
|
726
726
|
type: "object",
|
|
727
727
|
properties: {
|
|
728
728
|
collectionId: { type: "string", description: "Collection TypeID" },
|
|
729
|
-
|
|
729
|
+
threadId: {
|
|
730
|
+
type: "string",
|
|
731
|
+
description: "Root or child Post TypeID in the Thread",
|
|
732
|
+
},
|
|
730
733
|
},
|
|
731
|
-
required: ["collectionId", "
|
|
734
|
+
required: ["collectionId", "threadId"],
|
|
732
735
|
additionalProperties: false,
|
|
733
736
|
},
|
|
734
737
|
async execute(args, context) {
|
|
735
|
-
const input =
|
|
736
|
-
await context.services.collections.
|
|
738
|
+
const input = RemoveCollectionThreadToolSchema.parse(args ?? {});
|
|
739
|
+
const collection = await context.services.collections.getById(
|
|
740
|
+
input.collectionId,
|
|
741
|
+
);
|
|
742
|
+
if (!collection) {
|
|
743
|
+
throw new NotFoundError("Collection");
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
await context.services.collections.removeThread(
|
|
737
747
|
input.collectionId,
|
|
738
|
-
input.
|
|
748
|
+
input.threadId,
|
|
739
749
|
);
|
|
740
750
|
return { success: true };
|
|
741
751
|
},
|
|
@@ -1287,14 +1297,14 @@ async function serializePost(
|
|
|
1287
1297
|
post: Parameters<typeof toApiPost>[0],
|
|
1288
1298
|
context: McpToolContext,
|
|
1289
1299
|
) {
|
|
1290
|
-
const [mediaList,
|
|
1300
|
+
const [mediaList, threadCollections] = await Promise.all([
|
|
1291
1301
|
context.services.media.getByPostId(post.id),
|
|
1292
1302
|
context.services.collections.getCollectionsByPostId(post.id),
|
|
1293
1303
|
]);
|
|
1294
1304
|
|
|
1295
1305
|
return toApiPost(post, {
|
|
1296
1306
|
attachments: serializeAttachments(mediaList, context.appConfig),
|
|
1297
|
-
collectionIds:
|
|
1307
|
+
collectionIds: threadCollections.map((collection) => collection.id),
|
|
1298
1308
|
});
|
|
1299
1309
|
}
|
|
1300
1310
|
|
package/src/services/media.ts
CHANGED
|
@@ -1005,7 +1005,7 @@ export function createMediaService(
|
|
|
1005
1005
|
|
|
1006
1006
|
const markdown = await new Response(object.body).text();
|
|
1007
1007
|
const tiptapJson = markdownToTiptapJson(markdown);
|
|
1008
|
-
const html = renderTiptapJson(tiptapJson);
|
|
1008
|
+
const html = renderTiptapJson(tiptapJson, { namespace: record.id });
|
|
1009
1009
|
|
|
1010
1010
|
return {
|
|
1011
1011
|
id: record.id,
|