@sonordev/site-kit 8.1.0 → 8.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +13 -1
- package/CHANGELOG.md +11 -0
- package/README.md +9 -5
- package/agent-manifest.json +47 -8
- package/dist/{AnalyticsProvider-UWTCE5UO.js → AnalyticsProvider-QXO33JUF.js} +6 -6
- package/dist/{ArticleFaqSection-3Q26DP7P.js → ArticleFaqSection-DEWE5B4Q.js} +1 -1
- package/dist/{ArticleViewTracker-MYD6SGNH.js → ArticleViewTracker-KN6ENIRS.js} +17 -6
- package/dist/{BlocksPopup-OY5G3EFH.js → BlocksPopup-3RPN6YVJ.js} +6 -6
- package/dist/{ChatWidget-BTR5VERC.js → ChatWidget-D2X445PO.js} +6 -6
- package/dist/{FileField-DEHVYXAO.js → FileField-UNGSNGJ5.js} +4 -4
- package/dist/{FormSpotlight-VGC4CZL4.js → FormSpotlight-JFH2ENKQ.js} +1 -1
- package/dist/{FormStage-CJUSCSAL.js → FormStage-RC3WHHXP.js} +1 -1
- package/dist/{ManagedForm-JCI2KPMO.js → ManagedForm-FOQZ2IYG.js} +7 -7
- package/dist/{ManagedNewsletterForm-DTE4KKXV.js → ManagedNewsletterForm-NFNF33V3.js} +16 -7
- package/dist/{SignalCore-LGCBQDEH.js → SignalCore-5KABGX5C.js} +4 -4
- package/dist/SiteChat-JQ46ZIJ4.js +6 -0
- package/dist/SiteDesignReporter-N7L6DR4J.js +12 -0
- package/dist/{SitePopups-T6YPHAVY.js → SitePopups-WZBKX5Q3.js} +6 -6
- package/dist/SitemapSync-GY6UADRI.js +9 -0
- package/dist/_client/booking-widget.js +6 -6
- package/dist/_client/testimonial-section.js +3 -3
- package/dist/affiliates/index.js +4 -4
- package/dist/analytics/index.js +6 -6
- package/dist/{article-styles-GGZOQG5X.js → article-styles-7QONZJR5.js} +3 -3
- package/dist/article-tables-6XHVULPU.js +2 -0
- package/dist/articles/Article.d.ts +2 -89
- package/dist/articles/ArticleFAQ.d.ts +2 -41
- package/dist/articles/ArticleFaqSection.d.ts +2 -2
- package/dist/articles/ArticleList.d.ts +2 -79
- package/dist/articles/ArticleViewTracker.d.ts +2 -15
- package/dist/articles/AuthorCard.d.ts +2 -10
- package/dist/articles/AuthorPage.d.ts +2 -8
- package/dist/articles/ClusterLandingPage.d.ts +2 -40
- package/dist/articles/ClusterNavigation.d.ts +2 -37
- package/dist/articles/ManagedNewsletterForm.d.ts +3 -12
- package/dist/articles/NewsletterShell.d.ts +2 -17
- package/dist/articles/NewsletterWidget.d.ts +2 -30
- package/dist/articles/PublicationLayout.d.ts +2 -67
- package/dist/articles/PublicationSidebar.d.ts +2 -36
- package/dist/articles/RelatedPosts.d.ts +2 -22
- package/dist/articles/ServiceCallout.d.ts +2 -56
- package/dist/articles/TableOfContents.d.ts +2 -6
- package/dist/articles/article-photos.d.ts +2 -9
- package/dist/articles/article-styles.d.ts +2 -21
- package/dist/articles/article-tables.d.ts +2 -12
- package/dist/articles/artwork.d.ts +2 -13
- package/dist/articles/author-schema.d.ts +2 -72
- package/dist/articles/author-social.d.ts +2 -14
- package/dist/articles/excerpt.d.ts +2 -17
- package/dist/articles/index.d.ts +2 -79
- package/dist/articles/index.js +16 -11
- package/dist/articles/news-sitemap.d.ts +2 -46
- package/dist/articles/processArticleHtml.d.ts +2 -34
- package/dist/articles/public-reads.d.ts +2 -26
- package/dist/articles/reading-time.d.ts +2 -9
- package/dist/articles/routes.d.ts +2 -52
- package/dist/articles/server-core.d.ts +2 -461
- package/dist/articles/server-ui.d.ts +2 -30
- package/dist/articles/server-ui.js +26 -17
- package/dist/articles/server.d.ts +2 -461
- package/dist/articles/server.js +14 -5
- package/dist/articles/types.d.ts +2 -315
- package/dist/articles/widget-styles.d.ts +2 -8
- package/dist/chat/index.js +8 -8
- package/dist/{chunk-AIDIEVPZ.js → chunk-2JMCBJM5.js} +1 -1
- package/dist/chunk-34ZYJTQR.js +1 -0
- package/dist/{chunk-ADI6IYFH.js → chunk-3OY52UFM.js} +1 -1
- package/dist/{chunk-WMVK77ND.js → chunk-4K735O3J.js} +1 -1
- package/dist/{chunk-55GHBRCN.js → chunk-4PVMUPC6.js} +2 -2
- package/dist/{chunk-L5UOHS6E.js → chunk-5D7ZISUP.js} +1 -1
- package/dist/{chunk-JT52KYHQ.js → chunk-5RYSTKVU.js} +1 -1
- package/dist/{chunk-6CJGZFOU.js → chunk-A4MPHIVP.js} +1 -1
- package/dist/chunk-B7T2SFUF.js +1 -0
- package/dist/{chunk-FV2VD7WM.js → chunk-BC7JMY4Z.js} +3 -3
- package/dist/{chunk-255NCCPB.js → chunk-BOCYBDKV.js} +1 -1
- package/dist/{chunk-NFRJTOTB.js → chunk-BSQP6PRA.js} +1 -1
- package/dist/{chunk-GKGSXUVQ.js → chunk-BVB5FBAA.js} +1 -1
- package/dist/{chunk-NWVB75ER.js → chunk-C2UROFGZ.js} +1 -1
- package/dist/{chunk-F2VYN3QL.js → chunk-C4AFNZT2.js} +1 -1
- package/dist/{chunk-GC2QWXJ3.js → chunk-D6ZIQMHB.js} +3 -3
- package/dist/{chunk-GYF2ULVO.js → chunk-DJAYFHVF.js} +1 -1
- package/dist/{chunk-XUCU3LX4.js → chunk-DPSNMOMZ.js} +1 -1
- package/dist/{chunk-A3EU6HB5.js → chunk-E3UR37RX.js} +2 -2
- package/dist/{chunk-Y3INC4QJ.js → chunk-E4PTMORJ.js} +1 -1
- package/dist/{chunk-TAO6BVA2.js → chunk-EA6FZMYA.js} +2 -2
- package/dist/{chunk-V56RZH7J.js → chunk-FHUTOXS7.js} +1 -1
- package/dist/{chunk-LLW5HKEE.js → chunk-GKJVSZ7H.js} +3 -3
- package/dist/chunk-H72ZFO3F.js +1 -0
- package/dist/{chunk-CSTC6HZO.js → chunk-HND6BDXC.js} +3 -3
- package/dist/chunk-IEGDPRQB.js +14 -0
- package/dist/{chunk-CAH4Y4PY.js → chunk-JO76F5WP.js} +1 -1
- package/dist/{chunk-4Z3DI2EL.js → chunk-KJVUOJMW.js} +58 -58
- package/dist/{chunk-SC4MZGWZ.js → chunk-LBDWAXAO.js} +1 -1
- package/dist/{chunk-RSBMQFZW.js → chunk-LPKKSYKX.js} +3 -3
- package/dist/{chunk-2UO4HPGE.js → chunk-M5SF7NCC.js} +73 -51
- package/dist/{chunk-S6IGQYHW.js → chunk-MQ4REMTH.js} +2 -2
- package/dist/{chunk-O4DETQJI.js → chunk-NA4Z4CQG.js} +1 -1
- package/dist/{chunk-WPOFTCKL.js → chunk-OIWFXUVD.js} +2 -2
- package/dist/{chunk-G2HCCWZQ.js → chunk-QBCWVT2S.js} +2 -2
- package/dist/{chunk-DZV3KPVF.js → chunk-QCZHYO5M.js} +1 -1
- package/dist/{chunk-DOMBP3FW.js → chunk-QDILCQBL.js} +9 -9
- package/dist/{chunk-IAYGTQJD.js → chunk-SCTVADDL.js} +2 -2
- package/dist/{chunk-QXMIKDOL.js → chunk-SNRE3ZVZ.js} +3 -3
- package/dist/{chunk-DSNCU2E3.js → chunk-TT4JZDKM.js} +3 -3
- package/dist/{chunk-6VHDCJIZ.js → chunk-U7Y3QG3A.js} +1 -1
- package/dist/{chunk-3G7YABRA.js → chunk-W7OPEMGJ.js} +4 -1
- package/dist/{chunk-2JEUDTL6.js → chunk-WBUG7XRL.js} +2 -2
- package/dist/{chunk-47VB5INH.js → chunk-WSOZ3FQN.js} +1 -1
- package/dist/chunk-X4IJS7LV.js +319 -0
- package/dist/{chunk-JIH32RH2.js → chunk-XA7Q2VQA.js} +1 -1
- package/dist/{chunk-5L467Z5Q.js → chunk-XLAXYPTK.js} +3 -3
- package/dist/client/index.js +4 -16
- package/dist/contracts/editorial-media.d.ts +39 -0
- package/dist/contracts/entries.d.ts +1 -1
- package/dist/fleet/index.js +6 -6
- package/dist/forms/index.js +9 -325
- package/dist/forms/server.js +2 -2
- package/dist/images/index.js +4 -4
- package/dist/index.js +1 -1
- package/dist/layout/client.js +9 -9
- package/dist/layout/index.js +10 -10
- package/dist/llms/index.js +8 -8
- package/dist/maps/index.js +4 -4
- package/dist/mcp/sonor.d.ts +42 -3
- package/dist/mcp/sonor.js +25 -20
- package/dist/mcp/transport.d.ts +1 -1
- package/dist/press-kit/articles/Article.d.ts +89 -0
- package/dist/press-kit/articles/ArticleFAQ.d.ts +41 -0
- package/dist/press-kit/articles/ArticleFaqSection.d.ts +2 -0
- package/dist/press-kit/articles/ArticleList.d.ts +79 -0
- package/dist/press-kit/articles/ArticleViewTracker.d.ts +15 -0
- package/dist/press-kit/articles/AuthorCard.d.ts +10 -0
- package/dist/press-kit/articles/AuthorPage.d.ts +8 -0
- package/dist/press-kit/articles/ClusterLandingPage.d.ts +40 -0
- package/dist/press-kit/articles/ClusterNavigation.d.ts +37 -0
- package/dist/press-kit/articles/ManagedNewsletterForm.d.ts +12 -0
- package/dist/press-kit/articles/NewsletterShell.d.ts +17 -0
- package/dist/press-kit/articles/NewsletterWidget.d.ts +30 -0
- package/dist/press-kit/articles/PublicationLayout.d.ts +67 -0
- package/dist/press-kit/articles/PublicationSidebar.d.ts +36 -0
- package/dist/press-kit/articles/RelatedPosts.d.ts +22 -0
- package/dist/press-kit/articles/ServiceCallout.d.ts +56 -0
- package/dist/press-kit/articles/TableOfContents.d.ts +6 -0
- package/dist/press-kit/articles/article-photos.d.ts +9 -0
- package/dist/press-kit/articles/article-styles.d.ts +21 -0
- package/dist/press-kit/articles/article-tables.d.ts +12 -0
- package/dist/press-kit/articles/artwork.d.ts +13 -0
- package/dist/press-kit/articles/author-schema.d.ts +72 -0
- package/dist/press-kit/articles/author-social.d.ts +14 -0
- package/dist/press-kit/articles/excerpt.d.ts +17 -0
- package/dist/press-kit/articles/index.d.ts +79 -0
- package/dist/press-kit/articles/news-sitemap.d.ts +46 -0
- package/dist/press-kit/articles/processArticleHtml.d.ts +34 -0
- package/dist/press-kit/articles/public-reads.d.ts +26 -0
- package/dist/press-kit/articles/reading-time.d.ts +9 -0
- package/dist/press-kit/articles/routes.d.ts +52 -0
- package/dist/press-kit/articles/server-core.d.ts +461 -0
- package/dist/press-kit/articles/server-ui.d.ts +31 -0
- package/dist/press-kit/articles/types.d.ts +316 -0
- package/dist/press-kit/articles/widget-styles.d.ts +8 -0
- package/dist/reputation/index.js +3 -3
- package/dist/reputation/server.js +2 -2
- package/dist/revalidate/index.js +2 -2
- package/dist/runtime/index.js +5 -4
- package/dist/seo/client.js +6 -6
- package/dist/seo/index.js +7 -7
- package/dist/seo/llms.js +8 -8
- package/dist/seo/register-sitemap-cli.js +1 -1
- package/dist/seo/sitemap.js +6 -6
- package/dist/server/index.js +3 -2
- package/dist/shared/build-entries.d.ts +2 -1
- package/dist/shared/clientApiConfig.d.ts +2 -0
- package/dist/shared/import-specifiers.d.ts +1 -1
- package/dist/shared/index.d.ts +6 -0
- package/dist/shared/index.js +8 -0
- package/dist/shared/version.d.ts +1 -1
- package/dist/signal/index.js +2 -2
- package/dist/sitemap/index.js +6 -6
- package/dist/sync/index.js +6 -6
- package/dist/website/images.js +4 -4
- package/dist/website/index.js +7 -7
- package/dist/website/popups.js +6 -6
- package/dist/{writeLLMsTxt-OS2WBSLM.js → writeLLMsTxt-VKHCM5IS.js} +5 -5
- package/docs/MIGRATING-TO-PRESS-KIT.md +83 -0
- package/docs.json +145 -29
- package/package.json +15 -5
- package/src/articles/README.md +5 -360
- package/src/mcp/README.md +16 -0
- package/src/og/README.md +1 -1
- package/src/runtime/README.md +4 -0
- package/dist/SiteChat-6SF7PVUT.js +0 -6
- package/dist/SiteDesignReporter-VISQZ5NQ.js +0 -12
- package/dist/SitemapSync-VKMVRPZL.js +0 -9
- package/dist/article-tables-AXSZOGNW.js +0 -2
package/src/articles/README.md
CHANGED
|
@@ -1,361 +1,6 @@
|
|
|
1
|
-
# Articles
|
|
1
|
+
# Articles moved to Press Kit
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
metadata alias; `basePath` takes precedence when both are provided.
|
|
8
|
-
|
|
9
|
-
```tsx
|
|
10
|
-
import { Article, ArticleList } from '@sonordev/site-kit/articles/server-ui'
|
|
11
|
-
import {
|
|
12
|
-
createPublicationRoutes, generateArticleMetadata, generateArticleSchema,
|
|
13
|
-
generateArticleSitemap, generateArticleStaticParams, generateRssFeed,
|
|
14
|
-
getArticle,
|
|
15
|
-
} from '@sonordev/site-kit/articles/server'
|
|
16
|
-
import type { PublicationRoutingOptions } from '@sonordev/site-kit/articles/server'
|
|
17
|
-
|
|
18
|
-
const siteUrl = 'https://example.com'
|
|
19
|
-
const routing = {
|
|
20
|
-
basePath: '/journal',
|
|
21
|
-
includeCategoryInPath: true,
|
|
22
|
-
categoryPath: (slug: string) => `/journal?category=${encodeURIComponent(slug)}`,
|
|
23
|
-
} satisfies PublicationRoutingOptions
|
|
24
|
-
|
|
25
|
-
// Cards, related stories and cluster links now use /journal/category/slug.
|
|
26
|
-
const article = <Article slug="build-first" routing={routing} />
|
|
27
|
-
const archive = <ArticleList routing={routing} />
|
|
28
|
-
|
|
29
|
-
// Use these helpers from their corresponding Next.js route exports.
|
|
30
|
-
const metadata = await generateArticleMetadata('build-first', { siteUrl, ...routing })
|
|
31
|
-
const post = await getArticle('build-first')
|
|
32
|
-
const schema = post ? generateArticleSchema(post, { siteUrl, ...routing }) : null
|
|
33
|
-
// In app/journal/[category]/[slug]/page.tsx, wrap it to pass the routing:
|
|
34
|
-
// export function generateStaticParams() { return generateArticleStaticParams(routing) }
|
|
35
|
-
const params = await generateArticleStaticParams(routing) // { category, slug }[]
|
|
36
|
-
const sitemap = await generateArticleSitemap(siteUrl, {
|
|
37
|
-
...routing,
|
|
38
|
-
includeCategories: false, // category filters don't need separate sitemap entries
|
|
39
|
-
includeClusters: false, // enable when your site implements cluster routes
|
|
40
|
-
})
|
|
41
|
-
const rss = await generateRssFeed({ siteUrl, siteName: 'The Journal', ...routing })
|
|
42
|
-
const href = createPublicationRoutes(routing).post({ slug: 'build-first', category: 'guides' })
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
`routing` also works on `PublicationLayout`, `PublicationSidebar`, `RelatedPosts`, `AuthorPage`,
|
|
46
|
-
`ClusterLandingPage`, and `ClusterNavigation`. To support another article shape,
|
|
47
|
-
provide `postPath: post => '/articles/' + encodeURIComponent(post.slug)`.
|
|
48
|
-
Callbacks return local paths; generated metadata, feeds, and sitemaps separately
|
|
49
|
-
honor a valid `canonical_url`. The same routes apply to Atom and generated
|
|
50
|
-
breadcrumb/cluster schemas. Supplied `schema`/`schema_json` objects stay intact, apart
|
|
51
|
-
from template placeholders (an `Organization` named "Example", `example.com` URLs,
|
|
52
|
-
`{post.title}`), which `generateAllArticleSchemas` drops. When nothing real is left,
|
|
53
|
-
it generates the article's schema from its own fields instead.
|
|
54
|
-
|
|
55
|
-
For custom layouts, `resolveArticleArtwork(post, 'article')` prefers
|
|
56
|
-
`editorial_image`, preserving an empty decorative `editorial_image_alt`.
|
|
57
|
-
`resolveArticleArtwork(post, 'card')` keeps the complete `featured_image`. Older API
|
|
58
|
-
responses fall back to the featured image for both. The stock UI keeps generated
|
|
59
|
-
Sonor cards fully visible, while manually selected photos keep their existing
|
|
60
|
-
layout. Social metadata and RSS enclosures use the featured share card.
|
|
61
|
-
|
|
62
|
-
The stock article renders accessible table scroll regions automatically. Custom
|
|
63
|
-
renderers can call `wrapArticleTables(html)` and include `articleTableCss` within their
|
|
64
|
-
`.sk-article-content` scope. Both exports are available from `article/server` and
|
|
65
|
-
`article/server-ui`. The wrapper preserves the original table and is safe to apply
|
|
66
|
-
twice. It's a layout transform, not a sanitizer; keep your existing content trust
|
|
67
|
-
policy. Scrollbars and keyboard focus use `--sk-*` tokens.
|
|
68
|
-
|
|
69
|
-
## Import server components from `article/server-ui`
|
|
70
|
-
|
|
71
|
-
`Article`, `ArticleList`, `PublicationLayout`, `PublicationSidebar` and `RelatedPosts` are async
|
|
72
|
-
server components. The `@sonordev/site-kit/articles` barrel is stamped `'use client'`
|
|
73
|
-
at build time, and an async component inside a client module is not something Next
|
|
74
|
-
can render — the route returns a 500.
|
|
75
|
-
|
|
76
|
-
```tsx
|
|
77
|
-
import { Article, ArticleList } from '@sonordev/site-kit/articles/server-ui' // ✅
|
|
78
|
-
import { Article } from '@sonordev/site-kit/articles' // ❌ 500s
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
The barrel still re-exports them for backwards compatibility and they will move out
|
|
82
|
-
in the next major. Reach for `@sonordev/site-kit/articles` only for genuine client
|
|
83
|
-
components: `TableOfContents`, `ArticleFAQ`, `AuthorCard`, `ServiceCallout`,
|
|
84
|
-
`NewsletterWidget`.
|
|
85
|
-
|
|
86
|
-
`NewsletterWidget` needs either an `onSubmit` callback or a `formSlug`
|
|
87
|
-
pointing at a managed Sonor form (newsletter routing) — with neither it
|
|
88
|
-
renders nothing rather than a form that discards emails. `Article` mounts a
|
|
89
|
-
childless `ArticleViewTracker` client island that counts real readers (one POST
|
|
90
|
-
per post per session, deferred to idle); it needs `SiteKitLayout`'s globals
|
|
91
|
-
and silently no-ops without them.
|
|
92
|
-
|
|
93
|
-
Data helpers live in `@sonordev/site-kit/articles/server`, which is `server-only` — a
|
|
94
|
-
client import there is a build error rather than a runtime one.
|
|
95
|
-
|
|
96
|
-
Sonor-managed article with SSG, topic clusters, E-E-A-T author profiles, and full SEO integration. Create posts in the Sonor dashboard — they appear on your site automatically.
|
|
97
|
-
|
|
98
|
-
## Quick Start
|
|
99
|
-
|
|
100
|
-
### Articles Index
|
|
101
|
-
|
|
102
|
-
```tsx
|
|
103
|
-
// app/article/page.tsx
|
|
104
|
-
import { ArticleList, PublicationLayout } from '@sonordev/site-kit/articles/server-ui'
|
|
105
|
-
import { generatePublicationMetadata } from '@sonordev/site-kit/articles/server'
|
|
106
|
-
|
|
107
|
-
export async function generateMetadata() {
|
|
108
|
-
return generatePublicationMetadata({ siteName: 'My Site', siteUrl: 'https://example.com' })
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export default function PublicationPage() {
|
|
112
|
-
return (
|
|
113
|
-
<PublicationLayout hero={{ title: 'The Journal', subtitle: 'Latest articles' }}>
|
|
114
|
-
<ArticleList showCategoryFilter showPagination />
|
|
115
|
-
</PublicationLayout>
|
|
116
|
-
)
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Single Post
|
|
121
|
-
|
|
122
|
-
```tsx
|
|
123
|
-
// app/article/[slug]/page.tsx
|
|
124
|
-
import { Article } from '@sonordev/site-kit/articles/server-ui'
|
|
125
|
-
import {
|
|
126
|
-
generateArticleStaticParams,
|
|
127
|
-
generateArticleMetadata,
|
|
128
|
-
requireArticle,
|
|
129
|
-
} from '@sonordev/site-kit/articles/server'
|
|
130
|
-
|
|
131
|
-
export const generateStaticParams = generateArticleStaticParams
|
|
132
|
-
|
|
133
|
-
type Props = { params: Promise<{ slug: string }> }
|
|
134
|
-
|
|
135
|
-
export async function generateMetadata({ params }: Props) {
|
|
136
|
-
const { slug } = await params
|
|
137
|
-
await requireArticle(slug) // 404 for an unknown slug, before anything streams
|
|
138
|
-
return generateArticleMetadata(slug, { siteName: 'My Site', siteUrl: 'https://example.com' })
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export default async function Post({ params }: Props) {
|
|
142
|
-
const { slug } = await params
|
|
143
|
-
return <Article slug={slug} showRelated showToc showAuthor />
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
**A missing post is a 404.** `requireArticle`, `generateArticleMetadata` and
|
|
148
|
-
`Article` all call Next's `notFound()` when the post doesn't exist, so a
|
|
149
|
-
mistyped or deleted post URL answers 404 with `noindex`. They used to render a
|
|
150
|
-
"Post Not Found" page on a 200 with an indexable title, which is a soft 404.
|
|
151
|
-
Calling `requireArticle` from `generateMetadata` is what guarantees the status:
|
|
152
|
-
metadata resolves before the page streams. To render your own missing-post
|
|
153
|
-
state instead, pass `notFound: false` to `generateArticleMetadata` (its
|
|
154
|
-
placeholder is marked `noindex`) and `notFound={false}` to `Article`.
|
|
155
|
-
|
|
156
|
-
**A post with its own social card.** If the post route has an
|
|
157
|
-
`opengraph-image.tsx` beside the page (see `@sonordev/site-kit/og/route`), pass
|
|
158
|
-
`images: false`:
|
|
159
|
-
|
|
160
|
-
```ts
|
|
161
|
-
return generateArticleMetadata(slug, { siteName: 'My Site', images: false })
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
Otherwise the featured image is declared as `openGraph.images`, and Next lets
|
|
165
|
-
declared images beat the file convention: the card never ships. `images: false`
|
|
166
|
-
leaves the keys out entirely (Next checks `hasOwnProperty('images')`, so even
|
|
167
|
-
`images: undefined` would hide the card). `sonor-setup doctor` flags a post
|
|
168
|
-
route that has a card but still declares the image.
|
|
169
|
-
|
|
170
|
-
### Topic Cluster Landing
|
|
171
|
-
|
|
172
|
-
```tsx
|
|
173
|
-
// app/article/topics/[slug]/page.tsx
|
|
174
|
-
// From `article/server-ui`, not `article` — it's an async server component that fetches
|
|
175
|
-
// the cluster, so it must stay out of the client-side `article` barrel.
|
|
176
|
-
import { ClusterLandingPage } from '@sonordev/site-kit/articles/server-ui'
|
|
177
|
-
|
|
178
|
-
export default function ClusterPage({ params }: { params: { slug: string } }) {
|
|
179
|
-
return <ClusterLandingPage slug={params.slug} basePath="/article" />
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
## Components
|
|
184
|
-
|
|
185
|
-
| Component | Import | Purpose |
|
|
186
|
-
|-----------|--------|---------|
|
|
187
|
-
| `Article` | `article/server-ui` | Single post with content, TOC, author, related |
|
|
188
|
-
| `ArticleList` | `article/server-ui` | Post grid with pagination and category filter |
|
|
189
|
-
| `PublicationLayout` | `article/server-ui` | Full layout with optional sidebar |
|
|
190
|
-
| `PublicationSidebar` | `article/server-ui` | Categories, recent posts, tags |
|
|
191
|
-
| `RelatedPosts` | `article/server-ui` | Related articles widget |
|
|
192
|
-
| `PublicationPage` | `article/server-ui` | Drop-in publication index page (layout + list) |
|
|
193
|
-
| `ArticlePage` | `article/server-ui` | Drop-in single-post page |
|
|
194
|
-
| `CategoryPage` | `article/server-ui` | Drop-in category archive page |
|
|
195
|
-
| `ClusterLandingPage` | `article/server-ui` | Topic cluster overview with pillar + support articles |
|
|
196
|
-
| `ClusterNavigation` | `article` | Breadcrumb-style cluster nav |
|
|
197
|
-
| `AuthorCard` | `article` | Author profile with E-E-A-T fields |
|
|
198
|
-
| `TableOfContents` | `article` | Auto-generated from H2-H4 headings |
|
|
199
|
-
| `ArticleFAQ` | `article` | FAQ section with schema |
|
|
200
|
-
| `ServiceCallout` | `article` | CTA callout for related services |
|
|
201
|
-
| `NewsletterWidget` | `article` | Email capture; needs `onSubmit` or `formSlug` |
|
|
202
|
-
|
|
203
|
-
## Server Functions (`article/server`)
|
|
204
|
-
|
|
205
|
-
```ts
|
|
206
|
-
// Data fetching
|
|
207
|
-
getArticle(slug) // Single post with full data, or null
|
|
208
|
-
requireArticle(slug) // Same, but calls notFound() when there's no post
|
|
209
|
-
getAllArticleSlugs() // All published slugs (for generateStaticParams)
|
|
210
|
-
getArticleCategories() // Categories with post counts
|
|
211
|
-
getTopicCluster(slug) // Cluster with pillar + support articles
|
|
212
|
-
getTopicClusters() // All clusters
|
|
213
|
-
|
|
214
|
-
// Next.js integration
|
|
215
|
-
generateArticleStaticParams(routing?) // [{ slug }], or [{ category, slug }] with includeCategoryInPath
|
|
216
|
-
generateCategoryStaticParams() // Returns [{ category }]
|
|
217
|
-
generateAuthorStaticParams() // Returns [{ slug }]
|
|
218
|
-
// With no routing options, all three can be exported directly as
|
|
219
|
-
// `generateStaticParams`; the props Next passes are ignored.
|
|
220
|
-
generateArticleMetadata(slug, opts) // Next.js Metadata object; notFound() for a missing post.
|
|
221
|
-
// opts.images: false when the route has its own opengraph-image card
|
|
222
|
-
generatePublicationMetadata(opts) // Index page metadata
|
|
223
|
-
generateArticleCategoryMetadata(name, opts)
|
|
224
|
-
|
|
225
|
-
// Schema & SEO
|
|
226
|
-
generateArticleSchema(post, opts) // JSON-LD Article with FAQ
|
|
227
|
-
generateArticleListSchema() // JSON-LD for publication index
|
|
228
|
-
generateFaqSchema(items) // FAQ Page schema
|
|
229
|
-
generateArticleSitemap(siteUrl) // Sitemap entries for article
|
|
230
|
-
|
|
231
|
-
// Validation
|
|
232
|
-
validateArticleSeo(post) // Returns field-by-field SEO audit
|
|
233
|
-
validateSeoTitle(title, keyphrase?) // Title length + keyword checks
|
|
234
|
-
validateMetaDescription(desc) // Description length check
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
## Multi-site projects
|
|
238
|
-
|
|
239
|
-
One Sonor project can serve many domains (example.com plus its city
|
|
240
|
-
microsites), each with its own article. A post with no site is project-wide and
|
|
241
|
-
shows on every host; a post tagged `charlotte.example.com` shows only there.
|
|
242
|
-
|
|
243
|
-
Every article read sends the site host automatically, as `?site=` on GETs and a
|
|
244
|
-
`site` field on the related-posts and view-count POSTs. The host resolves from
|
|
245
|
-
`NEXT_PUBLIC_SITE_URL`, which every microsite already sets, so most sites
|
|
246
|
-
change nothing. To pin a host explicitly, pass `site`:
|
|
247
|
-
|
|
248
|
-
```tsx
|
|
249
|
-
<ArticleList site="charlotte.example.com" /> // also Article, PublicationSidebar, PublicationLayout, RelatedPosts, ClusterLandingPage
|
|
250
|
-
await getArticle(slug, { site: 'charlotte.example.com' })
|
|
251
|
-
await getAllArticles({ site: 'charlotte.example.com' })
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
`getAllArticleSlugs()` and `getAllAuthorSlugs()` take no arguments, so they can
|
|
255
|
-
still be exported as `generateStaticParams`. They always use
|
|
256
|
-
`NEXT_PUBLIC_SITE_URL`. Single-site projects and older API servers ignore
|
|
257
|
-
`site`.
|
|
258
|
-
|
|
259
|
-
## Article Props
|
|
260
|
-
|
|
261
|
-
```ts
|
|
262
|
-
interface ArticleProps {
|
|
263
|
-
slug: string
|
|
264
|
-
showRelated?: boolean // Related posts section
|
|
265
|
-
showToc?: boolean // Table of contents
|
|
266
|
-
showAuthor?: boolean // Author card
|
|
267
|
-
unstyled?: boolean // Skip default styles
|
|
268
|
-
notFound?: boolean // Default true: notFound() when the post is missing. false renders a message.
|
|
269
|
-
className?: string
|
|
270
|
-
children?: (props: { post, toc, related }) => ReactNode // Render prop
|
|
271
|
-
}
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
## ArticleList Props
|
|
275
|
-
|
|
276
|
-
```ts
|
|
277
|
-
interface ArticleListProps {
|
|
278
|
-
category?: string // Filter by category slug
|
|
279
|
-
tag?: string // Filter by tag
|
|
280
|
-
author?: string // Filter by author
|
|
281
|
-
featured?: boolean // Featured posts only
|
|
282
|
-
search?: string // Search posts
|
|
283
|
-
page?: number // Default: 1
|
|
284
|
-
perPage?: number // Default: 12
|
|
285
|
-
orderBy?: 'published_at' | 'title' | 'view_count'
|
|
286
|
-
order?: 'asc' | 'desc'
|
|
287
|
-
showCategoryFilter?: boolean
|
|
288
|
-
showPagination?: boolean
|
|
289
|
-
unstyled?: boolean
|
|
290
|
-
className?: string
|
|
291
|
-
children?: (props: { posts, pagination, categories }) => ReactNode
|
|
292
|
-
}
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
## Key Types
|
|
296
|
-
|
|
297
|
-
```ts
|
|
298
|
-
interface Article {
|
|
299
|
-
slug: string; title: string; excerpt?: string; content: string;
|
|
300
|
-
featured_image?: string; author?: ArticleAuthor; category?: ArticleCategory;
|
|
301
|
-
tags?: string[]; meta_title?: string; meta_description?: string;
|
|
302
|
-
faq_items?: { question: string; answer: string }[];
|
|
303
|
-
article_type?: 'pillar' | 'support' | 'comparison' | 'faq' | 'glossary' | 'checklist';
|
|
304
|
-
cluster_slug?: string; reading_time?: number; // the API column ('X min read')
|
|
305
|
-
published_at?: string; status: 'draft' | 'published' | 'scheduled' | 'archived';
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
interface ArticleAuthor {
|
|
309
|
-
name: string; slug: string; bio?: string; avatar_url?: string;
|
|
310
|
-
title?: string; credentials?: string[]; expertise_areas?: string[];
|
|
311
|
-
years_experience?: number; is_subject_matter_expert?: boolean;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
interface TopicCluster {
|
|
315
|
-
cluster_name: string; cluster_slug: string; core_topic: string;
|
|
316
|
-
geo_target?: string; target_service_page?: string; article_count: number;
|
|
317
|
-
pillar: Article | null; supports: Article[];
|
|
318
|
-
}
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
## Styling
|
|
322
|
-
|
|
323
|
-
Components use `.sk-article-*` and `.sk-article-list-*` classes. Import default styles:
|
|
324
|
-
|
|
325
|
-
```tsx
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
Or use `unstyled` prop + `children` render prop for complete control.
|
|
329
|
-
|
|
330
|
-
## Supporting photos (8.1)
|
|
331
|
-
|
|
332
|
-
Sonor's article photo plan can attach up to eight supporting images to exact
|
|
333
|
-
places in the article. The public response includes `editorial_photos`, an
|
|
334
|
-
optional array of approved snapshots with `url`, `alt`, optional `caption`,
|
|
335
|
-
`width`, `height`, and `placement`. Placement is `{ kind: 'after_intro' }` or
|
|
336
|
-
`{ kind: 'after_heading', heading: 'Exact section heading' }`. Photo briefs,
|
|
337
|
-
file ownership and private source notes stay in the authenticated API.
|
|
338
|
-
|
|
339
|
-
The default `Article` renderer and the RSS/Atom generators apply these
|
|
340
|
-
placements automatically. Custom publications use the same pure helper after
|
|
341
|
-
converting Markdown to HTML and before rendering their content:
|
|
342
|
-
|
|
343
|
-
```tsx
|
|
344
|
-
import { insertArticlePhotos, articlePhotoCss } from '@sonordev/site-kit/articles/server-ui'
|
|
345
|
-
|
|
346
|
-
const html = insertArticlePhotos(articleHtml, post.editorial_photos)
|
|
347
|
-
// Render articlePhotoCss once and use html with your existing trusted-content policy.
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
The helper inserts a semantic figure after the introduction's first prose
|
|
351
|
-
paragraph or a section's first prose paragraph. It preserves the surrounding
|
|
352
|
-
HTML and includes intrinsic dimensions, descriptions, captions and lazy loading.
|
|
353
|
-
It skips missing or duplicate headings, code examples, tables, existing figures,
|
|
354
|
-
lists, and callouts. An image already in the body isn't inserted again. It
|
|
355
|
-
doesn't sanitize the original article HTML; retain your existing content policy.
|
|
356
|
-
Articles without this field render as before. Custom styles can target
|
|
357
|
-
`.sk-article-photo` and `.sk-article-photo figcaption` or use `articlePhotoCss`.
|
|
358
|
-
|
|
359
|
-
Agents managing photos use the authenticated Sonor article media tools. The
|
|
360
|
-
site's public `get_article` MCP tool only reads the approved photo descriptions
|
|
361
|
-
and placements; it can't attach photos or read planning briefs.
|
|
3
|
+
The canonical article/publication implementation ships in `@sonordev/press-kit`.
|
|
4
|
+
Site Kit 8.3 keeps `/articles`, `/articles/server` and `/articles/server-ui` imports working.
|
|
5
|
+
New sites should install Press Kit 1.x beside Site Kit 8.3 and use its corresponding entries.
|
|
6
|
+
See [the migration guide](../../docs/MIGRATING-TO-PRESS-KIT.md).
|
package/src/mcp/README.md
CHANGED
|
@@ -44,9 +44,11 @@ site's pages use, so an agent gets what a visitor gets.
|
|
|
44
44
|
// lib/mcp.ts
|
|
45
45
|
import 'server-only'
|
|
46
46
|
import { sonorMcpServer } from '@sonordev/site-kit/mcp/sonor'
|
|
47
|
+
import { getAllArticles, getArticle } from '@sonordev/press-kit/articles/server'
|
|
47
48
|
|
|
48
49
|
export const mcpServer = sonorMcpServer({
|
|
49
50
|
businessName: 'Example Law',
|
|
51
|
+
articles: { path: '/news', readMany: getAllArticles, readOne: getArticle },
|
|
50
52
|
inquiry: { form: 'contact' }, // the form's "Agent inquiries" switch must be on in Sonor
|
|
51
53
|
tools: [/* the site's own tools, served beside these */],
|
|
52
54
|
})
|
|
@@ -63,6 +65,20 @@ export const { POST, GET, DELETE, OPTIONS } = createMcpHandler({
|
|
|
63
65
|
})
|
|
64
66
|
```
|
|
65
67
|
|
|
68
|
+
**Articles use Press Kit readers (8.3).** Install `@sonordev/press-kit` alongside
|
|
69
|
+
Site Kit 8.3 or newer and pass `getAllArticles` as `readMany` and `getArticle` as
|
|
70
|
+
`readOne`, as above. Both receive the configured sub-site, so site pages and
|
|
71
|
+
agent tools read the same publication. Custom CMS adapters can implement the
|
|
72
|
+
exported `McpArticleReaders` shape. Provide both readers together; a partial
|
|
73
|
+
pair fails with an actionable error. The tools return only published public
|
|
74
|
+
article data and approved photo snapshots, never private photo briefs or files.
|
|
75
|
+
|
|
76
|
+
The 8.x compatibility adapter remains when neither reader is supplied, so
|
|
77
|
+
existing servers keep working. It's loaded only if an article tool is called.
|
|
78
|
+
New `npx sonor-setup mcp --articles /news` scaffolds explicit Press Kit readers.
|
|
79
|
+
For a site without articles, use `articles: false` or `--no-articles` and no
|
|
80
|
+
Press Kit install is needed. Migrating packages doesn't publish or broadcast.
|
|
81
|
+
|
|
66
82
|
**`list_offerings` needs a reader (8.0).** The catalog it lists belongs to
|
|
67
83
|
[commerce-kit](https://sonor.dev/commerce-kit), so you hand the tool commerce-kit's
|
|
68
84
|
fetcher instead of site-kit importing it:
|
package/src/og/README.md
CHANGED
|
@@ -234,7 +234,7 @@ renders on demand with next/og. Use the metadata file convention:
|
|
|
234
234
|
```tsx
|
|
235
235
|
// app/article/[slug]/opengraph-image.tsx
|
|
236
236
|
import { createOgImage } from '@sonordev/site-kit/og/route'
|
|
237
|
-
import { getArticle } from '@sonordev/
|
|
237
|
+
import { getArticle } from '@sonordev/press-kit/articles/server'
|
|
238
238
|
import ogConfig from '../../../og.config'
|
|
239
239
|
|
|
240
240
|
export { size, contentType } from '@sonordev/site-kit/og/route'
|
package/src/runtime/README.md
CHANGED
|
@@ -49,3 +49,7 @@ Pass `apiKey` from `getClientApiConfig()` and `sonorFetch` sets the `x-api-key`
|
|
|
49
49
|
## Stability
|
|
50
50
|
|
|
51
51
|
This entry is public API under semver. New exports arrive in minor releases; nothing in the list above is removed or changed in a minor or patch release.
|
|
52
|
+
|
|
53
|
+
## Shared website utilities (8.3)
|
|
54
|
+
|
|
55
|
+
`@sonordev/site-kit/shared` provides `withSiteParam`, `withSiteBody`, `resolveSiteHost`, `readPublishedSite`, `applyTrailingSlash`, `resolveTrailingSlash` and `safeJsonLd`. These are framework-free functions for kits and sites; the entry is never marked `use client`. Press Kit uses them to keep sub-site scoping, URL shape and JSON-LD escaping consistent without reaching into private Site Kit files. `readPublishedSite()` returns the layout's explicitly published host, or undefined on the server.
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export { SiteDesignReporter } from './chunk-G2HCCWZQ.js';
|
|
2
|
-
import './chunk-DSNCU2E3.js';
|
|
3
|
-
import './chunk-IHG36STL.js';
|
|
4
|
-
import './chunk-YC7ELZS3.js';
|
|
5
|
-
import './chunk-S22FSH7C.js';
|
|
6
|
-
import './chunk-CAH4Y4PY.js';
|
|
7
|
-
import './chunk-EKBEOXTH.js';
|
|
8
|
-
import './chunk-3G7YABRA.js';
|
|
9
|
-
import './chunk-WPOFTCKL.js';
|
|
10
|
-
import './chunk-F2VYN3QL.js';
|
|
11
|
-
import './chunk-NWVB75ER.js';
|
|
12
|
-
import './chunk-PKBMQBKP.js';
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export { SitemapSync } from './chunk-IAYGTQJD.js';
|
|
2
|
-
import './chunk-YVKRYQRG.js';
|
|
3
|
-
import './chunk-CAH4Y4PY.js';
|
|
4
|
-
import './chunk-EKBEOXTH.js';
|
|
5
|
-
import './chunk-3G7YABRA.js';
|
|
6
|
-
import './chunk-WPOFTCKL.js';
|
|
7
|
-
import './chunk-F2VYN3QL.js';
|
|
8
|
-
import './chunk-NWVB75ER.js';
|
|
9
|
-
import './chunk-PKBMQBKP.js';
|