@valkyrianlabs/payload-markdown-docs 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/dist/next/PayloadMarkdownDocsPage.js +11 -4
- package/dist/next/PayloadMarkdownDocsPage.js.map +1 -1
- package/dist/next/sidebar.js +19 -9
- package/dist/next/sidebar.js.map +1 -1
- package/dist/next/types.d.ts +1 -1
- package/dist/next/types.js.map +1 -1
- package/dist/skills/codex/SKILL.md +8 -0
- package/dist/skills/codex/reference/formatting.md +24 -0
- package/dist/skills/codex/reference/frontmatter.md +2 -0
- package/dist/skills/codex/reference/routing.md +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,6 +73,10 @@ to a matching docs set from the configured branch.
|
|
|
73
73
|
|
|
74
74
|
## Render In Next
|
|
75
75
|
|
|
76
|
+
The plugin does not mutate your Pages collection and does not register public
|
|
77
|
+
frontend routes. Add route handlers in your Next app where you want docs to
|
|
78
|
+
render.
|
|
79
|
+
|
|
76
80
|
```tsx
|
|
77
81
|
import config from '@payload-config'
|
|
78
82
|
import {
|
|
@@ -108,6 +112,37 @@ const docsLinks = await getPayloadMarkdownDocsLinks({ payload })
|
|
|
108
112
|
// [{ label: 'Payload Markdown Docs', url: '/plugins/payload-markdown-docs' }]
|
|
109
113
|
```
|
|
110
114
|
|
|
115
|
+
## Serve Raw Markdown
|
|
116
|
+
|
|
117
|
+
The AI-facing raw Markdown export is a route-handler response, not a generated
|
|
118
|
+
Payload Page. Add a `route.ts` at the exported path, usually the value from
|
|
119
|
+
`docs/index.ai.yml`:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
// app/(frontend)/plugins/payload-markdown-docs.md/route.ts
|
|
123
|
+
import config from '@payload-config'
|
|
124
|
+
import { createPayloadMarkdownDocsMarkdownResponse } from '@valkyrianlabs/payload-markdown-docs/next'
|
|
125
|
+
import { notFound } from 'next/navigation'
|
|
126
|
+
import { getPayload } from 'payload'
|
|
127
|
+
|
|
128
|
+
export async function GET() {
|
|
129
|
+
const payload = await getPayload({ config })
|
|
130
|
+
const response = await createPayloadMarkdownDocsMarkdownResponse({
|
|
131
|
+
payload,
|
|
132
|
+
path: '/plugins/payload-markdown-docs.md',
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
if (response) {
|
|
136
|
+
return response
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
notFound()
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The response is `text/markdown; charset=utf-8` and is assembled from synced
|
|
144
|
+
docs records using `docs/index.ai.yml` when present.
|
|
145
|
+
|
|
111
146
|
## Validate Locally
|
|
112
147
|
|
|
113
148
|
In an app or docs repository that has installed this package:
|
|
@@ -127,6 +162,19 @@ pnpm cli validate ./docs --source payload-markdown-docs
|
|
|
127
162
|
In GitHub Actions, `--source` can be omitted when the docs set slug matches the
|
|
128
163
|
repository name. The CLI infers it from `GITHUB_REPOSITORY`.
|
|
129
164
|
|
|
165
|
+
## Maintain Docs With Codex
|
|
166
|
+
|
|
167
|
+
In a docs set target application, install the local Codex skill so agents have
|
|
168
|
+
repo-local guidance for maintaining Markdown docs, frontmatter, `index.ai.yml`,
|
|
169
|
+
validation, and sync safety rules.
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
pnpm exec payload-markdown-docs install skill --codex
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The installer writes `.agents/skills/payload-markdown-docs/`. It does not sync
|
|
176
|
+
docs, call Payload, or publish content.
|
|
177
|
+
|
|
130
178
|
## Publish From GitHub Actions
|
|
131
179
|
|
|
132
180
|
```yaml
|
|
@@ -23,16 +23,23 @@ const renderSidebarItems = (items, activeRoute, depth = 0)=>{
|
|
|
23
23
|
}
|
|
24
24
|
return /*#__PURE__*/ _jsx("ul", {
|
|
25
25
|
className: cx(depth === 0 ? 'space-y-1' : 'ml-3 mt-1 space-y-1 border-l border-border pl-3'),
|
|
26
|
-
children: items.map((item)
|
|
26
|
+
children: items.map((item)=>{
|
|
27
|
+
const isActive = item.route === activeRoute;
|
|
28
|
+
const labelClassName = cx('block rounded-lg px-3 py-2 text-sm leading-5 transition-colors', item.route ? isActive ? 'bg-cyan-400/10 text-cyan-200' : 'text-foreground/70 hover:bg-white/[0.04] hover:text-foreground' : 'text-foreground/55');
|
|
29
|
+
return /*#__PURE__*/ _jsxs("li", {
|
|
27
30
|
children: [
|
|
28
|
-
/*#__PURE__*/ _jsx("a", {
|
|
29
|
-
className:
|
|
31
|
+
item.route ? /*#__PURE__*/ _jsx("a", {
|
|
32
|
+
className: labelClassName,
|
|
30
33
|
href: item.route,
|
|
31
34
|
children: item.label
|
|
35
|
+
}) : /*#__PURE__*/ _jsx("span", {
|
|
36
|
+
className: labelClassName,
|
|
37
|
+
children: item.label
|
|
32
38
|
}),
|
|
33
39
|
item.children ? renderSidebarItems(item.children, activeRoute, depth + 1) : null
|
|
34
40
|
]
|
|
35
|
-
}, item.route)
|
|
41
|
+
}, item.route ?? item.sourcePath);
|
|
42
|
+
})
|
|
36
43
|
});
|
|
37
44
|
};
|
|
38
45
|
const renderMarkdown = async ({ collectionSlug, markdown })=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/next/PayloadMarkdownDocsPage.tsx"],"sourcesContent":["import type { CSSProperties, ReactNode } from 'react'\n\nimport type {\n PayloadMarkdownDocsHeroImage,\n PayloadMarkdownDocsSidebarItem,\n ResolvedPayloadMarkdownDocsRecord,\n ResolvedPayloadMarkdownDocsRoute,\n ResolvedPayloadMarkdownDocsSet,\n} from './types.js'\n\nimport { DEFAULT_DOCS_COLLECTION_SLUG } from '../constants.js'\n\nexport type PayloadMarkdownDocsPageProps = {\n collectionSlug?: string\n renderSidebar?: boolean\n resolved: ResolvedPayloadMarkdownDocsRoute\n}\n\nconst cx = (...values: (false | null | string | undefined)[]): string =>\n values.filter(Boolean).join(' ')\n\nconst docsLayoutStyles = `\n[data-payload-markdown-docs-layout] {\n display: grid;\n gap: 2.5rem;\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 1024px) {\n [data-payload-markdown-docs-layout=\"with-sidebar\"] {\n grid-template-columns: 16rem minmax(0, 1fr);\n }\n}\n`\n\nconst getDocsLayoutStyle = (hasHero: boolean): CSSProperties =>\n hasHero\n ? {}\n : {\n marginTop: '6rem',\n }\n\nconst renderSidebarItems = (\n items: PayloadMarkdownDocsSidebarItem[],\n activeRoute: string,\n depth = 0,\n): ReactNode => {\n if (items.length === 0) {\n return null\n }\n\n return (\n <ul\n className={cx(depth === 0 ? 'space-y-1' : 'ml-3 mt-1 space-y-1 border-l border-border pl-3')}\n >\n {items.map((item) => (\n <li key={item.route}>\n <a\n className={cx(\n 'block rounded-lg px-3 py-2 text-sm leading-5 transition-colors',\n item.route === activeRoute\n ? 'bg-cyan-400/10 text-cyan-200'\n : 'text-foreground/70 hover:bg-white/[0.04] hover:text-foreground',\n )}\n href={item.route}\n >\n {item.label}\n </a>\n {item.children ? renderSidebarItems(item.children, activeRoute, depth + 1) : null}\n </li>\n ))}\n </ul>\n )\n}\n\nconst renderMarkdown = async ({\n collectionSlug,\n markdown,\n}: {\n collectionSlug: string\n markdown?: string\n}): Promise<ReactNode> => {\n if (!markdown?.trim()) {\n return null\n }\n\n const { MarkdownRenderer } = await import('@valkyrianlabs/payload-markdown/server')\n\n return MarkdownRenderer({\n className: 'min-w-0',\n collectionSlug,\n markdown,\n scope: 'field',\n size: 'md',\n variant: 'docs',\n wrapperClassName: 'min-w-0',\n })\n}\n\nconst DocsHeader = ({\n doc,\n docsSet,\n}: {\n doc?: ResolvedPayloadMarkdownDocsRecord\n docsSet: ResolvedPayloadMarkdownDocsSet\n}) => {\n const description = doc?.description ?? docsSet.description\n const title = doc?.title ?? docsSet.title\n\n return (\n <header className=\"mb-10 border-b border-border pb-8\">\n <h1 className=\"text-4xl font-semibold tracking-tight text-foreground md:text-5xl\">{title}</h1>\n {description ? (\n <p className=\"mt-4 max-w-3xl text-lg leading-8 text-foreground/70\">{description}</p>\n ) : null}\n </header>\n )\n}\n\nconst DocsHero = ({\n heroImage,\n title,\n}: {\n heroImage?: PayloadMarkdownDocsHeroImage\n title: string\n}) => {\n if (!heroImage) {\n return null\n }\n\n return (\n <figure\n className=\"mb-10 overflow-hidden rounded-xl border border-border bg-white/[0.03]\"\n data-payload-markdown-docs-hero\n style={{\n borderRadius: '0.75rem',\n marginBottom: '2.5rem',\n overflow: 'hidden',\n }}\n >\n <img\n alt={heroImage.alt ?? title}\n className=\"block h-auto w-full\"\n height={heroImage.height}\n src={heroImage.url}\n style={{\n display: 'block',\n height: 'auto',\n width: '100%',\n }}\n width={heroImage.width}\n />\n </figure>\n )\n}\n\nexport const PayloadMarkdownDocsPage = async ({\n collectionSlug = DEFAULT_DOCS_COLLECTION_SLUG,\n renderSidebar = true,\n resolved,\n}: PayloadMarkdownDocsPageProps) => {\n if (resolved.type === 'docsGroupIndex') {\n return (\n <main\n className=\"min-h-screen bg-background text-foreground\"\n data-payload-markdown-docs-route={resolved.route}\n >\n <div className=\"mx-auto w-full max-w-6xl px-6 py-14 lg:px-8\">\n <header className=\"mb-10 border-b border-border pb-8\">\n <p className=\"mb-3 text-sm font-medium uppercase tracking-wide text-cyan-300\">Docs</p>\n <h1 className=\"text-4xl font-semibold tracking-tight md:text-5xl\">\n {resolved.group.navTitle ?? resolved.group.title}\n </h1>\n {resolved.group.description ? (\n <p className=\"mt-4 max-w-3xl text-lg leading-8 text-foreground/70\">\n {resolved.group.description}\n </p>\n ) : null}\n </header>\n {resolved.docsSets.length > 0 ? (\n <nav aria-label=\"Docs sets\">\n <ul className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {resolved.docsSets.map((docsSet) => (\n <li key={docsSet.id}>\n <a\n className=\"block rounded-xl border border-border bg-white/[0.03] p-5 transition-colors hover:bg-white/[0.06]\"\n href={docsSet.routeBase}\n >\n <span className=\"text-base font-semibold text-foreground\">\n {docsSet.navTitle ?? docsSet.title}\n </span>\n {docsSet.description ? (\n <span className=\"mt-2 block text-sm leading-6 text-foreground/65\">\n {docsSet.description}\n </span>\n ) : null}\n </a>\n </li>\n ))}\n </ul>\n </nav>\n ) : null}\n </div>\n </main>\n )\n }\n\n const markdown = await renderMarkdown({\n collectionSlug,\n markdown: resolved.doc?.content,\n })\n const hasHero = Boolean(resolved.doc?.heroImage)\n const hasSidebar = renderSidebar && resolved.sidebar.length > 0\n\n return (\n <main\n className=\"min-h-screen bg-background text-foreground\"\n data-payload-markdown-docs-route={resolved.route}\n >\n <style>{docsLayoutStyles}</style>\n <div\n className=\"mx-auto w-full max-w-7xl px-6 py-10 lg:px-8\"\n data-payload-markdown-docs-layout={hasSidebar ? 'with-sidebar' : 'default'}\n style={getDocsLayoutStyle(hasHero)}\n >\n {hasSidebar ? (\n <aside\n className=\"lg:sticky lg:top-8 lg:self-start\"\n style={{\n alignSelf: 'start',\n }}\n >\n <nav\n aria-label=\"Docs navigation\"\n className=\"rounded-xl border border-border bg-white/[0.03] p-3\"\n >\n {renderSidebarItems(resolved.sidebar, resolved.route)}\n </nav>\n </aside>\n ) : null}\n <article className=\"min-w-0 max-w-4xl\">\n <DocsHero\n heroImage={resolved.doc?.heroImage}\n title={resolved.doc?.title ?? resolved.docsSet.title}\n />\n <DocsHeader doc={resolved.doc} docsSet={resolved.docsSet} />\n {markdown}\n </article>\n </div>\n </main>\n )\n}\n"],"names":["DEFAULT_DOCS_COLLECTION_SLUG","cx","values","filter","Boolean","join","docsLayoutStyles","getDocsLayoutStyle","hasHero","marginTop","renderSidebarItems","items","activeRoute","depth","length","ul","className","map","item","li","a","route","href","label","children","renderMarkdown","collectionSlug","markdown","trim","MarkdownRenderer","scope","size","variant","wrapperClassName","DocsHeader","doc","docsSet","description","title","header","h1","p","DocsHero","heroImage","figure","data-payload-markdown-docs-hero","style","borderRadius","marginBottom","overflow","img","alt","height","src","url","display","width","PayloadMarkdownDocsPage","renderSidebar","resolved","type","main","data-payload-markdown-docs-route","div","group","navTitle","docsSets","nav","aria-label","routeBase","span","id","content","hasSidebar","sidebar","data-payload-markdown-docs-layout","aside","alignSelf","article"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/next/PayloadMarkdownDocsPage.tsx"],"sourcesContent":["import type { CSSProperties, ReactNode } from 'react'\n\nimport type {\n PayloadMarkdownDocsHeroImage,\n PayloadMarkdownDocsSidebarItem,\n ResolvedPayloadMarkdownDocsRecord,\n ResolvedPayloadMarkdownDocsRoute,\n ResolvedPayloadMarkdownDocsSet,\n} from './types.js'\n\nimport { DEFAULT_DOCS_COLLECTION_SLUG } from '../constants.js'\n\nexport type PayloadMarkdownDocsPageProps = {\n collectionSlug?: string\n renderSidebar?: boolean\n resolved: ResolvedPayloadMarkdownDocsRoute\n}\n\nconst cx = (...values: (false | null | string | undefined)[]): string =>\n values.filter(Boolean).join(' ')\n\nconst docsLayoutStyles = `\n[data-payload-markdown-docs-layout] {\n display: grid;\n gap: 2.5rem;\n grid-template-columns: minmax(0, 1fr);\n}\n\n@media (min-width: 1024px) {\n [data-payload-markdown-docs-layout=\"with-sidebar\"] {\n grid-template-columns: 16rem minmax(0, 1fr);\n }\n}\n`\n\nconst getDocsLayoutStyle = (hasHero: boolean): CSSProperties =>\n hasHero\n ? {}\n : {\n marginTop: '6rem',\n }\n\nconst renderSidebarItems = (\n items: PayloadMarkdownDocsSidebarItem[],\n activeRoute: string,\n depth = 0,\n): ReactNode => {\n if (items.length === 0) {\n return null\n }\n\n return (\n <ul\n className={cx(depth === 0 ? 'space-y-1' : 'ml-3 mt-1 space-y-1 border-l border-border pl-3')}\n >\n {items.map((item) => {\n const isActive = item.route === activeRoute\n const labelClassName = cx(\n 'block rounded-lg px-3 py-2 text-sm leading-5 transition-colors',\n item.route\n ? isActive\n ? 'bg-cyan-400/10 text-cyan-200'\n : 'text-foreground/70 hover:bg-white/[0.04] hover:text-foreground'\n : 'text-foreground/55',\n )\n\n return (\n <li key={item.route ?? item.sourcePath}>\n {item.route ? (\n <a className={labelClassName} href={item.route}>\n {item.label}\n </a>\n ) : (\n <span className={labelClassName}>{item.label}</span>\n )}\n {item.children ? renderSidebarItems(item.children, activeRoute, depth + 1) : null}\n </li>\n )\n })}\n </ul>\n )\n}\n\nconst renderMarkdown = async ({\n collectionSlug,\n markdown,\n}: {\n collectionSlug: string\n markdown?: string\n}): Promise<ReactNode> => {\n if (!markdown?.trim()) {\n return null\n }\n\n const { MarkdownRenderer } = await import('@valkyrianlabs/payload-markdown/server')\n\n return MarkdownRenderer({\n className: 'min-w-0',\n collectionSlug,\n markdown,\n scope: 'field',\n size: 'md',\n variant: 'docs',\n wrapperClassName: 'min-w-0',\n })\n}\n\nconst DocsHeader = ({\n doc,\n docsSet,\n}: {\n doc?: ResolvedPayloadMarkdownDocsRecord\n docsSet: ResolvedPayloadMarkdownDocsSet\n}) => {\n const description = doc?.description ?? docsSet.description\n const title = doc?.title ?? docsSet.title\n\n return (\n <header className=\"mb-10 border-b border-border pb-8\">\n <h1 className=\"text-4xl font-semibold tracking-tight text-foreground md:text-5xl\">{title}</h1>\n {description ? (\n <p className=\"mt-4 max-w-3xl text-lg leading-8 text-foreground/70\">{description}</p>\n ) : null}\n </header>\n )\n}\n\nconst DocsHero = ({\n heroImage,\n title,\n}: {\n heroImage?: PayloadMarkdownDocsHeroImage\n title: string\n}) => {\n if (!heroImage) {\n return null\n }\n\n return (\n <figure\n className=\"mb-10 overflow-hidden rounded-xl border border-border bg-white/[0.03]\"\n data-payload-markdown-docs-hero\n style={{\n borderRadius: '0.75rem',\n marginBottom: '2.5rem',\n overflow: 'hidden',\n }}\n >\n <img\n alt={heroImage.alt ?? title}\n className=\"block h-auto w-full\"\n height={heroImage.height}\n src={heroImage.url}\n style={{\n display: 'block',\n height: 'auto',\n width: '100%',\n }}\n width={heroImage.width}\n />\n </figure>\n )\n}\n\nexport const PayloadMarkdownDocsPage = async ({\n collectionSlug = DEFAULT_DOCS_COLLECTION_SLUG,\n renderSidebar = true,\n resolved,\n}: PayloadMarkdownDocsPageProps) => {\n if (resolved.type === 'docsGroupIndex') {\n return (\n <main\n className=\"min-h-screen bg-background text-foreground\"\n data-payload-markdown-docs-route={resolved.route}\n >\n <div className=\"mx-auto w-full max-w-6xl px-6 py-14 lg:px-8\">\n <header className=\"mb-10 border-b border-border pb-8\">\n <p className=\"mb-3 text-sm font-medium uppercase tracking-wide text-cyan-300\">Docs</p>\n <h1 className=\"text-4xl font-semibold tracking-tight md:text-5xl\">\n {resolved.group.navTitle ?? resolved.group.title}\n </h1>\n {resolved.group.description ? (\n <p className=\"mt-4 max-w-3xl text-lg leading-8 text-foreground/70\">\n {resolved.group.description}\n </p>\n ) : null}\n </header>\n {resolved.docsSets.length > 0 ? (\n <nav aria-label=\"Docs sets\">\n <ul className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {resolved.docsSets.map((docsSet) => (\n <li key={docsSet.id}>\n <a\n className=\"block rounded-xl border border-border bg-white/[0.03] p-5 transition-colors hover:bg-white/[0.06]\"\n href={docsSet.routeBase}\n >\n <span className=\"text-base font-semibold text-foreground\">\n {docsSet.navTitle ?? docsSet.title}\n </span>\n {docsSet.description ? (\n <span className=\"mt-2 block text-sm leading-6 text-foreground/65\">\n {docsSet.description}\n </span>\n ) : null}\n </a>\n </li>\n ))}\n </ul>\n </nav>\n ) : null}\n </div>\n </main>\n )\n }\n\n const markdown = await renderMarkdown({\n collectionSlug,\n markdown: resolved.doc?.content,\n })\n const hasHero = Boolean(resolved.doc?.heroImage)\n const hasSidebar = renderSidebar && resolved.sidebar.length > 0\n\n return (\n <main\n className=\"min-h-screen bg-background text-foreground\"\n data-payload-markdown-docs-route={resolved.route}\n >\n <style>{docsLayoutStyles}</style>\n <div\n className=\"mx-auto w-full max-w-7xl px-6 py-10 lg:px-8\"\n data-payload-markdown-docs-layout={hasSidebar ? 'with-sidebar' : 'default'}\n style={getDocsLayoutStyle(hasHero)}\n >\n {hasSidebar ? (\n <aside\n className=\"lg:sticky lg:top-8 lg:self-start\"\n style={{\n alignSelf: 'start',\n }}\n >\n <nav\n aria-label=\"Docs navigation\"\n className=\"rounded-xl border border-border bg-white/[0.03] p-3\"\n >\n {renderSidebarItems(resolved.sidebar, resolved.route)}\n </nav>\n </aside>\n ) : null}\n <article className=\"min-w-0 max-w-4xl\">\n <DocsHero\n heroImage={resolved.doc?.heroImage}\n title={resolved.doc?.title ?? resolved.docsSet.title}\n />\n <DocsHeader doc={resolved.doc} docsSet={resolved.docsSet} />\n {markdown}\n </article>\n </div>\n </main>\n )\n}\n"],"names":["DEFAULT_DOCS_COLLECTION_SLUG","cx","values","filter","Boolean","join","docsLayoutStyles","getDocsLayoutStyle","hasHero","marginTop","renderSidebarItems","items","activeRoute","depth","length","ul","className","map","item","isActive","route","labelClassName","li","a","href","label","span","children","sourcePath","renderMarkdown","collectionSlug","markdown","trim","MarkdownRenderer","scope","size","variant","wrapperClassName","DocsHeader","doc","docsSet","description","title","header","h1","p","DocsHero","heroImage","figure","data-payload-markdown-docs-hero","style","borderRadius","marginBottom","overflow","img","alt","height","src","url","display","width","PayloadMarkdownDocsPage","renderSidebar","resolved","type","main","data-payload-markdown-docs-route","div","group","navTitle","docsSets","nav","aria-label","routeBase","id","content","hasSidebar","sidebar","data-payload-markdown-docs-layout","aside","alignSelf","article"],"mappings":"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"}
|
package/dist/next/sidebar.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DEFAULT_DOCS_COLLECTION_SLUG, DEFAULT_MARKDOWN_FIELD_NAME } from '../constants.js';
|
|
2
|
-
import { joinRouteSegments } from '../routing/index.js';
|
|
3
2
|
import { isVisibleDocsRecord, toResolvedDocsRecord } from './records.js';
|
|
4
3
|
const titleCaseSegment = (segment)=>segment.split(/[-_]+/).filter(Boolean).map((part)=>`${part.charAt(0).toUpperCase()}${part.slice(1)}`).join(' ');
|
|
5
4
|
const getSidebarLabel = (record)=>record.overrides?.navTitle ?? record.navTitle ?? record.title ?? record.sourcePath;
|
|
@@ -11,6 +10,17 @@ const getSourcePathSegments = (sourcePath)=>{
|
|
|
11
10
|
}
|
|
12
11
|
return segments;
|
|
13
12
|
};
|
|
13
|
+
const getSidebarPath = (segments)=>segments.join('/');
|
|
14
|
+
const getFolderPaths = (records)=>{
|
|
15
|
+
const folderPaths = new Set();
|
|
16
|
+
for (const record of records){
|
|
17
|
+
const segments = getSourcePathSegments(record.sourcePath);
|
|
18
|
+
for(let index = 1; index < segments.length; index += 1){
|
|
19
|
+
folderPaths.add(getSidebarPath(segments.slice(0, index)));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return folderPaths;
|
|
23
|
+
};
|
|
14
24
|
const compareSidebarItems = (first, second)=>{
|
|
15
25
|
if (first.order !== second.order) {
|
|
16
26
|
return first.order - second.order;
|
|
@@ -25,7 +35,7 @@ const sortSidebarTree = (items)=>{
|
|
|
25
35
|
}
|
|
26
36
|
}
|
|
27
37
|
};
|
|
28
|
-
const getOrCreateFolderNode = ({ currentItems, depth,
|
|
38
|
+
const getOrCreateFolderNode = ({ currentItems, depth, order, segment, sourcePath })=>{
|
|
29
39
|
const existing = currentItems.find((item)=>item.sourcePath === sourcePath);
|
|
30
40
|
if (existing) {
|
|
31
41
|
existing.order = Math.min(existing.order, order);
|
|
@@ -36,13 +46,12 @@ const getOrCreateFolderNode = ({ currentItems, depth, docsSet, order, segment, s
|
|
|
36
46
|
depth,
|
|
37
47
|
label: titleCaseSegment(segment),
|
|
38
48
|
order,
|
|
39
|
-
route: docsSet ? joinRouteSegments(docsSet.routeBase, sourcePath) : `/${sourcePath}`,
|
|
40
49
|
sourcePath
|
|
41
50
|
};
|
|
42
51
|
currentItems.push(node);
|
|
43
52
|
return node;
|
|
44
53
|
};
|
|
45
|
-
const mergeLeafIntoTree = ({
|
|
54
|
+
const mergeLeafIntoTree = ({ folderPaths, record, rootItems })=>{
|
|
46
55
|
const segments = getSourcePathSegments(record.sourcePath);
|
|
47
56
|
if (segments.length === 0) {
|
|
48
57
|
rootItems.push({
|
|
@@ -56,15 +65,16 @@ const mergeLeafIntoTree = ({ docsSet, record, rootItems })=>{
|
|
|
56
65
|
}
|
|
57
66
|
let currentItems = rootItems;
|
|
58
67
|
for (const [index, segment] of segments.entries()){
|
|
59
|
-
const sourcePath = segments.slice(0, index + 1)
|
|
68
|
+
const sourcePath = getSidebarPath(segments.slice(0, index + 1));
|
|
60
69
|
const isLeaf = index === segments.length - 1;
|
|
61
70
|
if (isLeaf) {
|
|
62
71
|
const existing = currentItems.find((item)=>item.sourcePath === sourcePath);
|
|
72
|
+
const itemSourcePath = folderPaths.has(sourcePath) ? sourcePath : record.sourcePath;
|
|
63
73
|
if (existing) {
|
|
64
74
|
existing.label = getSidebarLabel(record);
|
|
65
75
|
existing.order = record.order;
|
|
66
76
|
existing.route = record.route;
|
|
67
|
-
existing.sourcePath =
|
|
77
|
+
existing.sourcePath = itemSourcePath;
|
|
68
78
|
existing.children ??= [];
|
|
69
79
|
return;
|
|
70
80
|
}
|
|
@@ -73,14 +83,13 @@ const mergeLeafIntoTree = ({ docsSet, record, rootItems })=>{
|
|
|
73
83
|
label: getSidebarLabel(record),
|
|
74
84
|
order: record.order,
|
|
75
85
|
route: record.route,
|
|
76
|
-
sourcePath:
|
|
86
|
+
sourcePath: itemSourcePath
|
|
77
87
|
});
|
|
78
88
|
return;
|
|
79
89
|
}
|
|
80
90
|
const folder = getOrCreateFolderNode({
|
|
81
91
|
currentItems,
|
|
82
92
|
depth: index,
|
|
83
|
-
docsSet,
|
|
84
93
|
order: record.order,
|
|
85
94
|
segment,
|
|
86
95
|
sourcePath
|
|
@@ -100,9 +109,10 @@ export const buildPayloadMarkdownDocsSidebar = (records, options = {})=>{
|
|
|
100
109
|
}
|
|
101
110
|
return first.sourcePath.localeCompare(second.sourcePath);
|
|
102
111
|
});
|
|
112
|
+
const folderPaths = getFolderPaths(visibleRecords);
|
|
103
113
|
for (const record of visibleRecords){
|
|
104
114
|
mergeLeafIntoTree({
|
|
105
|
-
|
|
115
|
+
folderPaths,
|
|
106
116
|
record,
|
|
107
117
|
rootItems: sidebar
|
|
108
118
|
});
|
package/dist/next/sidebar.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/next/sidebar.ts"],"sourcesContent":["import type {\n PayloadMarkdownDocsCollectionSlugs,\n PayloadMarkdownDocsReadPayload,\n PayloadMarkdownDocsSidebarItem,\n ResolvedPayloadMarkdownDocsRecord,\n ResolvedPayloadMarkdownDocsSet,\n} from './types.js'\n\nimport { DEFAULT_DOCS_COLLECTION_SLUG, DEFAULT_MARKDOWN_FIELD_NAME } from '../constants.js'\nimport { joinRouteSegments } from '../routing/index.js'\nimport { isVisibleDocsRecord, toResolvedDocsRecord } from './records.js'\n\nexport type BuildPayloadMarkdownDocsSidebarOptions = {\n docsSet?: ResolvedPayloadMarkdownDocsSet\n includeDrafts?: boolean\n}\n\nexport type GetPayloadMarkdownDocsSidebarOptions = {\n collections?: PayloadMarkdownDocsCollectionSlugs\n docsSet: ResolvedPayloadMarkdownDocsSet\n includeDrafts?: boolean\n markdownField?: string\n overrideAccess?: boolean\n payload: PayloadMarkdownDocsReadPayload\n}\n\nconst titleCaseSegment = (segment: string): string =>\n segment\n .split(/[-_]+/)\n .filter(Boolean)\n .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)\n .join(' ')\n\nconst getSidebarLabel = (record: ResolvedPayloadMarkdownDocsRecord): string =>\n record.overrides?.navTitle ?? record.navTitle ?? record.title ?? record.sourcePath\n\nconst getSourcePathSegments = (sourcePath: string): string[] => {\n const withoutExtension = sourcePath.replace(/\\.md$/i, '')\n const segments = withoutExtension.split('/').filter(Boolean)\n\n if (segments.at(-1) === 'index') {\n return segments.slice(0, -1)\n }\n\n return segments\n}\n\nconst compareSidebarItems = (\n first: PayloadMarkdownDocsSidebarItem,\n second: PayloadMarkdownDocsSidebarItem,\n): number => {\n if (first.order !== second.order) {\n return first.order - second.order\n }\n\n return first.sourcePath.localeCompare(second.sourcePath)\n}\n\nconst sortSidebarTree = (items: PayloadMarkdownDocsSidebarItem[]) => {\n items.sort(compareSidebarItems)\n\n for (const item of items) {\n if (item.children) {\n sortSidebarTree(item.children)\n }\n }\n}\n\nconst getOrCreateFolderNode = ({\n currentItems,\n depth,\n docsSet,\n order,\n segment,\n sourcePath,\n}: {\n currentItems: PayloadMarkdownDocsSidebarItem[]\n depth: number\n docsSet?: ResolvedPayloadMarkdownDocsSet\n order: number\n segment: string\n sourcePath: string\n}): PayloadMarkdownDocsSidebarItem => {\n const existing = currentItems.find((item) => item.sourcePath === sourcePath)\n\n if (existing) {\n existing.order = Math.min(existing.order, order)\n return existing\n }\n\n const node: PayloadMarkdownDocsSidebarItem = {\n children: [],\n depth,\n label: titleCaseSegment(segment),\n order,\n route: docsSet ? joinRouteSegments(docsSet.routeBase, sourcePath) : `/${sourcePath}`,\n sourcePath,\n }\n\n currentItems.push(node)\n\n return node\n}\n\nconst mergeLeafIntoTree = ({\n docsSet,\n record,\n rootItems,\n}: {\n docsSet?: ResolvedPayloadMarkdownDocsSet\n record: ResolvedPayloadMarkdownDocsRecord\n rootItems: PayloadMarkdownDocsSidebarItem[]\n}) => {\n const segments = getSourcePathSegments(record.sourcePath)\n\n if (segments.length === 0) {\n rootItems.push({\n depth: 0,\n label: getSidebarLabel(record),\n order: record.order,\n route: record.route,\n sourcePath: record.sourcePath,\n })\n return\n }\n\n let currentItems = rootItems\n\n for (const [index, segment] of segments.entries()) {\n const sourcePath = segments.slice(0, index + 1).join('/')\n const isLeaf = index === segments.length - 1\n\n if (isLeaf) {\n const existing = currentItems.find((item) => item.sourcePath === sourcePath)\n\n if (existing) {\n existing.label = getSidebarLabel(record)\n existing.order = record.order\n existing.route = record.route\n existing.sourcePath = record.sourcePath\n existing.children ??= []\n return\n }\n\n currentItems.push({\n depth: index,\n label: getSidebarLabel(record),\n order: record.order,\n route: record.route,\n sourcePath: record.sourcePath,\n })\n return\n }\n\n const folder = getOrCreateFolderNode({\n currentItems,\n depth: index,\n docsSet,\n order: record.order,\n segment,\n sourcePath,\n })\n\n folder.children ??= []\n currentItems = folder.children\n }\n}\n\nexport const buildPayloadMarkdownDocsSidebar = (\n records: ResolvedPayloadMarkdownDocsRecord[],\n options: BuildPayloadMarkdownDocsSidebarOptions = {},\n): PayloadMarkdownDocsSidebarItem[] => {\n const sidebar: PayloadMarkdownDocsSidebarItem[] = []\n const visibleRecords = records\n .filter((record) =>\n isVisibleDocsRecord({\n includeDrafts: options.includeDrafts,\n record,\n }),\n )\n .filter((record) => record.overrides?.hideFromNav !== true)\n .sort((first, second) => {\n if (first.order !== second.order) {\n return first.order - second.order\n }\n\n return first.sourcePath.localeCompare(second.sourcePath)\n })\n\n for (const record of visibleRecords) {\n mergeLeafIntoTree({\n docsSet: options.docsSet,\n record,\n rootItems: sidebar,\n })\n }\n\n sortSidebarTree(sidebar)\n\n return sidebar\n}\n\nexport const getPayloadMarkdownDocsSidebar = async ({\n collections,\n docsSet,\n includeDrafts = false,\n markdownField = DEFAULT_MARKDOWN_FIELD_NAME,\n // Sidebar data reads plugin-owned generated docs server-side.\n // Access is overridden here, then nav visibility is enforced explicitly.\n overrideAccess = true,\n payload,\n}: GetPayloadMarkdownDocsSidebarOptions): Promise<PayloadMarkdownDocsSidebarItem[]> => {\n const result = await payload.find({\n collection: collections?.docs ?? DEFAULT_DOCS_COLLECTION_SLUG,\n depth: 0,\n draft: includeDrafts,\n limit: 1000,\n overrideAccess,\n where: {\n docsSet: {\n equals: docsSet.id,\n },\n },\n })\n\n const records = result.docs\n .map((doc) =>\n toResolvedDocsRecord({\n doc,\n markdownField,\n }),\n )\n .filter((record): record is ResolvedPayloadMarkdownDocsRecord => record !== undefined)\n\n return buildPayloadMarkdownDocsSidebar(records, {\n docsSet,\n includeDrafts,\n })\n}\n"],"names":["DEFAULT_DOCS_COLLECTION_SLUG","DEFAULT_MARKDOWN_FIELD_NAME","joinRouteSegments","isVisibleDocsRecord","toResolvedDocsRecord","titleCaseSegment","segment","split","filter","Boolean","map","part","charAt","toUpperCase","slice","join","getSidebarLabel","record","overrides","navTitle","title","sourcePath","getSourcePathSegments","withoutExtension","replace","segments","at","compareSidebarItems","first","second","order","localeCompare","sortSidebarTree","items","sort","item","children","getOrCreateFolderNode","currentItems","depth","docsSet","existing","find","Math","min","node","label","route","routeBase","push","mergeLeafIntoTree","rootItems","length","index","entries","isLeaf","folder","buildPayloadMarkdownDocsSidebar","records","options","sidebar","visibleRecords","includeDrafts","hideFromNav","getPayloadMarkdownDocsSidebar","collections","markdownField","overrideAccess","payload","result","collection","docs","draft","limit","where","equals","id","doc","undefined"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/next/sidebar.ts"],"sourcesContent":["import type {\n PayloadMarkdownDocsCollectionSlugs,\n PayloadMarkdownDocsReadPayload,\n PayloadMarkdownDocsSidebarItem,\n ResolvedPayloadMarkdownDocsRecord,\n ResolvedPayloadMarkdownDocsSet,\n} from './types.js'\n\nimport { DEFAULT_DOCS_COLLECTION_SLUG, DEFAULT_MARKDOWN_FIELD_NAME } from '../constants.js'\nimport { isVisibleDocsRecord, toResolvedDocsRecord } from './records.js'\n\nexport type BuildPayloadMarkdownDocsSidebarOptions = {\n docsSet?: ResolvedPayloadMarkdownDocsSet\n includeDrafts?: boolean\n}\n\nexport type GetPayloadMarkdownDocsSidebarOptions = {\n collections?: PayloadMarkdownDocsCollectionSlugs\n docsSet: ResolvedPayloadMarkdownDocsSet\n includeDrafts?: boolean\n markdownField?: string\n overrideAccess?: boolean\n payload: PayloadMarkdownDocsReadPayload\n}\n\nconst titleCaseSegment = (segment: string): string =>\n segment\n .split(/[-_]+/)\n .filter(Boolean)\n .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)\n .join(' ')\n\nconst getSidebarLabel = (record: ResolvedPayloadMarkdownDocsRecord): string =>\n record.overrides?.navTitle ?? record.navTitle ?? record.title ?? record.sourcePath\n\nconst getSourcePathSegments = (sourcePath: string): string[] => {\n const withoutExtension = sourcePath.replace(/\\.md$/i, '')\n const segments = withoutExtension.split('/').filter(Boolean)\n\n if (segments.at(-1) === 'index') {\n return segments.slice(0, -1)\n }\n\n return segments\n}\n\nconst getSidebarPath = (segments: string[]): string => segments.join('/')\n\nconst getFolderPaths = (records: ResolvedPayloadMarkdownDocsRecord[]): Set<string> => {\n const folderPaths = new Set<string>()\n\n for (const record of records) {\n const segments = getSourcePathSegments(record.sourcePath)\n\n for (let index = 1; index < segments.length; index += 1) {\n folderPaths.add(getSidebarPath(segments.slice(0, index)))\n }\n }\n\n return folderPaths\n}\n\nconst compareSidebarItems = (\n first: PayloadMarkdownDocsSidebarItem,\n second: PayloadMarkdownDocsSidebarItem,\n): number => {\n if (first.order !== second.order) {\n return first.order - second.order\n }\n\n return first.sourcePath.localeCompare(second.sourcePath)\n}\n\nconst sortSidebarTree = (items: PayloadMarkdownDocsSidebarItem[]) => {\n items.sort(compareSidebarItems)\n\n for (const item of items) {\n if (item.children) {\n sortSidebarTree(item.children)\n }\n }\n}\n\nconst getOrCreateFolderNode = ({\n currentItems,\n depth,\n order,\n segment,\n sourcePath,\n}: {\n currentItems: PayloadMarkdownDocsSidebarItem[]\n depth: number\n order: number\n segment: string\n sourcePath: string\n}): PayloadMarkdownDocsSidebarItem => {\n const existing = currentItems.find((item) => item.sourcePath === sourcePath)\n\n if (existing) {\n existing.order = Math.min(existing.order, order)\n return existing\n }\n\n const node: PayloadMarkdownDocsSidebarItem = {\n children: [],\n depth,\n label: titleCaseSegment(segment),\n order,\n sourcePath,\n }\n\n currentItems.push(node)\n\n return node\n}\n\nconst mergeLeafIntoTree = ({\n folderPaths,\n record,\n rootItems,\n}: {\n folderPaths: Set<string>\n record: ResolvedPayloadMarkdownDocsRecord\n rootItems: PayloadMarkdownDocsSidebarItem[]\n}) => {\n const segments = getSourcePathSegments(record.sourcePath)\n\n if (segments.length === 0) {\n rootItems.push({\n depth: 0,\n label: getSidebarLabel(record),\n order: record.order,\n route: record.route,\n sourcePath: record.sourcePath,\n })\n return\n }\n\n let currentItems = rootItems\n\n for (const [index, segment] of segments.entries()) {\n const sourcePath = getSidebarPath(segments.slice(0, index + 1))\n const isLeaf = index === segments.length - 1\n\n if (isLeaf) {\n const existing = currentItems.find((item) => item.sourcePath === sourcePath)\n const itemSourcePath = folderPaths.has(sourcePath) ? sourcePath : record.sourcePath\n\n if (existing) {\n existing.label = getSidebarLabel(record)\n existing.order = record.order\n existing.route = record.route\n existing.sourcePath = itemSourcePath\n existing.children ??= []\n return\n }\n\n currentItems.push({\n depth: index,\n label: getSidebarLabel(record),\n order: record.order,\n route: record.route,\n sourcePath: itemSourcePath,\n })\n return\n }\n\n const folder = getOrCreateFolderNode({\n currentItems,\n depth: index,\n order: record.order,\n segment,\n sourcePath,\n })\n\n folder.children ??= []\n currentItems = folder.children\n }\n}\n\nexport const buildPayloadMarkdownDocsSidebar = (\n records: ResolvedPayloadMarkdownDocsRecord[],\n options: BuildPayloadMarkdownDocsSidebarOptions = {},\n): PayloadMarkdownDocsSidebarItem[] => {\n const sidebar: PayloadMarkdownDocsSidebarItem[] = []\n const visibleRecords = records\n .filter((record) =>\n isVisibleDocsRecord({\n includeDrafts: options.includeDrafts,\n record,\n }),\n )\n .filter((record) => record.overrides?.hideFromNav !== true)\n .sort((first, second) => {\n if (first.order !== second.order) {\n return first.order - second.order\n }\n\n return first.sourcePath.localeCompare(second.sourcePath)\n })\n const folderPaths = getFolderPaths(visibleRecords)\n\n for (const record of visibleRecords) {\n mergeLeafIntoTree({\n folderPaths,\n record,\n rootItems: sidebar,\n })\n }\n\n sortSidebarTree(sidebar)\n\n return sidebar\n}\n\nexport const getPayloadMarkdownDocsSidebar = async ({\n collections,\n docsSet,\n includeDrafts = false,\n markdownField = DEFAULT_MARKDOWN_FIELD_NAME,\n // Sidebar data reads plugin-owned generated docs server-side.\n // Access is overridden here, then nav visibility is enforced explicitly.\n overrideAccess = true,\n payload,\n}: GetPayloadMarkdownDocsSidebarOptions): Promise<PayloadMarkdownDocsSidebarItem[]> => {\n const result = await payload.find({\n collection: collections?.docs ?? DEFAULT_DOCS_COLLECTION_SLUG,\n depth: 0,\n draft: includeDrafts,\n limit: 1000,\n overrideAccess,\n where: {\n docsSet: {\n equals: docsSet.id,\n },\n },\n })\n\n const records = result.docs\n .map((doc) =>\n toResolvedDocsRecord({\n doc,\n markdownField,\n }),\n )\n .filter((record): record is ResolvedPayloadMarkdownDocsRecord => record !== undefined)\n\n return buildPayloadMarkdownDocsSidebar(records, {\n docsSet,\n includeDrafts,\n })\n}\n"],"names":["DEFAULT_DOCS_COLLECTION_SLUG","DEFAULT_MARKDOWN_FIELD_NAME","isVisibleDocsRecord","toResolvedDocsRecord","titleCaseSegment","segment","split","filter","Boolean","map","part","charAt","toUpperCase","slice","join","getSidebarLabel","record","overrides","navTitle","title","sourcePath","getSourcePathSegments","withoutExtension","replace","segments","at","getSidebarPath","getFolderPaths","records","folderPaths","Set","index","length","add","compareSidebarItems","first","second","order","localeCompare","sortSidebarTree","items","sort","item","children","getOrCreateFolderNode","currentItems","depth","existing","find","Math","min","node","label","push","mergeLeafIntoTree","rootItems","route","entries","isLeaf","itemSourcePath","has","folder","buildPayloadMarkdownDocsSidebar","options","sidebar","visibleRecords","includeDrafts","hideFromNav","getPayloadMarkdownDocsSidebar","collections","docsSet","markdownField","overrideAccess","payload","result","collection","docs","draft","limit","where","equals","id","doc","undefined"],"mappings":"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"}
|
package/dist/next/types.d.ts
CHANGED
package/dist/next/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/next/types.ts"],"sourcesContent":["import type { DocsAiExportManifest } from '../sync/index.js'\n\nexport type PayloadMarkdownDocsFindArgs = {\n collection: string\n depth?: number\n draft?: boolean\n limit?: number\n overrideAccess?: boolean\n sort?: string\n where?: unknown\n}\n\nexport type PayloadMarkdownDocsReadPayload = {\n find: (args: PayloadMarkdownDocsFindArgs) => Promise<{\n docs: unknown[]\n }>\n}\n\nexport type PayloadMarkdownDocsCollectionSlugs = {\n docs?: string\n docsGroups?: string\n docsSets?: string\n}\n\nexport type ResolvePayloadMarkdownDocsRouteOptions = {\n collections?: PayloadMarkdownDocsCollectionSlugs\n includeDrafts?: boolean\n markdownField?: string\n overrideAccess?: boolean\n path?: string\n payload: PayloadMarkdownDocsReadPayload\n slug?: string | string[]\n}\n\nexport type PayloadMarkdownDocsDefaults = {\n sidebarMode?: 'auto' | 'hidden' | 'manual'\n}\n\nexport type PayloadMarkdownDocsOverrides = {\n hideFromNav?: boolean\n navTitle?: string\n}\n\nexport type PayloadMarkdownDocsHeroImage = {\n alt?: string\n height?: number\n id?: string\n relationTo?: string\n url: string\n width?: number\n}\n\nexport type ResolvedPayloadMarkdownDocsSet = {\n aiExport?: DocsAiExportManifest\n defaults?: PayloadMarkdownDocsDefaults\n description?: string\n id: string\n navTitle?: string\n order: number\n routeBase: string\n slug?: string\n status?: 'draft' | 'published'\n title: string\n}\n\nexport type ResolvedPayloadMarkdownDocsGroup = {\n description?: string\n id: string\n navTitle?: string\n order: number\n routePath: string\n serveIndex: boolean\n slug?: string\n title: string\n}\n\nexport type ResolvedPayloadMarkdownDocsRecord = {\n archived: boolean\n content?: string\n depth: number\n description?: string\n docsSetId?: string\n heroImage?: PayloadMarkdownDocsHeroImage\n id: string\n navTitle?: string\n order: number\n overrides?: PayloadMarkdownDocsOverrides\n route: string\n sourceHash?: string\n sourcePath: string\n status?: 'draft' | 'published'\n title: string\n}\n\nexport type PayloadMarkdownDocsSidebarItem = {\n children?: PayloadMarkdownDocsSidebarItem[]\n depth: number\n hidden?: boolean\n label: string\n order: number\n route
|
|
1
|
+
{"version":3,"sources":["../../src/next/types.ts"],"sourcesContent":["import type { DocsAiExportManifest } from '../sync/index.js'\n\nexport type PayloadMarkdownDocsFindArgs = {\n collection: string\n depth?: number\n draft?: boolean\n limit?: number\n overrideAccess?: boolean\n sort?: string\n where?: unknown\n}\n\nexport type PayloadMarkdownDocsReadPayload = {\n find: (args: PayloadMarkdownDocsFindArgs) => Promise<{\n docs: unknown[]\n }>\n}\n\nexport type PayloadMarkdownDocsCollectionSlugs = {\n docs?: string\n docsGroups?: string\n docsSets?: string\n}\n\nexport type ResolvePayloadMarkdownDocsRouteOptions = {\n collections?: PayloadMarkdownDocsCollectionSlugs\n includeDrafts?: boolean\n markdownField?: string\n overrideAccess?: boolean\n path?: string\n payload: PayloadMarkdownDocsReadPayload\n slug?: string | string[]\n}\n\nexport type PayloadMarkdownDocsDefaults = {\n sidebarMode?: 'auto' | 'hidden' | 'manual'\n}\n\nexport type PayloadMarkdownDocsOverrides = {\n hideFromNav?: boolean\n navTitle?: string\n}\n\nexport type PayloadMarkdownDocsHeroImage = {\n alt?: string\n height?: number\n id?: string\n relationTo?: string\n url: string\n width?: number\n}\n\nexport type ResolvedPayloadMarkdownDocsSet = {\n aiExport?: DocsAiExportManifest\n defaults?: PayloadMarkdownDocsDefaults\n description?: string\n id: string\n navTitle?: string\n order: number\n routeBase: string\n slug?: string\n status?: 'draft' | 'published'\n title: string\n}\n\nexport type ResolvedPayloadMarkdownDocsGroup = {\n description?: string\n id: string\n navTitle?: string\n order: number\n routePath: string\n serveIndex: boolean\n slug?: string\n title: string\n}\n\nexport type ResolvedPayloadMarkdownDocsRecord = {\n archived: boolean\n content?: string\n depth: number\n description?: string\n docsSetId?: string\n heroImage?: PayloadMarkdownDocsHeroImage\n id: string\n navTitle?: string\n order: number\n overrides?: PayloadMarkdownDocsOverrides\n route: string\n sourceHash?: string\n sourcePath: string\n status?: 'draft' | 'published'\n title: string\n}\n\nexport type PayloadMarkdownDocsSidebarItem = {\n children?: PayloadMarkdownDocsSidebarItem[]\n depth: number\n hidden?: boolean\n label: string\n order: number\n route?: string\n sourcePath: string\n}\n\nexport type ResolvedPayloadMarkdownDocsRoute =\n | {\n doc: ResolvedPayloadMarkdownDocsRecord\n docsSet: ResolvedPayloadMarkdownDocsSet\n route: string\n sidebar: PayloadMarkdownDocsSidebarItem[]\n type: 'doc'\n }\n | {\n doc?: ResolvedPayloadMarkdownDocsRecord\n docsSet: ResolvedPayloadMarkdownDocsSet\n route: string\n sidebar: PayloadMarkdownDocsSidebarItem[]\n type: 'docsSetIndex'\n }\n | {\n docsSets: ResolvedPayloadMarkdownDocsSet[]\n group: ResolvedPayloadMarkdownDocsGroup\n route: string\n type: 'docsGroupIndex'\n }\n\nexport type PayloadMarkdownDocsMetadata = {\n description?: string\n title?: string\n}\n"],"names":[],"mappings":"AA8HA,WAGC"}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: payload-markdown-docs
|
|
3
|
+
description: Use this skill when maintaining Git-backed documentation for a project that uses `@valkyrianlabs/payload-markdown-docs`.
|
|
4
|
+
|
|
1
5
|
# Payload Markdown Docs Skill
|
|
2
6
|
|
|
3
7
|
Use this skill when maintaining Git-backed documentation for a project that uses `@valkyrianlabs/payload-markdown-docs`.
|
|
@@ -11,8 +15,10 @@ The docs source lives in `{{docsRoot}}` unless the user says otherwise. Edit Mar
|
|
|
11
15
|
- Use supported frontmatter only.
|
|
12
16
|
- Keep internal docs links root-relative inside the docs set, such as `/getting-started/quick-start`.
|
|
13
17
|
- Use `payload-markdown` directives only when they are supported.
|
|
18
|
+
- Keep formatting plain Markdown: supported frontmatter, one H1, clear H2/H3 sections, root-relative links, and supported directives.
|
|
14
19
|
- Do not invent directives, frontmatter fields, CLI flags, sync modes, or runtime features.
|
|
15
20
|
- Do not describe unsupported features as implemented.
|
|
21
|
+
- Do not add MDX, arbitrary YAML objects, inline frontmatter arrays, HTML widgets, or one Payload Page per Markdown file.
|
|
16
22
|
- Run validation before finishing docs edits.
|
|
17
23
|
- Treat sync and publishing as CMS/server-owned. The request may ask; Payload
|
|
18
24
|
docs sets and plugin config decide.
|
|
@@ -39,6 +45,7 @@ Rules for `index.ai.yml`:
|
|
|
39
45
|
- Do not show the manifest in human docs navigation.
|
|
40
46
|
- Do not render the manifest as a normal docs page.
|
|
41
47
|
- Use it as the canonical ordering source for the `.md` export.
|
|
48
|
+
- Remember that the `.md` export is served by a Next route handler, not by a generated Payload Page.
|
|
42
49
|
- Avoid backend hand-sorting unless the project already has a docs sort field.
|
|
43
50
|
|
|
44
51
|
Ordering source preference:
|
|
@@ -154,6 +161,7 @@ Sync writes require `sync.allowWrites: true`. Publishing additionally requires `
|
|
|
154
161
|
## References
|
|
155
162
|
|
|
156
163
|
- `reference/payload-markdown-directives.md`
|
|
164
|
+
- `reference/formatting.md`
|
|
157
165
|
- `reference/frontmatter.md`
|
|
158
166
|
- `reference/workflow.md`
|
|
159
167
|
- `reference/sync.md`
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Formatting
|
|
2
|
+
|
|
3
|
+
Keep docs as plain Markdown that validates before sync.
|
|
4
|
+
|
|
5
|
+
Expected shape:
|
|
6
|
+
|
|
7
|
+
- supported frontmatter at the top
|
|
8
|
+
- one H1 for the page title
|
|
9
|
+
- H2 and H3 sections for structure
|
|
10
|
+
- root-relative internal links, such as `/workflow/publishing`
|
|
11
|
+
- supported `payload-markdown` directives only
|
|
12
|
+
- `index.ai.yml` updated when pages are added, moved, renamed, or removed
|
|
13
|
+
|
|
14
|
+
Do not add:
|
|
15
|
+
|
|
16
|
+
- `.mdx` files
|
|
17
|
+
- arbitrary YAML objects
|
|
18
|
+
- inline frontmatter arrays
|
|
19
|
+
- invented directive names or props
|
|
20
|
+
- HTML scripts, iframes, or client-side widgets
|
|
21
|
+
- one Payload Page per Markdown file
|
|
22
|
+
- production docs domains for internal links
|
|
23
|
+
|
|
24
|
+
Prefer short, concrete sections over long pages with deeply nested headings.
|
|
@@ -35,5 +35,7 @@ Rules:
|
|
|
35
35
|
- `tags` and `redirectFrom` must use list item syntax.
|
|
36
36
|
- `slug` may contain letters, numbers, and hyphens.
|
|
37
37
|
- Avoid arbitrary nested YAML objects.
|
|
38
|
+
- Avoid inline arrays such as `tags: [getting-started]`.
|
|
38
39
|
- Avoid unsupported fields unless the user accepts validation warnings.
|
|
39
40
|
- Explicit `title` is preferred even though title fallback exists.
|
|
41
|
+
- Use `slug` only to override the final route segment; move files to change route hierarchy.
|
|
@@ -34,3 +34,11 @@ Do not hardcode production docs domains for internal navigation.
|
|
|
34
34
|
## Route Adapter
|
|
35
35
|
|
|
36
36
|
The `/next` export can resolve docs routes and let an app fall back to normal Pages rendering when no docs route matches. It does not mutate Pages.
|
|
37
|
+
|
|
38
|
+
## Raw Markdown
|
|
39
|
+
|
|
40
|
+
The AI-facing `.md` export is served by a Next route handler. It is not a
|
|
41
|
+
generated Payload Page and cannot be returned from a `page.tsx` catch-all.
|
|
42
|
+
|
|
43
|
+
Use `createPayloadMarkdownDocsMarkdownResponse` at the output path from
|
|
44
|
+
`index.ai.yml`, or place AI exports in a dedicated namespace such as `/ai`.
|
package/package.json
CHANGED