@writedocs/generator 0.4.7 → 0.4.9
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/astro.config.mjs +13 -0
- package/bin/writedocs.js +167 -139
- package/package.json +2 -1
- package/src/components/AppIcon.astro +14 -2
- package/src/components/Color.astro +61 -0
- package/src/components/ColorItem.astro +93 -0
- package/src/components/ColorRow.astro +39 -0
- package/src/components/GitHubRepo.astro +156 -0
- package/src/components/Panel.astro +11 -0
- package/src/components/Prompt.astro +143 -0
- package/src/components/Tile.astro +65 -0
- package/src/components/Tree.astro +213 -0
- package/src/components/TreeFile.astro +17 -0
- package/src/components/TreeFolder.astro +28 -0
- package/src/components/Update.astro +171 -0
- package/src/components/View.astro +214 -0
- package/src/components/Visibility.astro +11 -0
- package/src/components/compound.ts +21 -0
- package/src/components/index.ts +7 -0
- package/src/content.config.ts +6 -127
- package/src/lib/config-schema.js +124 -0
- package/src/lib/config-schema.ts +1574 -1437
- package/src/lib/config.ts +7 -140
- package/src/lib/content-check.js +232 -0
- package/src/lib/icons.js +109 -0
- package/src/lib/inline-markdown.js +29 -0
- package/src/lib/mdx-inject-builtins.js +16 -3
- package/src/lib/mdx-mintlify.js +99 -0
- package/src/lib/mdx-title-anchor-ids.js +7 -2
- package/src/lib/mdx-unknown-components.js +96 -0
- package/src/lib/pages.js +78 -0
- package/src/lib/visibility.js +29 -0
- package/src/pages/[...slug].astro +23 -1
- package/src/pages/[...slug].md.ts +4 -1
- package/src/pages/llms-full.txt.ts +3 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { visit } from 'unist-util-visit';
|
|
2
|
+
|
|
3
|
+
// Remark plugins for Mintlify components whose input can't be read from a
|
|
4
|
+
// component's own props/slot at render time. Registered in
|
|
5
|
+
// astro.config.mjs's markdown processor, next to the other remark plugins.
|
|
6
|
+
|
|
7
|
+
const TREE_NAMES = new Set(['Tree', 'FileTree']);
|
|
8
|
+
|
|
9
|
+
function textOf(node) {
|
|
10
|
+
if (!node) return '';
|
|
11
|
+
if (node.type === 'text' || node.type === 'inlineCode') return node.value;
|
|
12
|
+
return (node.children ?? []).map(textOf).join('');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function jsxAttr(name, value) {
|
|
16
|
+
return { type: 'mdxJsxAttribute', name, value };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function listToTreeNodes(list) {
|
|
20
|
+
return list.children
|
|
21
|
+
.filter((item) => item.type === 'listItem')
|
|
22
|
+
.map((item) => {
|
|
23
|
+
const label = textOf(item.children.find((c) => c.type === 'paragraph')).trim();
|
|
24
|
+
const nested = item.children.find((c) => c.type === 'list');
|
|
25
|
+
const isFolder = label.endsWith('/') || Boolean(nested);
|
|
26
|
+
const name = label.replace(/\/+$/, '');
|
|
27
|
+
if (!isFolder) {
|
|
28
|
+
return { type: 'mdxJsxFlowElement', name: 'Tree.File', attributes: [jsxAttr('name', name)], children: [] };
|
|
29
|
+
}
|
|
30
|
+
const children = nested ? listToTreeNodes(nested) : [];
|
|
31
|
+
const attributes = [jsxAttr('name', name)];
|
|
32
|
+
// "Folders that contain nested items expand by default" - Mintlify's
|
|
33
|
+
// rule for the list syntax.
|
|
34
|
+
if (children.length > 0) attributes.push(jsxAttr('defaultOpen', null));
|
|
35
|
+
return { type: 'mdxJsxFlowElement', name: 'Tree.Folder', attributes, children };
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Mintlify's Markdown-list form of <Tree>/<FileTree>:
|
|
41
|
+
*
|
|
42
|
+
* <FileTree>
|
|
43
|
+
*
|
|
44
|
+
* - docs/
|
|
45
|
+
* - index.mdx
|
|
46
|
+
* - docs.config.ts
|
|
47
|
+
*
|
|
48
|
+
* </FileTree>
|
|
49
|
+
*
|
|
50
|
+
* A trailing slash or nested items make an entry a folder; formatting in a
|
|
51
|
+
* name (`code`, **bold**) is dropped. Rewritten here into the same
|
|
52
|
+
* <Tree.Folder>/<Tree.File> elements the component form uses, so Tree.astro
|
|
53
|
+
* only ever renders one shape.
|
|
54
|
+
*/
|
|
55
|
+
export function remarkMintlifyTreeLists() {
|
|
56
|
+
return (tree) => {
|
|
57
|
+
visit(tree, 'mdxJsxFlowElement', (node) => {
|
|
58
|
+
if (!TREE_NAMES.has(node.name)) return;
|
|
59
|
+
if (!node.children.some((c) => c.type === 'list')) return;
|
|
60
|
+
node.children = node.children.flatMap((c) => (c.type === 'list' ? listToTreeNodes(c) : [c]));
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function dedent(text) {
|
|
66
|
+
const lines = text.split('\n');
|
|
67
|
+
const indents = lines.filter((l) => l.trim()).map((l) => l.match(/^[ \t]*/)[0].length);
|
|
68
|
+
const min = indents.length ? Math.min(...indents) : 0;
|
|
69
|
+
return lines
|
|
70
|
+
.map((l) => l.slice(min))
|
|
71
|
+
.join('\n')
|
|
72
|
+
.trim();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Mintlify's <Prompt> copies (and sends to Cursor) the prompt's own source
|
|
77
|
+
* text - Markdown included, e.g. "- Use second-person voice" - not the
|
|
78
|
+
* rendered HTML's plain text. A component only receives the rendered slot,
|
|
79
|
+
* so the source is read here, from the file between the element's first
|
|
80
|
+
* and last child, and passed down as a hidden `_text` prop.
|
|
81
|
+
*/
|
|
82
|
+
export function remarkMintlifyPromptText() {
|
|
83
|
+
return (tree, file) => {
|
|
84
|
+
const source = String(file.value ?? '');
|
|
85
|
+
visit(tree, 'mdxJsxFlowElement', (node) => {
|
|
86
|
+
if (node.name !== 'Prompt' || node.children.length === 0) return;
|
|
87
|
+
if (node.attributes.some((a) => a.type === 'mdxJsxAttribute' && a.name === '_text')) return;
|
|
88
|
+
const start = node.children[0].position?.start?.offset;
|
|
89
|
+
const end = node.children[node.children.length - 1].position?.end?.offset;
|
|
90
|
+
if (typeof start !== 'number' || typeof end !== 'number') return;
|
|
91
|
+
// Back up to the start of the first child's line, so its own
|
|
92
|
+
// indentation is part of what dedent() measures - but only over
|
|
93
|
+
// whitespace, never into the <Prompt ...> tag on the same line.
|
|
94
|
+
const lineStart = source.lastIndexOf('\n', start - 1) + 1;
|
|
95
|
+
const from = /^[ \t]*$/.test(source.slice(lineStart, start)) ? lineStart : start;
|
|
96
|
+
node.attributes.push(jsxAttr('_text', dedent(source.slice(from, end))));
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
}
|
|
@@ -8,7 +8,11 @@ import { visit } from 'unist-util-visit';
|
|
|
8
8
|
// (Card, Tabs, AccordionGroup, etc. don't). AccordionGroup is deliberately
|
|
9
9
|
// excluded - it has no `title` of its own, just wraps Accordion children
|
|
10
10
|
// that each get their own entry here independently.
|
|
11
|
-
const TITLED_COMPONENT_NAMES = ['Callout', 'Note', 'Info', 'Tip', 'Warning', 'Danger', 'Check', 'Accordion'];
|
|
11
|
+
const TITLED_COMPONENT_NAMES = ['Callout', 'Note', 'Info', 'Tip', 'Warning', 'Danger', 'Check', 'Accordion', 'Update'];
|
|
12
|
+
|
|
13
|
+
// The attribute each one's anchor text comes from - `title`, except
|
|
14
|
+
// Mintlify's <Update>, whose anchor is its `label`.
|
|
15
|
+
const ANCHOR_ATTRIBUTE = { Update: 'label' };
|
|
12
16
|
|
|
13
17
|
// Deliberately the same slugify Callout.astro/Accordion.astro themselves
|
|
14
18
|
// fall back to (see their own comments) - kept as a literal duplicate
|
|
@@ -67,8 +71,9 @@ export function remarkTitleAnchorIds() {
|
|
|
67
71
|
const seen = new Map();
|
|
68
72
|
visit(tree, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node) => {
|
|
69
73
|
if (!node.name || !TITLED_COMPONENT_NAMES.includes(node.name)) return;
|
|
74
|
+
const attrName = ANCHOR_ATTRIBUTE[node.name] ?? 'title';
|
|
70
75
|
const titleAttr = node.attributes?.find(
|
|
71
|
-
(attr) => attr.type === 'mdxJsxAttribute' && attr.name ===
|
|
76
|
+
(attr) => attr.type === 'mdxJsxAttribute' && attr.name === attrName
|
|
72
77
|
);
|
|
73
78
|
if (!titleAttr || typeof titleAttr.value !== 'string') return;
|
|
74
79
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { visit } from 'unist-util-visit';
|
|
3
|
+
import { BUILTIN_COMPONENT_NAMES } from './mdx-inject-builtins.js';
|
|
4
|
+
|
|
5
|
+
// Which JSX elements in an MDX file are components writedocs can't resolve
|
|
6
|
+
// - neither a built-in nor something the file imports or defines itself.
|
|
7
|
+
// MDX fails the whole build on one ("Expected component `X` to be
|
|
8
|
+
// defined"), which turned every custom component in a migrated Mintlify
|
|
9
|
+
// project into a build-breaking hunt, one component per build. Shared by
|
|
10
|
+
// the build (remarkUnknownComponentFallback below) and `writedocs
|
|
11
|
+
// validate` (lib/content-check.js), so both agree on exactly which
|
|
12
|
+
// elements are unknown. Plain JavaScript so validate can load it with
|
|
13
|
+
// plain Node.
|
|
14
|
+
|
|
15
|
+
// Dotted built-ins: the root and the parts it has (see
|
|
16
|
+
// src/components/compound.ts). `<Tree.Leaf>` is unknown even though
|
|
17
|
+
// `Tree` isn't.
|
|
18
|
+
const BUILTIN_MEMBERS = {
|
|
19
|
+
Tree: ['Folder', 'File'],
|
|
20
|
+
FileTree: ['Folder', 'File'],
|
|
21
|
+
Color: ['Row', 'Item'],
|
|
22
|
+
GitHub: ['Repo'],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function localBindings(tree) {
|
|
26
|
+
const names = new Set();
|
|
27
|
+
visit(tree, 'mdxjsEsm', (node) => {
|
|
28
|
+
for (const stmt of node.data?.estree?.body ?? []) {
|
|
29
|
+
if (stmt.type === 'ImportDeclaration') {
|
|
30
|
+
for (const s of stmt.specifiers) if (s.local?.name) names.add(s.local.name);
|
|
31
|
+
} else if (stmt.type === 'ExportNamedDeclaration') {
|
|
32
|
+
const decl = stmt.declaration;
|
|
33
|
+
if (decl?.type === 'VariableDeclaration') {
|
|
34
|
+
for (const d of decl.declarations) if (d.id?.type === 'Identifier') names.add(d.id.name);
|
|
35
|
+
} else if (decl?.id?.name) {
|
|
36
|
+
names.add(decl.id.name); // export function X / export class X
|
|
37
|
+
}
|
|
38
|
+
for (const s of stmt.specifiers ?? []) if (s.exported?.name) names.add(s.exported.name);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
return names;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Every JSX element in an MDX tree that names a component this file can't
|
|
46
|
+
* resolve, in document order: [{ node, name, line, column }]. Lowercase
|
|
47
|
+
* names are plain HTML elements and never count. */
|
|
48
|
+
export function findUnknownComponents(tree) {
|
|
49
|
+
const bound = localBindings(tree);
|
|
50
|
+
const unknown = [];
|
|
51
|
+
visit(tree, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node) => {
|
|
52
|
+
if (!node.name) return; // a fragment, <>...</>
|
|
53
|
+
const [root, ...members] = node.name.split('.');
|
|
54
|
+
if (!/^[A-Z]/.test(root)) return; // <div>, <svg>, <foo.bar> - HTML or a lowercase member expression
|
|
55
|
+
if (bound.has(root)) return;
|
|
56
|
+
if (BUILTIN_COMPONENT_NAMES.includes(root)) {
|
|
57
|
+
if (members.length === 0) return;
|
|
58
|
+
if (members.length === 1 && BUILTIN_MEMBERS[root]?.includes(members[0])) return;
|
|
59
|
+
}
|
|
60
|
+
unknown.push({
|
|
61
|
+
node,
|
|
62
|
+
name: node.name,
|
|
63
|
+
line: node.position?.start?.line,
|
|
64
|
+
column: node.position?.start?.column,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
return unknown;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Remark plugin: an unknown component renders its children and nothing
|
|
72
|
+
* else - the element becomes a fragment - with a warning naming the file
|
|
73
|
+
* and line, instead of failing the build. The site still builds and reads
|
|
74
|
+
* sensibly (the component's text is still there), and the warning, plus
|
|
75
|
+
* `writedocs validate`, say what to fix.
|
|
76
|
+
*/
|
|
77
|
+
export function remarkUnknownComponentFallback() {
|
|
78
|
+
return (tree, file) => {
|
|
79
|
+
const found = findUnknownComponents(tree);
|
|
80
|
+
if (found.length === 0) return;
|
|
81
|
+
const contentDir = process.env.WRITEDOCS_CONTENT_DIR;
|
|
82
|
+
const filePath = file.path
|
|
83
|
+
? contentDir
|
|
84
|
+
? path.relative(contentDir, file.path).split(path.sep).join('/')
|
|
85
|
+
: file.path
|
|
86
|
+
: '(unknown file)';
|
|
87
|
+
for (const { node, name, line } of found) {
|
|
88
|
+
console.warn(
|
|
89
|
+
`[writedocs] ${filePath}${line ? `:${line}` : ''} - unknown component <${name}>, showing only its content. ` +
|
|
90
|
+
'Remove it, or define it in a snippet.'
|
|
91
|
+
);
|
|
92
|
+
node.name = null;
|
|
93
|
+
node.attributes = [];
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
package/src/lib/pages.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Which files in a content directory are pages. Plain JavaScript, not
|
|
2
|
+
// TypeScript, so `writedocs validate` can load it with plain Node (Node
|
|
3
|
+
// refuses to strip types under node_modules - see lib/icons.js's comment);
|
|
4
|
+
// lib/config.ts re-exports findAllPages() for everything inside Astro.
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import matter from 'gray-matter';
|
|
8
|
+
|
|
9
|
+
// Top-level folders of a content directory that are never scanned for
|
|
10
|
+
// pages - build output, dependencies, and static assets.
|
|
11
|
+
export const EXCLUDED_TOP_LEVEL_DIRS = new Set(['node_modules', 'dist', '.astro', '.writedocs', 'public', '.git']);
|
|
12
|
+
|
|
13
|
+
/** Recursively finds every .md/.mdx file under `contentDir` that has a
|
|
14
|
+
* frontmatter block, skipping the handful of build/dependency
|
|
15
|
+
* directories a real content directory tends to also contain (see
|
|
16
|
+
* EXCLUDED_TOP_LEVEL_DIRS above) - docs/ is scanned exactly like any
|
|
17
|
+
* other folder, no special-casing. Returns POSIX-relative paths (from
|
|
18
|
+
* `contentDir`) suitable to hand straight to Astro's `glob()` loader as
|
|
19
|
+
* a literal `pattern` array - see content.config.ts's `pages`
|
|
20
|
+
* collection, and [...slug].astro, which needs the identical list to
|
|
21
|
+
* decide whether calling `getCollection('pages')` is worth doing at all
|
|
22
|
+
* (see content.config.ts's own comment on why an empty collection still
|
|
23
|
+
* needs to exist, just backed by a no-op loader, to avoid Astro's "does
|
|
24
|
+
* not exist or is empty" warning). Also reused directly by
|
|
25
|
+
* astro.config.mjs's noindex/sitemap scan and by `writedocs validate`, so
|
|
26
|
+
* both always see exactly the same file set that actually becomes a page.
|
|
27
|
+
* Synchronous and re-run from scratch wherever it's called rather than
|
|
28
|
+
* cached and shared across modules - cheap enough in practice (a docs
|
|
29
|
+
* site's own file count) not to matter. */
|
|
30
|
+
export function findAllPages(contentDir) {
|
|
31
|
+
const results = [];
|
|
32
|
+
function walk(dir, relBase) {
|
|
33
|
+
let entries;
|
|
34
|
+
try {
|
|
35
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
36
|
+
} catch {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
const rel = relBase ? `${relBase}/${entry.name}` : entry.name;
|
|
41
|
+
const abs = path.join(dir, entry.name);
|
|
42
|
+
if (entry.isDirectory()) {
|
|
43
|
+
if (relBase === '' && EXCLUDED_TOP_LEVEL_DIRS.has(entry.name)) continue;
|
|
44
|
+
walk(abs, rel);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (!entry.isFile() || !/\.mdx?$/i.test(entry.name)) continue;
|
|
48
|
+
let raw;
|
|
49
|
+
try {
|
|
50
|
+
raw = fs.readFileSync(abs, 'utf-8');
|
|
51
|
+
} catch {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
let data;
|
|
55
|
+
try {
|
|
56
|
+
({ data } = matter(raw));
|
|
57
|
+
} catch {
|
|
58
|
+
// Frontmatter that isn't valid YAML: still a page (it opens a
|
|
59
|
+
// frontmatter block), so the error surfaces with this file's name -
|
|
60
|
+
// from `writedocs validate`, or Astro's own loader in the build -
|
|
61
|
+
// instead of as a bare YAML error thrown from here.
|
|
62
|
+
if (/^---\r?\n/.test(raw)) results.push(rel);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (Object.keys(data).length === 0) continue; // no frontmatter at all - not a page
|
|
66
|
+
results.push(rel);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
walk(contentDir, '');
|
|
70
|
+
return results;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The id writedocs.json's navigation refers to a page by: its path from
|
|
74
|
+
* the content directory, without the extension or a trailing `/index`
|
|
75
|
+
* (same algorithm as fileIdForEntry() in lib/config.ts). */
|
|
76
|
+
export function fileIdForPath(relativePath) {
|
|
77
|
+
return relativePath.replace(/\.mdx?$/i, '').replace(/\/index$/, '');
|
|
78
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Mintlify's <Visibility for="humans|agents"> in the page source text that
|
|
2
|
+
// writedocs serves to agents - the per-page `.md` route and llms-full.txt,
|
|
3
|
+
// which both hand out a page's raw MDX body rather than its rendered HTML.
|
|
4
|
+
// The rendered site shows `humans` content and hides `agents` content (see
|
|
5
|
+
// Visibility.astro); the text versions do the opposite: `humans` blocks are
|
|
6
|
+
// removed, and `agents` blocks are unwrapped so only their contents remain.
|
|
7
|
+
// A <Visibility> with no `for` counts as `humans`, like the component.
|
|
8
|
+
//
|
|
9
|
+
// Works on source text, not an AST, since that's what these routes serve.
|
|
10
|
+
// Nested <Visibility> blocks aren't supported (Mintlify doesn't document
|
|
11
|
+
// nesting either) - the regex pairs each opening tag with the first
|
|
12
|
+
// closing tag after it.
|
|
13
|
+
|
|
14
|
+
const VISIBILITY_BLOCK_RE =
|
|
15
|
+
/<Visibility(?:\s+for\s*=\s*(?:"(humans|agents)"|'(humans|agents)'|\{\s*["'](humans|agents)["']\s*\}))?\s*>([\s\S]*?)<\/Visibility>/g;
|
|
16
|
+
|
|
17
|
+
function dedent(text) {
|
|
18
|
+
const lines = text.replace(/^\n+|\s+$/g, '').split('\n');
|
|
19
|
+
const indents = lines.filter((l) => l.trim()).map((l) => l.match(/^[ \t]*/)[0].length);
|
|
20
|
+
const min = indents.length ? Math.min(...indents) : 0;
|
|
21
|
+
return lines.map((l) => l.slice(min)).join('\n');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function applyVisibilityForAgents(body) {
|
|
25
|
+
return body.replace(VISIBILITY_BLOCK_RE, (_, dq, sq, expr, inner) => {
|
|
26
|
+
const audience = dq ?? sq ?? expr ?? 'humans';
|
|
27
|
+
return audience === 'agents' ? dedent(inner) : '';
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -56,6 +56,13 @@ import ParamField from "../components/ParamField.astro";
|
|
|
56
56
|
import ResponseField from "../components/ResponseField.astro";
|
|
57
57
|
import Columns from "../components/Columns.astro";
|
|
58
58
|
import Tooltip from "../components/Tooltip.astro";
|
|
59
|
+
import Update from "../components/Update.astro";
|
|
60
|
+
import Tile from "../components/Tile.astro";
|
|
61
|
+
import Panel from "../components/Panel.astro";
|
|
62
|
+
import Prompt from "../components/Prompt.astro";
|
|
63
|
+
import View from "../components/View.astro";
|
|
64
|
+
import Visibility from "../components/Visibility.astro";
|
|
65
|
+
import { Tree, FileTree, Color, GitHub } from "../components/compound";
|
|
59
66
|
import ApiPlayground from "../components/ApiPlayground.astro";
|
|
60
67
|
import ApiReferencePanel from "../components/ApiReferencePanel.astro";
|
|
61
68
|
|
|
@@ -438,6 +445,16 @@ const components = {
|
|
|
438
445
|
ResponseField,
|
|
439
446
|
Columns,
|
|
440
447
|
Tooltip,
|
|
448
|
+
Update,
|
|
449
|
+
Tile,
|
|
450
|
+
Panel,
|
|
451
|
+
Prompt,
|
|
452
|
+
View,
|
|
453
|
+
Visibility,
|
|
454
|
+
Tree,
|
|
455
|
+
FileTree,
|
|
456
|
+
Color,
|
|
457
|
+
GitHub,
|
|
441
458
|
};
|
|
442
459
|
---
|
|
443
460
|
|
|
@@ -727,7 +744,12 @@ const components = {
|
|
|
727
744
|
const tocCol = document.querySelector<HTMLElement>(".wd-toc-col");
|
|
728
745
|
if (!tocCol) return; // no toc column on this page (mode !== 'default') - nothing to dock into
|
|
729
746
|
|
|
730
|
-
|
|
747
|
+
// Top-level panels only: Mintlify's <Panel> (Panel.astro) is itself a
|
|
748
|
+
// .wd-example-panel, and a RequestExample/ResponseExample inside it
|
|
749
|
+
// moves with it rather than on its own.
|
|
750
|
+
const panels = Array.from(root.querySelectorAll<HTMLElement>(".wd-example-panel")).filter(
|
|
751
|
+
(panel) => !panel.parentElement?.closest(".wd-example-panel"),
|
|
752
|
+
);
|
|
731
753
|
if (panels.length === 0) return;
|
|
732
754
|
if (tocCol.dataset.wdExampleInit) return;
|
|
733
755
|
tocCol.dataset.wdExampleInit = "true";
|
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import type { APIRoute } from 'astro';
|
|
5
5
|
import { loadDocsConfig, normalizeEntryId, findAllPages } from '../lib/config';
|
|
6
6
|
import { writedocsTempDir } from '../lib/writedocs-temp-dir.js';
|
|
7
|
+
import { applyVisibilityForAgents } from '../lib/visibility.js';
|
|
7
8
|
|
|
8
9
|
// The raw-Markdown twin of [...slug].astro: every content page is also
|
|
9
10
|
// reachable at the exact same slug with a literal ".md" suffix (e.g.
|
|
@@ -73,7 +74,9 @@ export const GET: APIRoute = ({ props }) => {
|
|
|
73
74
|
// it here means this route needs no separate file read/parse of its
|
|
74
75
|
// own, and always matches exactly what [...slug].astro rendered from
|
|
75
76
|
// (same entry, same collection query).
|
|
76
|
-
|
|
77
|
+
// Mintlify's <Visibility>: this is the agents' view of the page - see
|
|
78
|
+
// lib/visibility.js.
|
|
79
|
+
const body = applyVisibilityForAgents(entry.body ?? '');
|
|
77
80
|
const markdown = `# ${entry.data.title}\n\n${body}`;
|
|
78
81
|
return new Response(markdown, {
|
|
79
82
|
headers: { 'Content-Type': 'text/markdown; charset=utf-8' },
|
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import type { APIRoute } from 'astro';
|
|
5
5
|
import { loadDocsConfig, normalizeEntryId, findAllPages } from '../lib/config';
|
|
6
6
|
import { writedocsTempDir } from '../lib/writedocs-temp-dir.js';
|
|
7
|
+
import { applyVisibilityForAgents } from '../lib/visibility.js';
|
|
7
8
|
|
|
8
9
|
// The "everything, concatenated" half of the llms.txt pair - see
|
|
9
10
|
// llms.txt.ts (right next to this file) for the lightweight index half,
|
|
@@ -53,7 +54,8 @@ export const GET: APIRoute = async () => {
|
|
|
53
54
|
.filter((entry) => !entry.data.url)
|
|
54
55
|
.map((entry: DocsEntry) => {
|
|
55
56
|
const slug = normalizeEntryId(entry.id);
|
|
56
|
-
|
|
57
|
+
// Agents' view of <Visibility> blocks, same as the .md route.
|
|
58
|
+
const body = applyVisibilityForAgents(entry.body ?? '');
|
|
57
59
|
return { slug, title: entry.data.title, body };
|
|
58
60
|
})
|
|
59
61
|
.sort((a, b) => a.slug.localeCompare(b.slug));
|