@jant/core 0.6.14 → 0.6.15
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 +81 -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-BxCOR3Uc.js +6 -0
- package/dist/{app-2i7-WQHg.js → app-C-oi_t8W.js} +7408 -6343
- package/dist/client/.vite/manifest.json +3 -3
- package/dist/client/_assets/client-B_eGKN5p.css +2 -0
- package/dist/client/_assets/{client-BvID5ucz.js → client-DxY5BFe8.js} +58 -37
- package/dist/client/_assets/client-auth-CakPESv9.js +5533 -0
- package/dist/{export-DWn149b7.js → export-CsFx6F_M.js} +1259 -125
- package/dist/{github-sync-Ckk0sJxK.js → github-sync-Cq1pzzOI.js} +1 -1
- package/dist/{github-sync-C0zyF6XS.js → github-sync-ppWXN3jb.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/node.js +5 -5
- 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__/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 +64 -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 +18 -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__/jant-collection-directory.test.ts +155 -6
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +370 -5
- package/src/client/components/__tests__/jant-compose-editor.test.ts +97 -0
- package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +104 -0
- 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-types.ts +8 -0
- package/src/client/components/jant-collection-directory.ts +165 -30
- package/src/client/components/jant-command-palette.ts +2 -1
- package/src/client/components/jant-compose-dialog.ts +85 -21
- package/src/client/components/jant-compose-editor.ts +13 -7
- package/src/client/components/jant-compose-fullscreen.ts +24 -4
- 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 +86 -74
- 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 +4 -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/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__/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/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 +8 -6
- package/src/client-auth.ts +1 -0
- package/src/client.ts +1 -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-collection-migration.test.ts +364 -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/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/_journal.json +28 -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/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/_journal.json +28 -0
- package/src/db/pg/schema.ts +28 -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 +28 -20
- package/src/i18n/__tests__/detect.test.ts +8 -2
- package/src/i18n/detect.ts +14 -5
- package/src/i18n/locales/public/en.po +133 -30
- package/src/i18n/locales/public/en.ts +1 -1
- package/src/i18n/locales/public/zh-Hans.po +128 -25
- package/src/i18n/locales/public/zh-Hans.ts +1 -1
- package/src/i18n/locales/public/zh-Hant.po +128 -25
- 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__/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 +8 -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__/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__/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/footnotes.ts +264 -0
- package/src/lib/github-sync-site-config.ts +2 -0
- package/src/lib/hugo-markdown.ts +10 -3
- package/src/lib/jant-branding.ts +1 -0
- package/src/lib/markdown-manager.ts +188 -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 +7 -1
- 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 +98 -122
- package/src/lib/timezones.ts +54 -14
- package/src/lib/tiptap-render.ts +355 -81
- package/src/lib/view.ts +30 -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__/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 +2 -2
- package/src/routes/api/public/__tests__/archive.test.ts +35 -0
- package/src/routes/api/public/__tests__/posts.test.ts +208 -0
- package/src/routes/api/public/archive.ts +4 -0
- package/src/routes/api/public/posts.ts +41 -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 +2 -1
- package/src/routes/dash/settings.tsx +45 -2
- package/src/routes/feed/__tests__/feed.test.ts +210 -6
- package/src/routes/feed/feed.ts +23 -14
- package/src/routes/pages/__tests__/archive-params.test.ts +92 -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__/preview.test.ts +174 -0
- package/src/routes/pages/__tests__/search.test.ts +69 -0
- package/src/routes/pages/archive.tsx +27 -7
- package/src/routes/pages/collection.tsx +32 -19
- package/src/routes/pages/collections.tsx +6 -1
- package/src/routes/pages/page.tsx +83 -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 +159 -44
- package/src/services/__tests__/navigation.test.ts +229 -21
- package/src/services/__tests__/post-timeline.test.ts +109 -30
- package/src/services/__tests__/post.test.ts +567 -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 +226 -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 +91 -80
- package/src/services/mcp.ts +37 -27
- package/src/services/media.ts +1 -1
- package/src/services/navigation.ts +185 -46
- package/src/services/post.ts +874 -271
- package/src/services/search.ts +8 -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 -0
- package/src/styles/site-media.css +111 -19
- package/src/styles/tokens.css +14 -9
- package/src/styles/ui.css +829 -29
- 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 +9 -8
- package/src/types/operations.ts +18 -10
- package/src/types/props.ts +8 -2
- package/src/types/raw-assets.d.ts +5 -0
- package/src/types/views.ts +16 -4
- package/src/ui/__tests__/font-themes.test.ts +42 -30
- package/src/ui/compose/ComposeDialog.tsx +92 -2
- 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/NoteCard.tsx +10 -20
- 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/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 +7 -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 +33 -0
- package/src/ui/shared/HomePageBranding.tsx +2 -2
- package/src/ui/shared/MediaGallery.tsx +1 -0
- package/src/ui/shared/PostFooter.tsx +3 -1
- 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/navigation-labels.ts +2 -4
- package/src/ui/shared/post-article-attributes.ts +46 -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
|
@@ -76,12 +76,20 @@ import PARTIAL_POST_CARD from "./export-theme/layouts/partials/post-card.html?ra
|
|
|
76
76
|
import PARTIAL_MEDIA_GALLERY from "./export-theme/layouts/partials/media-gallery.html?raw";
|
|
77
77
|
import PARTIAL_REPLY from "./export-theme/layouts/partials/reply.html?raw";
|
|
78
78
|
import PARTIAL_THREAD_PREVIEW from "./export-theme/layouts/partials/thread-preview.html?raw";
|
|
79
|
+
import PARTIAL_FEATURED_THREAD from "./export-theme/layouts/partials/featured-thread.html?raw";
|
|
79
80
|
import LAYOUT_RSS from "./export-theme/layouts/_default/rss.xml?raw";
|
|
80
81
|
import PARTIAL_FEED_POST_CONTENT from "./export-theme/layouts/partials/feed-post-content.xml?raw";
|
|
81
82
|
|
|
82
83
|
import type { StorageDriver } from "../lib/storage.js";
|
|
83
84
|
import { base64ToUint8Array } from "../lib/favicon.js";
|
|
84
|
-
import
|
|
85
|
+
import {
|
|
86
|
+
SYSTEM_NAV_KEYS,
|
|
87
|
+
type Collection,
|
|
88
|
+
type Media,
|
|
89
|
+
type NavItem,
|
|
90
|
+
type Post,
|
|
91
|
+
type SystemNavKey,
|
|
92
|
+
} from "../types.js";
|
|
85
93
|
|
|
86
94
|
/** A file entry in the exported Hugo site. */
|
|
87
95
|
export interface ExportFile {
|
|
@@ -102,6 +110,10 @@ export interface SiteConfig {
|
|
|
102
110
|
siteDescription: string;
|
|
103
111
|
siteLanguage: string;
|
|
104
112
|
showJantBrandingOnHome: boolean;
|
|
113
|
+
/** Whether anonymous JSON reads are enabled on the source Jant site. */
|
|
114
|
+
publicApiEnabled: boolean;
|
|
115
|
+
/** Whether the exported Hugo site should emit Atom feeds. */
|
|
116
|
+
rssFeedsEnabled: boolean;
|
|
105
117
|
/** "latest" or "featured" — drives the default RSS nav link in the exported site. */
|
|
106
118
|
mainRssFeed: string;
|
|
107
119
|
siteFooter: string;
|
|
@@ -171,7 +183,7 @@ interface ExportCollectionDirectorySourceItem {
|
|
|
171
183
|
slug: string;
|
|
172
184
|
title: string;
|
|
173
185
|
description?: string | null;
|
|
174
|
-
|
|
186
|
+
threadCount?: number;
|
|
175
187
|
recentActivityAt?: number;
|
|
176
188
|
};
|
|
177
189
|
}
|
|
@@ -184,14 +196,14 @@ interface SiteIconAssets {
|
|
|
184
196
|
}
|
|
185
197
|
|
|
186
198
|
interface ExportedCollectionMetrics {
|
|
187
|
-
|
|
199
|
+
threadCount: number;
|
|
188
200
|
recentActivityAt: number;
|
|
189
201
|
}
|
|
190
202
|
|
|
191
203
|
/**
|
|
192
204
|
* A single `collections:` front-matter entry, already resolved to its
|
|
193
205
|
* Hugo-visible slug. Assembled from
|
|
194
|
-
* `collectionService.
|
|
206
|
+
* `collectionService.getCollectionEntriesByThreadIds` + `collectionSlugMap`.
|
|
195
207
|
*/
|
|
196
208
|
interface ExportedCollectionEntry {
|
|
197
209
|
slug: string;
|
|
@@ -262,15 +274,15 @@ export function createExportService(
|
|
|
262
274
|
const rootPostIds = roots.map((p) => p.id);
|
|
263
275
|
|
|
264
276
|
const [
|
|
265
|
-
|
|
266
|
-
|
|
277
|
+
collectionsByRoot,
|
|
278
|
+
collectionEntriesByThread,
|
|
267
279
|
rawMediaByPost,
|
|
268
280
|
slugMap,
|
|
269
281
|
aliasMap,
|
|
270
282
|
collectionSlugMap,
|
|
271
283
|
] = await Promise.all([
|
|
272
|
-
services.collections.getCollectionsByPostIds(
|
|
273
|
-
services.collections.
|
|
284
|
+
services.collections.getCollectionsByPostIds(rootPostIds),
|
|
285
|
+
services.collections.getCollectionEntriesByThreadIds(rootPostIds),
|
|
274
286
|
services.media.getByPostIds(allPostIds),
|
|
275
287
|
services.paths.getPostSlugMap(allPostIds),
|
|
276
288
|
services.paths.getPostAliases(rootPostIds),
|
|
@@ -289,7 +301,7 @@ export function createExportService(
|
|
|
289
301
|
const collectionMetrics = buildExportedCollectionMetrics(
|
|
290
302
|
allCollections,
|
|
291
303
|
allPosts,
|
|
292
|
-
|
|
304
|
+
collectionsByRoot,
|
|
293
305
|
);
|
|
294
306
|
const exportedCollectionDirectoryItems =
|
|
295
307
|
buildExportedCollectionDirectoryItems(
|
|
@@ -325,9 +337,9 @@ export function createExportService(
|
|
|
325
337
|
const threadReplies = repliesByThread.get(root.id) ?? [];
|
|
326
338
|
const rootAliases = [...(aliasMap.get(root.id) ?? [])];
|
|
327
339
|
|
|
328
|
-
const rootCollectionEntries =
|
|
340
|
+
const rootCollectionEntries = buildExportedCollectionEntriesForThread(
|
|
329
341
|
root.id,
|
|
330
|
-
|
|
342
|
+
collectionEntriesByThread,
|
|
331
343
|
collectionSlugMap,
|
|
332
344
|
collectionTitleMap,
|
|
333
345
|
);
|
|
@@ -339,9 +351,6 @@ export function createExportService(
|
|
|
339
351
|
rootAliases,
|
|
340
352
|
rootCollectionEntries,
|
|
341
353
|
slugMap,
|
|
342
|
-
collectionEntriesByPost,
|
|
343
|
-
collectionSlugMap,
|
|
344
|
-
collectionTitleMap,
|
|
345
354
|
rawMediaByPost,
|
|
346
355
|
siteConfig,
|
|
347
356
|
deps.storage ?? null,
|
|
@@ -353,10 +362,16 @@ export function createExportService(
|
|
|
353
362
|
// Collection landing pages (`content/{slug}/_index.md`).
|
|
354
363
|
for (const collection of allCollections) {
|
|
355
364
|
const slug = collectionSlugMap.get(collection.id) ?? collection.slug;
|
|
356
|
-
const entryCount =
|
|
365
|
+
const entryCount =
|
|
366
|
+
collectionMetrics.get(collection.id)?.threadCount ?? 0;
|
|
357
367
|
exportFiles.push({
|
|
358
368
|
path: `content/${slug}/_index.md`,
|
|
359
|
-
content: await buildCollectionSection(
|
|
369
|
+
content: await buildCollectionSection(
|
|
370
|
+
collection,
|
|
371
|
+
slug,
|
|
372
|
+
entryCount,
|
|
373
|
+
siteConfig.rssFeedsEnabled,
|
|
374
|
+
),
|
|
360
375
|
});
|
|
361
376
|
}
|
|
362
377
|
|
|
@@ -375,7 +390,7 @@ export function createExportService(
|
|
|
375
390
|
});
|
|
376
391
|
exportFiles.push({
|
|
377
392
|
path: "content/archive/_index.md",
|
|
378
|
-
content: await buildArchiveSection(),
|
|
393
|
+
content: await buildArchiveSection(siteConfig.rssFeedsEnabled),
|
|
379
394
|
});
|
|
380
395
|
|
|
381
396
|
const usedSlugs = new Set<string>();
|
|
@@ -384,7 +399,7 @@ export function createExportService(
|
|
|
384
399
|
if (!usedSlugs.has("featured")) {
|
|
385
400
|
exportFiles.push({
|
|
386
401
|
path: "content/featured/_index.md",
|
|
387
|
-
content: await buildFeaturedSection(),
|
|
402
|
+
content: await buildFeaturedSection(siteConfig.rssFeedsEnabled),
|
|
388
403
|
});
|
|
389
404
|
}
|
|
390
405
|
|
|
@@ -477,6 +492,10 @@ export function createExportService(
|
|
|
477
492
|
path: "themes/jant/layouts/partials/thread-preview.html",
|
|
478
493
|
content: PARTIAL_THREAD_PREVIEW,
|
|
479
494
|
});
|
|
495
|
+
exportFiles.push({
|
|
496
|
+
path: "themes/jant/layouts/partials/featured-thread.html",
|
|
497
|
+
content: PARTIAL_FEATURED_THREAD,
|
|
498
|
+
});
|
|
480
499
|
exportFiles.push({
|
|
481
500
|
path: "themes/jant/layouts/_default/rss.xml",
|
|
482
501
|
content: LAYOUT_RSS,
|
|
@@ -625,9 +644,9 @@ async function buildSiteIconAssets(
|
|
|
625
644
|
// Thread bundle generation
|
|
626
645
|
// ---------------------------------------------------------------------------
|
|
627
646
|
|
|
628
|
-
function
|
|
629
|
-
|
|
630
|
-
|
|
647
|
+
function buildExportedCollectionEntriesForThread(
|
|
648
|
+
threadId: string,
|
|
649
|
+
collectionEntriesByThread: Map<
|
|
631
650
|
string,
|
|
632
651
|
{
|
|
633
652
|
collectionId: string;
|
|
@@ -639,7 +658,7 @@ function buildExportedCollectionEntriesForPost(
|
|
|
639
658
|
collectionSlugMap: Map<string, string>,
|
|
640
659
|
collectionTitleMap: Map<string, string>,
|
|
641
660
|
): ExportedCollectionEntry[] {
|
|
642
|
-
const entries =
|
|
661
|
+
const entries = collectionEntriesByThread.get(threadId) ?? [];
|
|
643
662
|
const resolved: ExportedCollectionEntry[] = [];
|
|
644
663
|
for (const entry of entries) {
|
|
645
664
|
const slug = collectionSlugMap.get(entry.collectionId);
|
|
@@ -805,23 +824,21 @@ async function buildThreadBundle(
|
|
|
805
824
|
rootAliases: string[],
|
|
806
825
|
rootCollectionEntries: ExportedCollectionEntry[],
|
|
807
826
|
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
827
|
mediaByPost: Map<string, Media[]>,
|
|
820
828
|
siteConfig: SiteConfig,
|
|
821
829
|
storage: StorageDriver | null,
|
|
822
830
|
bundleMedia: boolean,
|
|
823
831
|
): Promise<ExportFile[]> {
|
|
824
832
|
const files: ExportFile[] = [];
|
|
833
|
+
const featuredPosts = [root, ...threadReplies].filter(
|
|
834
|
+
(post) => post.status === "published" && post.featuredAt !== null,
|
|
835
|
+
);
|
|
836
|
+
const featuredPostIds = featuredPosts.map((post) => post.id);
|
|
837
|
+
const featuredSortAt = featuredPosts.reduce<number | null>(
|
|
838
|
+
(latest, post) =>
|
|
839
|
+
Math.max(latest ?? -1, post.publishedAt ?? post.createdAt),
|
|
840
|
+
null,
|
|
841
|
+
);
|
|
825
842
|
|
|
826
843
|
// Root aliases = historical root slugs + every reply slug (so
|
|
827
844
|
// /{reply-slug}/ gets a Hugo alias page that redirects/anchors to
|
|
@@ -876,6 +893,9 @@ async function buildThreadBundle(
|
|
|
876
893
|
rating: root.rating ?? undefined,
|
|
877
894
|
featured_at:
|
|
878
895
|
root.featuredAt !== null ? toISOString(root.featuredAt) : undefined,
|
|
896
|
+
featured_post_ids: featuredPostIds.length > 0 ? featuredPostIds : undefined,
|
|
897
|
+
featured_sort_at:
|
|
898
|
+
featuredSortAt !== null ? toISOString(featuredSortAt) : undefined,
|
|
879
899
|
pinned_at: root.pinnedAt !== null ? toISOString(root.pinnedAt) : undefined,
|
|
880
900
|
root_aliases: rootAliases.length > 0 ? rootAliases : undefined,
|
|
881
901
|
collections:
|
|
@@ -926,13 +946,6 @@ async function buildThreadBundle(
|
|
|
926
946
|
buildMediaEmission(m, siteConfig, bundleMedia),
|
|
927
947
|
);
|
|
928
948
|
const replyMediaList = replyEmissions.map((e) => e.entry);
|
|
929
|
-
const replyCollectionEntries = buildExportedCollectionEntriesForPost(
|
|
930
|
-
reply.id,
|
|
931
|
-
collectionEntriesByPost,
|
|
932
|
-
collectionSlugMap,
|
|
933
|
-
collectionTitleMap,
|
|
934
|
-
);
|
|
935
|
-
|
|
936
949
|
const replyFrontMatter: HugoFrontMatter = {
|
|
937
950
|
id: reply.id,
|
|
938
951
|
title: reply.format !== "quote" ? (reply.title ?? undefined) : undefined,
|
|
@@ -965,10 +978,6 @@ async function buildThreadBundle(
|
|
|
965
978
|
reply.featuredAt !== null ? toISOString(reply.featuredAt) : undefined,
|
|
966
979
|
pinned_at:
|
|
967
980
|
reply.pinnedAt !== null ? toISOString(reply.pinnedAt) : undefined,
|
|
968
|
-
collections:
|
|
969
|
-
replyCollectionEntries.length > 0
|
|
970
|
-
? collectionEntriesToRefs(replyCollectionEntries)
|
|
971
|
-
: undefined,
|
|
972
981
|
media: replyMediaList.length > 0 ? replyMediaList : undefined,
|
|
973
982
|
};
|
|
974
983
|
|
|
@@ -1046,22 +1055,22 @@ async function buildCollectionsSection(): Promise<string> {
|
|
|
1046
1055
|
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
1047
1056
|
}
|
|
1048
1057
|
|
|
1049
|
-
async function buildArchiveSection(): Promise<string> {
|
|
1058
|
+
async function buildArchiveSection(rssFeedsEnabled: boolean): Promise<string> {
|
|
1050
1059
|
const frontMatter: HugoFrontMatter = {
|
|
1051
1060
|
title: "Archive",
|
|
1052
1061
|
type: "archive",
|
|
1053
1062
|
// Opt into Atom output at /archive/index.xml.
|
|
1054
|
-
outputs: ["html", "rss"],
|
|
1063
|
+
outputs: rssFeedsEnabled ? ["html", "rss"] : ["html"],
|
|
1055
1064
|
};
|
|
1056
1065
|
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
1057
1066
|
}
|
|
1058
1067
|
|
|
1059
|
-
async function buildFeaturedSection(): Promise<string> {
|
|
1068
|
+
async function buildFeaturedSection(rssFeedsEnabled: boolean): Promise<string> {
|
|
1060
1069
|
const frontMatter: HugoFrontMatter = {
|
|
1061
1070
|
title: "Featured",
|
|
1062
1071
|
type: "featured",
|
|
1063
1072
|
// Opt into Atom output at /featured/index.xml.
|
|
1064
|
-
outputs: ["html", "rss"],
|
|
1073
|
+
outputs: rssFeedsEnabled ? ["html", "rss"] : ["html"],
|
|
1065
1074
|
};
|
|
1066
1075
|
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
1067
1076
|
}
|
|
@@ -1070,6 +1079,7 @@ async function buildCollectionSection(
|
|
|
1070
1079
|
collection: Collection,
|
|
1071
1080
|
slug: string,
|
|
1072
1081
|
entryCount: number,
|
|
1082
|
+
rssFeedsEnabled: boolean,
|
|
1073
1083
|
): Promise<string> {
|
|
1074
1084
|
const frontMatter: HugoFrontMatter = {
|
|
1075
1085
|
title: collection.title,
|
|
@@ -1079,7 +1089,7 @@ async function buildCollectionSection(
|
|
|
1079
1089
|
sort_order: collection.sortOrder,
|
|
1080
1090
|
entry_count: entryCount,
|
|
1081
1091
|
// Opt into Atom output at /{slug}/index.xml.
|
|
1082
|
-
outputs: ["html", "rss"],
|
|
1092
|
+
outputs: rssFeedsEnabled ? ["html", "rss"] : ["html"],
|
|
1083
1093
|
};
|
|
1084
1094
|
return `${await formatFrontMatter(frontMatter)}\n`;
|
|
1085
1095
|
}
|
|
@@ -1248,7 +1258,11 @@ function buildExportedCollectionDirectoryItems(
|
|
|
1248
1258
|
sequence: sequenceLabels[index] ?? "",
|
|
1249
1259
|
label: item.label,
|
|
1250
1260
|
url: item.url,
|
|
1251
|
-
descriptionHtml: description
|
|
1261
|
+
descriptionHtml: description
|
|
1262
|
+
? renderMarkdown(description, {
|
|
1263
|
+
namespace: `collection-directory-link-${sequenceLabels[index] ?? index}`,
|
|
1264
|
+
})
|
|
1265
|
+
: null,
|
|
1252
1266
|
});
|
|
1253
1267
|
return;
|
|
1254
1268
|
}
|
|
@@ -1273,12 +1287,12 @@ function buildExportedCollectionDirectoryItems(
|
|
|
1273
1287
|
slug,
|
|
1274
1288
|
title: collection.title || slug,
|
|
1275
1289
|
descriptionHtml: collectionDescription
|
|
1276
|
-
? renderMarkdown(collectionDescription)
|
|
1290
|
+
? renderMarkdown(collectionDescription, { namespace: collection.id })
|
|
1277
1291
|
: null,
|
|
1278
1292
|
entryCount:
|
|
1279
|
-
metrics?.
|
|
1280
|
-
(typeof collection.
|
|
1281
|
-
? collection.
|
|
1293
|
+
metrics?.threadCount ??
|
|
1294
|
+
(typeof collection.threadCount === "number"
|
|
1295
|
+
? collection.threadCount
|
|
1282
1296
|
: undefined),
|
|
1283
1297
|
recentActivityLabel: formatCollectionActivityLabel(activityTimestamp),
|
|
1284
1298
|
recentActivityIso: formatCollectionActivityIso(activityTimestamp),
|
|
@@ -1291,13 +1305,13 @@ function buildExportedCollectionDirectoryItems(
|
|
|
1291
1305
|
function buildExportedCollectionMetrics(
|
|
1292
1306
|
collections: readonly Collection[],
|
|
1293
1307
|
posts: readonly Post[],
|
|
1294
|
-
|
|
1308
|
+
collectionsByThread: ReadonlyMap<string, readonly Collection[]>,
|
|
1295
1309
|
): Map<string, ExportedCollectionMetrics> {
|
|
1296
1310
|
const metrics = new Map<string, ExportedCollectionMetrics>();
|
|
1297
1311
|
|
|
1298
1312
|
for (const collection of collections) {
|
|
1299
1313
|
metrics.set(collection.id, {
|
|
1300
|
-
|
|
1314
|
+
threadCount: 0,
|
|
1301
1315
|
recentActivityAt: collection.updatedAt,
|
|
1302
1316
|
});
|
|
1303
1317
|
}
|
|
@@ -1317,15 +1331,15 @@ function buildExportedCollectionMetrics(
|
|
|
1317
1331
|
post.publishedAt ??
|
|
1318
1332
|
post.updatedAt ??
|
|
1319
1333
|
post.createdAt;
|
|
1320
|
-
const
|
|
1334
|
+
const threadCollections = collectionsByThread.get(post.id) ?? [];
|
|
1321
1335
|
|
|
1322
|
-
for (const collection of
|
|
1336
|
+
for (const collection of threadCollections) {
|
|
1323
1337
|
const current = metrics.get(collection.id);
|
|
1324
1338
|
if (!current) {
|
|
1325
1339
|
continue;
|
|
1326
1340
|
}
|
|
1327
1341
|
|
|
1328
|
-
if (current.
|
|
1342
|
+
if (current.threadCount === 0) {
|
|
1329
1343
|
current.recentActivityAt = activityAt;
|
|
1330
1344
|
} else {
|
|
1331
1345
|
current.recentActivityAt = Math.max(
|
|
@@ -1333,7 +1347,7 @@ function buildExportedCollectionMetrics(
|
|
|
1333
1347
|
activityAt,
|
|
1334
1348
|
);
|
|
1335
1349
|
}
|
|
1336
|
-
current.
|
|
1350
|
+
current.threadCount += 1;
|
|
1337
1351
|
}
|
|
1338
1352
|
}
|
|
1339
1353
|
|
|
@@ -1344,27 +1358,11 @@ function buildExportedCollectionMetrics(
|
|
|
1344
1358
|
// Nav item resolution
|
|
1345
1359
|
// ---------------------------------------------------------------------------
|
|
1346
1360
|
|
|
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
1361
|
function resolveNavItemLabel(item: SiteConfig["navItems"][number]): string {
|
|
1364
1362
|
if (item.label) return item.label;
|
|
1365
1363
|
if (item.systemKey) {
|
|
1366
|
-
const
|
|
1367
|
-
if (
|
|
1364
|
+
const definition = SYSTEM_NAV_KEYS[item.systemKey as SystemNavKey];
|
|
1365
|
+
if (definition) return definition.defaultLabel;
|
|
1368
1366
|
}
|
|
1369
1367
|
return item.label;
|
|
1370
1368
|
}
|
|
@@ -1447,7 +1445,7 @@ function buildHugoToml(config: SiteConfig): string {
|
|
|
1447
1445
|
// Override the built-in RSS output format to emit Atom instead of
|
|
1448
1446
|
// RSS 2.0 so the wire format mirrors the main site's `lib/feed.ts`.
|
|
1449
1447
|
"[outputs]",
|
|
1450
|
-
|
|
1448
|
+
` home = ["html"${config.rssFeedsEnabled ? ', "rss"' : ""}]`,
|
|
1451
1449
|
// Hugo's default for sections is ["html", "rss"] — without overriding
|
|
1452
1450
|
// it here every root post (which is a section) would get its own
|
|
1453
1451
|
// /{slug}/index.xml. Turn sections off by default and re-enable RSS
|
|
@@ -1472,6 +1470,8 @@ function buildHugoToml(config: SiteConfig): string {
|
|
|
1472
1470
|
"[params]",
|
|
1473
1471
|
` description = "${escapeTomlString(config.siteDescription)}"`,
|
|
1474
1472
|
` main_rss_feed = "${escapeTomlString(config.mainRssFeed)}"`,
|
|
1473
|
+
` public_api_enabled = ${config.publicApiEnabled}`,
|
|
1474
|
+
` rss_feeds_enabled = ${config.rssFeedsEnabled}`,
|
|
1475
1475
|
` show_jant_branding_on_home = ${config.showJantBrandingOnHome}`,
|
|
1476
1476
|
` show_header_avatar = ${config.showHeaderAvatar}`,
|
|
1477
1477
|
` noindex = ${config.noindex}`,
|
|
@@ -1502,7 +1502,9 @@ function buildJantDataToml(
|
|
|
1502
1502
|
iconAssets: SiteIconAssets,
|
|
1503
1503
|
directoryItems: readonly ExportedCollectionDirectoryItem[],
|
|
1504
1504
|
): string {
|
|
1505
|
-
const footerHtml = config.siteFooter
|
|
1505
|
+
const footerHtml = config.siteFooter
|
|
1506
|
+
? renderMarkdown(config.siteFooter, { namespace: "site-footer" })
|
|
1507
|
+
: "";
|
|
1506
1508
|
const parts: string[] = [
|
|
1507
1509
|
'format = "jant-site"',
|
|
1508
1510
|
"version = 1",
|
|
@@ -1511,6 +1513,8 @@ function buildJantDataToml(
|
|
|
1511
1513
|
`site_description = "${escapeTomlString(config.siteDescription)}"`,
|
|
1512
1514
|
`site_language = "${escapeTomlString(config.siteLanguage)}"`,
|
|
1513
1515
|
`main_rss_feed = "${escapeTomlString(config.mainRssFeed)}"`,
|
|
1516
|
+
`public_api_enabled = ${config.publicApiEnabled}`,
|
|
1517
|
+
`rss_feeds_enabled = ${config.rssFeedsEnabled}`,
|
|
1514
1518
|
`show_jant_branding_on_home = ${config.showJantBrandingOnHome}`,
|
|
1515
1519
|
`show_header_avatar = ${config.showHeaderAvatar}`,
|
|
1516
1520
|
`noindex = ${config.noindex}`,
|
|
@@ -1548,6 +1552,13 @@ function buildJantDataToml(
|
|
|
1548
1552
|
// `settings` is authenticated-only and has no corresponding page in the
|
|
1549
1553
|
// static Hugo site — drop it at export time so it never shows up in nav.
|
|
1550
1554
|
if (item.systemKey === "settings") continue;
|
|
1555
|
+
if (
|
|
1556
|
+
!config.rssFeedsEnabled &&
|
|
1557
|
+
item.type === "system" &&
|
|
1558
|
+
item.systemKey === "rss"
|
|
1559
|
+
) {
|
|
1560
|
+
continue;
|
|
1561
|
+
}
|
|
1551
1562
|
parts.push("");
|
|
1552
1563
|
parts.push("[[nav]]");
|
|
1553
1564
|
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,
|