@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 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)=>/*#__PURE__*/ _jsxs("li", {
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: cx('block rounded-lg px-3 py-2 text-sm leading-5 transition-colors', item.route === activeRoute ? 'bg-cyan-400/10 text-cyan-200' : 'text-foreground/70 hover:bg-white/[0.04] hover:text-foreground'),
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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"}
@@ -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, docsSet, order, segment, sourcePath })=>{
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 = ({ docsSet, record, rootItems })=>{
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).join('/');
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 = record.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: record.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
- docsSet: options.docsSet,
115
+ folderPaths,
106
116
  record,
107
117
  rootItems: sidebar
108
118
  });
@@ -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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"}
@@ -87,7 +87,7 @@ export type PayloadMarkdownDocsSidebarItem = {
87
87
  hidden?: boolean;
88
88
  label: string;
89
89
  order: number;
90
- route: string;
90
+ route?: string;
91
91
  sourcePath: string;
92
92
  };
93
93
  export type ResolvedPayloadMarkdownDocsRoute = {
@@ -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: 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
+ {"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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valkyrianlabs/payload-markdown-docs",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Git-backed Markdown documentation sync for Payload CMS, powered by payload-markdown.",
5
5
  "bin": {
6
6
  "payload-markdown-docs": "./dist/cli/index.js"