reallysimpledocs 1.0.0-beta.4 → 1.0.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 +59 -39
- package/package.json +11 -11
- package/src/astro/index.js +64 -98
- package/src/astro/preview-scripts.js +140 -0
- package/src/astro/preview-source.js +150 -0
- package/src/astro/rehype-basecoat-tables.js +62 -0
- package/src/components/index.js +1 -0
- package/src/css/custom.css +197 -58
- package/src/css/styles/luma.css +9 -0
- package/src/css/styles/lyra.css +9 -0
- package/src/css/styles/maia.css +9 -0
- package/src/css/styles/mira.css +9 -0
- package/src/css/styles/nova.css +9 -0
- package/src/css/styles/rhea.css +9 -0
- package/src/css/styles/sera.css +9 -0
- package/src/css/styles/vega.css +9 -0
- package/src/runtime/components/BaseLayout.astro +22 -28
- package/src/runtime/components/CommandDialog.astro +52 -35
- package/src/runtime/components/DefaultContentHeader.astro +0 -17
- package/src/runtime/components/DefaultHead.astro +110 -0
- package/src/runtime/components/DefaultHeadContent.astro +2 -0
- package/src/runtime/components/DefaultSidebarHeader.astro +1 -1
- package/src/runtime/components/DocsLayout.astro +99 -166
- package/src/runtime/components/DropdownMenu.astro +1 -1
- package/src/runtime/components/ThemeToggle.astro +21 -0
- package/src/runtime/components/mdx/Callout.astro +2 -32
- package/src/runtime/components/mdx/Card.astro +3 -8
- package/src/runtime/components/mdx/CardGrid.astro +1 -1
- package/src/runtime/components/mdx/Code.astro +21 -27
- package/src/runtime/components/mdx/CodeGroup.astro +114 -24
- package/src/runtime/components/mdx/Preview.astro +7 -13
- package/src/runtime/components/mdx/Steps.astro +1 -5
- package/src/runtime/components/mdx/Tab.astro +6 -0
- package/src/runtime/components/mdx/Tabs.astro +7 -7
- package/src/runtime/lib/code-meta.js +8 -0
- package/src/runtime/lib/docs.js +141 -119
- package/src/runtime/lib/highlight.js +1 -14
- package/src/runtime/lib/html.js +9 -0
- package/src/runtime/lib/macros.js +10 -5
- package/src/runtime/lib/markdown-export.js +240 -169
- package/src/runtime/pages/doc.astro +1 -3
- package/src/runtime/pages/llms-full.txt.ts +2 -2
- package/src/runtime/pages/llms.txt.ts +2 -2
- package/src/runtime/pages/markdown.ts +2 -2
- package/src/runtime/pages/search-index.ts +12 -3
- package/CHANGELOG.md +0 -35
- package/src/css/overrides.css +0 -1
- package/src/css/sources.css +0 -1
- package/src/runtime/lib/markdown-exporters/index.js +0 -144
- package/src/runtime/lib/mdx-source.js +0 -19
- package/src/runtime/lib/preview-source-plugin.js +0 -33
- package/src/runtime/scripts/copy-code.js +0 -130
- package/src/site/ContentHeader.astro +0 -33
package/src/runtime/lib/html.js
CHANGED
|
@@ -6,6 +6,15 @@ export function escapeHtml(value) {
|
|
|
6
6
|
.replace(/"/g, """);
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
export function decodeHtml(value) {
|
|
10
|
+
return String(value ?? "")
|
|
11
|
+
.replace(/</g, "<")
|
|
12
|
+
.replace(/>/g, ">")
|
|
13
|
+
.replace(/"/g, '"')
|
|
14
|
+
.replace(/'/g, "'")
|
|
15
|
+
.replace(/&/g, "&");
|
|
16
|
+
}
|
|
17
|
+
|
|
9
18
|
export function attrs(values = {}) {
|
|
10
19
|
return Object.entries(values)
|
|
11
20
|
.filter(([, value]) => value !== undefined && value !== null && value !== false)
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import ArrowUpRight from "lucide-static/icons/arrow-up-right.svg?raw";
|
|
2
1
|
import Search from "lucide-static/icons/search.svg?raw";
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
const externalLinkIcon = ArrowUpRight.replace('class="lucide', 'class="lucide ml-auto size-3.5 shrink-0');
|
|
2
|
+
import ArrowUpRight from "lucide-static/icons/arrow-up-right.svg?raw";
|
|
3
|
+
import { addSvgClass, attrs, escapeHtml } from "./html.js";
|
|
6
4
|
|
|
7
5
|
export function renderSidebar({
|
|
8
6
|
id = "sidebar",
|
|
@@ -61,13 +59,20 @@ export function renderSidebarContent(items = [], parentIdPrefix = "content") {
|
|
|
61
59
|
<a${attrs({ href: item.url, "aria-current": item.current ? "page" : undefined, ...without(item.attrs, ["href", "aria-current"]) })}>
|
|
62
60
|
${item.icon || ""}
|
|
63
61
|
<span>${escapeHtml(item.label)}</span>
|
|
64
|
-
${item
|
|
62
|
+
${renderSidebarMeta(item)}
|
|
65
63
|
</a>
|
|
66
64
|
</li>`;
|
|
67
65
|
})
|
|
68
66
|
.join("");
|
|
69
67
|
}
|
|
70
68
|
|
|
69
|
+
function renderSidebarMeta(item) {
|
|
70
|
+
const badge = item.badge ? `<span class="badge" data-variant="outline">${escapeHtml(item.badge)}</span>` : "";
|
|
71
|
+
const external = item.external ? addSvgClass(ArrowUpRight, "size-3 opacity-50") : "";
|
|
72
|
+
if (!badge && !external) return "";
|
|
73
|
+
return `<span class="ml-auto inline-flex items-center gap-1">${badge}${external}</span>`;
|
|
74
|
+
}
|
|
75
|
+
|
|
71
76
|
export function renderCommandDialog({
|
|
72
77
|
id = "command-search",
|
|
73
78
|
items = [],
|
|
@@ -1,200 +1,271 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const processor = unified().use(remarkParse).use(remarkGfm).use(remarkMdx);
|
|
11
|
-
|
|
12
|
-
const customSerializersCache = new Map();
|
|
13
|
-
|
|
14
|
-
export async function mdxToMarkdown(source, options = {}) {
|
|
15
|
-
const tree = processor.parse(source);
|
|
16
|
-
const state = {
|
|
17
|
-
constants: collectExportedStrings(tree),
|
|
18
|
-
customSerializers: await loadCustomSerializers(options.markdownSerializers),
|
|
19
|
-
source: String(source || ""),
|
|
20
|
-
};
|
|
21
|
-
const children = await transformBlocks(tree.children || [], state);
|
|
22
|
-
return `${stringify({ type: "root", children }).trim()}\n`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async function loadCustomSerializers(entries = {}) {
|
|
26
|
-
const cacheKey = JSON.stringify(entries || {});
|
|
27
|
-
if (customSerializersCache.has(cacheKey)) return customSerializersCache.get(cacheKey);
|
|
28
|
-
|
|
29
|
-
const serializers = {};
|
|
30
|
-
for (const [name, moduleId] of Object.entries(entries || {})) {
|
|
31
|
-
if (!moduleId) continue;
|
|
32
|
-
const module = await import(/* @vite-ignore */ moduleId);
|
|
33
|
-
const serializer = module.default || module.serialize;
|
|
34
|
-
if (typeof serializer !== "function") {
|
|
35
|
-
throw new Error(`Markdown serializer for "${name}" must export a function as default or serialize.`);
|
|
36
|
-
}
|
|
37
|
-
serializers[name] = serializer;
|
|
38
|
-
}
|
|
1
|
+
export function normalizeDocMarkdown(markdown, context = new Map()) {
|
|
2
|
+
return transformMarkdownTextSegments(markdown, (content) => {
|
|
3
|
+
return normalizeMdxComponents(stripMdxModuleSyntax(content), context);
|
|
4
|
+
})
|
|
5
|
+
.replace(/[ \t]+\n/g, "\n")
|
|
6
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
7
|
+
.trim();
|
|
8
|
+
}
|
|
39
9
|
|
|
40
|
-
|
|
41
|
-
return
|
|
10
|
+
function stripMdxModuleSyntax(content) {
|
|
11
|
+
return String(content || "")
|
|
12
|
+
.replace(/^\s*import\s+[^;\n]+;?\s*$/gm, "")
|
|
13
|
+
.replace(/^\s*export\s+(?:const|let|var|function)\s+[\s\S]*?(?=\n(?:#|[A-Z][A-Za-z]*\(|<[A-Z]|\w|\s*$))/gm, "");
|
|
42
14
|
}
|
|
43
15
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return blocks.filter(Boolean);
|
|
16
|
+
function transformMarkdownTextSegments(markdown, transform) {
|
|
17
|
+
return String(markdown || "")
|
|
18
|
+
.split(/(```[\s\S]*?```)/g)
|
|
19
|
+
.map((part) => (part.startsWith("```") ? part : transform(part)))
|
|
20
|
+
.join("");
|
|
50
21
|
}
|
|
51
22
|
|
|
52
|
-
|
|
53
|
-
|
|
23
|
+
const serializers = {
|
|
24
|
+
Code: serializeCode,
|
|
25
|
+
CodeGroup: serializeChildren,
|
|
26
|
+
Tabs: serializeChildren,
|
|
27
|
+
Tab: serializeChildren,
|
|
28
|
+
CardGrid: serializeChildren,
|
|
29
|
+
Preview: serializePreview,
|
|
30
|
+
Callout: serializeCallout,
|
|
31
|
+
Steps: serializeSteps,
|
|
32
|
+
Card: serializeCard,
|
|
33
|
+
};
|
|
54
34
|
|
|
55
|
-
|
|
35
|
+
export function normalizeMdxComponents(content, context = new Map()) {
|
|
36
|
+
let out = String(content || "");
|
|
37
|
+
const previewBlocks = [];
|
|
56
38
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
39
|
+
out = replacePairedComponent(out, "Preview", (attrs, children) => {
|
|
40
|
+
const token = `\u0000RSD_PREVIEW_${previewBlocks.length}\u0000`;
|
|
41
|
+
previewBlocks.push(serializers.Preview(attrs, children, context));
|
|
42
|
+
return token;
|
|
43
|
+
});
|
|
60
44
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (children.length === 1 && isMdxElement(children[0])) {
|
|
64
|
-
return await transformComponent(children[0], state);
|
|
65
|
-
}
|
|
45
|
+
for (const name of ["CodeGroup", "Tabs", "Tab", "CardGrid", "Callout", "Steps", "Code"]) {
|
|
46
|
+
out = replacePairedComponent(out, name, (attrs, children) => serializers[name](attrs, children, context));
|
|
66
47
|
}
|
|
67
48
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
return [clone];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async function transformComponent(node, state) {
|
|
80
|
-
return (
|
|
81
|
-
(await serializeComponent(
|
|
82
|
-
node,
|
|
83
|
-
{
|
|
84
|
-
transformBlocks: (nodes) => transformBlocks(nodes, state),
|
|
85
|
-
getAttr: (entry, name) => getAttr(entry, name, state.constants),
|
|
86
|
-
getCodeValue: (entry) => getCodeValue(entry, state.constants),
|
|
87
|
-
getChildrenSource: (entry) => getMdxChildrenSource(entry, state.source),
|
|
88
|
-
isNamedElement,
|
|
89
|
-
significantChildren,
|
|
90
|
-
},
|
|
91
|
-
state.customSerializers,
|
|
92
|
-
)) || (await transformBlocks(node.children || [], state))
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async function transformInline(nodes, state) {
|
|
97
|
-
const inline = [];
|
|
98
|
-
for (const node of nodes) {
|
|
99
|
-
if (isMdxElement(node)) {
|
|
100
|
-
const text = stringify({ type: "root", children: await transformComponent(node, state) }).trim();
|
|
101
|
-
if (text) inline.push({ type: "text", value: text });
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
if (node.type === "mdxTextExpression" || node.type === "mdxFlowExpression") {
|
|
105
|
-
const value = expressionValue(node, state.constants);
|
|
106
|
-
if (value) inline.push({ type: "text", value });
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
const clone = { ...node };
|
|
110
|
-
if (Array.isArray(clone.children)) clone.children = await transformInline(clone.children, state);
|
|
111
|
-
inline.push(clone);
|
|
112
|
-
}
|
|
113
|
-
return inline;
|
|
49
|
+
out = out.replace(/<Code\b([^>]*)\/>/g, (_, attrs) => serializeCode(attrs, getCodeAttr(attrs, context), context));
|
|
50
|
+
out = out.replace(/<Card\b([^>]*)\/>/g, (_, attrs) => serializeCard(attrs));
|
|
51
|
+
previewBlocks.forEach((block, index) => {
|
|
52
|
+
out = out.replaceAll(`\u0000RSD_PREVIEW_${index}\u0000`, block);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return out;
|
|
114
56
|
}
|
|
115
57
|
|
|
116
|
-
function
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
58
|
+
function replacePairedComponent(content, name, serialize) {
|
|
59
|
+
let output = "";
|
|
60
|
+
let cursor = 0;
|
|
61
|
+
const source = String(content || "");
|
|
62
|
+
|
|
63
|
+
while (cursor < source.length) {
|
|
64
|
+
const open = findNextTag(source, name, cursor);
|
|
65
|
+
if (!open) {
|
|
66
|
+
output += source.slice(cursor);
|
|
67
|
+
break;
|
|
121
68
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
function getAttr(node, name, constants) {
|
|
128
|
-
const attr = (node.attributes || []).find((entry) => entry.type === "mdxJsxAttribute" && entry.name === name);
|
|
129
|
-
if (!attr) return "";
|
|
130
|
-
if (attr.value == null) return "true";
|
|
131
|
-
if (typeof attr.value === "string") return attr.value;
|
|
132
|
-
return expressionValue(attr.value, constants);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function collectExportedStrings(tree) {
|
|
136
|
-
const constants = new Map();
|
|
137
|
-
for (const node of tree.children || []) {
|
|
138
|
-
if (node.type !== "mdxjsEsm") continue;
|
|
139
|
-
for (const statement of node.data?.estree?.body || []) {
|
|
140
|
-
if (statement.type !== "ExportNamedDeclaration") continue;
|
|
141
|
-
if (statement.declaration?.type !== "VariableDeclaration") continue;
|
|
142
|
-
for (const declaration of statement.declaration.declarations || []) {
|
|
143
|
-
if (declaration.id?.type !== "Identifier") continue;
|
|
144
|
-
const value = staticValue(declaration.init, constants);
|
|
145
|
-
if (value) constants.set(declaration.id.name, value);
|
|
146
|
-
}
|
|
69
|
+
|
|
70
|
+
const close = findMatchingCloseTag(source, open);
|
|
71
|
+
if (!close) {
|
|
72
|
+
output += source.slice(cursor);
|
|
73
|
+
break;
|
|
147
74
|
}
|
|
75
|
+
|
|
76
|
+
output += source.slice(cursor, open.start);
|
|
77
|
+
output += serialize(open.attrs, source.slice(open.end, close.start));
|
|
78
|
+
cursor = close.end;
|
|
148
79
|
}
|
|
149
|
-
|
|
80
|
+
|
|
81
|
+
return output;
|
|
150
82
|
}
|
|
151
83
|
|
|
152
|
-
function
|
|
153
|
-
|
|
154
|
-
return staticValue(expression, constants) || "";
|
|
84
|
+
function serializeChildren(_attrs, children, context = new Map()) {
|
|
85
|
+
return `\n\n${normalizeMdxComponents(children, context).trim()}\n\n`;
|
|
155
86
|
}
|
|
156
87
|
|
|
157
|
-
function
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return node.elements.map((element) => staticValue(element, constants));
|
|
173
|
-
}
|
|
174
|
-
return "";
|
|
88
|
+
function serializePreview(attrs, children, context = new Map()) {
|
|
89
|
+
const lang = getStringAttr(attrs, "lang") || "mdx";
|
|
90
|
+
const code = getExpressionAttr(attrs, "code");
|
|
91
|
+
const source = stripCommonIndent(code && context.has(code) ? context.get(code) : children).trim();
|
|
92
|
+
return `\n\n${fencedCode(lang, source)}\n\n`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function serializeCallout(attrs, children, context = new Map()) {
|
|
96
|
+
const title = getStringAttr(attrs, "title") || calloutTitle(getStringAttr(attrs, "type"));
|
|
97
|
+
const body = normalizeMdxComponents(children, context)
|
|
98
|
+
.trim()
|
|
99
|
+
.split("\n")
|
|
100
|
+
.map((line) => `> ${line}`)
|
|
101
|
+
.join("\n");
|
|
102
|
+
return `\n\n> **${title}**\n${body ? `${body}\n` : ""}\n`;
|
|
175
103
|
}
|
|
176
104
|
|
|
177
|
-
function
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
105
|
+
function serializeSteps(_attrs, children, context = new Map()) {
|
|
106
|
+
let index = 0;
|
|
107
|
+
const steps = children.replace(/<Step\b([^>]*)>([\s\S]*?)<\/Step>/g, (_step, attrs, body) => {
|
|
108
|
+
index += 1;
|
|
109
|
+
const title = getStringAttr(attrs, "title");
|
|
110
|
+
const text = normalizeMdxComponents(body, context).trim();
|
|
111
|
+
return `${index}. ${title ? `**${title}**` : ""}${text ? `\n\n${indentMarkdown(text, " ")}` : ""}`;
|
|
183
112
|
});
|
|
113
|
+
return `\n\n${steps}\n\n`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function serializeCode(attrs, children, context = new Map()) {
|
|
117
|
+
const lang = getStringAttr(attrs, "lang") || "text";
|
|
118
|
+
const title = getStringAttr(attrs, "title");
|
|
119
|
+
const label = title ? ` ${title}` : "";
|
|
120
|
+
const code = unwrapMdxString(children, context).trim();
|
|
121
|
+
return `\n\n${fencedCode(`${lang}${label}`, code)}\n\n`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function serializeCard(attrs) {
|
|
125
|
+
const title = getStringAttr(attrs, "title") || "Card";
|
|
126
|
+
const description = getStringAttr(attrs, "description");
|
|
127
|
+
const href = getStringAttr(attrs, "href");
|
|
128
|
+
const label = description ? `**${title}**: ${description}` : `**${title}**`;
|
|
129
|
+
return href ? `\n\n- [${label}](${href})\n` : `\n\n- ${label}\n`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function getStringAttr(attrs, name) {
|
|
133
|
+
return String(attrs || "").match(new RegExp(`${name}=["']([^"']*)["']`))?.[1] || "";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getExpressionAttr(attrs, name) {
|
|
137
|
+
return String(attrs || "").match(new RegExp(`${name}=\\{([A-Za-z_$][\\w$]*)\\}`))?.[1] || "";
|
|
184
138
|
}
|
|
185
139
|
|
|
186
|
-
function
|
|
187
|
-
|
|
140
|
+
function getCodeAttr(attrs, context = new Map()) {
|
|
141
|
+
const direct = String(attrs || "").match(/\bcode=\{`([\s\S]*?)`\}/)?.[1] || "";
|
|
142
|
+
if (direct) return direct.replace(/\\n/g, "\n").replace(/\\`/g, "`");
|
|
143
|
+
const ref = getExpressionAttr(attrs, "code");
|
|
144
|
+
return ref && context.has(ref) ? context.get(ref) : "";
|
|
188
145
|
}
|
|
189
146
|
|
|
190
|
-
function
|
|
191
|
-
|
|
147
|
+
function unwrapMdxString(value, context = new Map()) {
|
|
148
|
+
const text = String(value || "").trim();
|
|
149
|
+
const ref = text.match(/^\{([A-Za-z_$][\w$]*)\}$/)?.[1];
|
|
150
|
+
if (ref && context.has(ref)) return context.get(ref);
|
|
151
|
+
const template = text.match(/^\{`([\s\S]*)`\}$/);
|
|
152
|
+
if (template) return template[1].replace(/\\n/g, "\n").replace(/\\`/g, "`");
|
|
153
|
+
return text.replace(/^\{["']([\s\S]*)["']\}$/, "$1");
|
|
192
154
|
}
|
|
193
155
|
|
|
194
|
-
function
|
|
195
|
-
|
|
156
|
+
function calloutTitle(type) {
|
|
157
|
+
if (type === "warning") return "Warning";
|
|
158
|
+
if (type === "destructive") return "Danger";
|
|
159
|
+
return "Note";
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function indentMarkdown(markdown, prefix) {
|
|
163
|
+
return String(markdown || "")
|
|
164
|
+
.split("\n")
|
|
165
|
+
.map((line) => `${prefix}${line}`)
|
|
166
|
+
.join("\n");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function fencedCode(info, code) {
|
|
170
|
+
const fence = longestBacktickRun(code) >= 3 ? "`".repeat(longestBacktickRun(code) + 1) : "```";
|
|
171
|
+
return `${fence}${info}\n${code}\n${fence}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function longestBacktickRun(value) {
|
|
175
|
+
return Math.max(0, ...String(value || "").matchAll(/`+/g).map((match) => match[0].length));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function findMatchingCloseTag(source, open) {
|
|
179
|
+
let depth = 1;
|
|
180
|
+
let cursor = open.end;
|
|
181
|
+
|
|
182
|
+
while (cursor < source.length) {
|
|
183
|
+
const tag = findNextTag(source, open.name, cursor);
|
|
184
|
+
if (!tag) return null;
|
|
185
|
+
if (tag.closing) depth -= 1;
|
|
186
|
+
else if (!tag.selfClosing) depth += 1;
|
|
187
|
+
if (depth === 0) return tag;
|
|
188
|
+
cursor = tag.end;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function findNextTag(source, name, fromIndex) {
|
|
195
|
+
let cursor = fromIndex;
|
|
196
|
+
|
|
197
|
+
while (cursor < source.length) {
|
|
198
|
+
const start = source.indexOf("<", cursor);
|
|
199
|
+
if (start === -1) return null;
|
|
200
|
+
const tag = readTag(source, start);
|
|
201
|
+
if (!tag) {
|
|
202
|
+
cursor = start + 1;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (tag.name === name) return tag;
|
|
206
|
+
if (!tag.closing && !tag.selfClosing && tag.name.toLowerCase() === "script") {
|
|
207
|
+
const scriptEnd = source.toLowerCase().indexOf("</script>", tag.end);
|
|
208
|
+
cursor = scriptEnd === -1 ? source.length : scriptEnd + "</script>".length;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
cursor = tag.end;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function readTag(source, start) {
|
|
218
|
+
let cursor = start + 1;
|
|
219
|
+
let closing = false;
|
|
220
|
+
|
|
221
|
+
if (source[cursor] === "/") {
|
|
222
|
+
closing = true;
|
|
223
|
+
cursor += 1;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const nameStart = cursor;
|
|
227
|
+
while (/[$A-Z_a-z0-9-]/.test(source[cursor] || "")) cursor += 1;
|
|
228
|
+
const name = source.slice(nameStart, cursor);
|
|
229
|
+
if (!name) return null;
|
|
230
|
+
|
|
231
|
+
let quote = "";
|
|
232
|
+
while (cursor < source.length) {
|
|
233
|
+
const char = source[cursor];
|
|
234
|
+
if (quote) {
|
|
235
|
+
if (char === "\\" && source[cursor + 1]) {
|
|
236
|
+
cursor += 2;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (char === quote) quote = "";
|
|
240
|
+
cursor += 1;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
244
|
+
quote = char;
|
|
245
|
+
cursor += 1;
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (char === ">") {
|
|
249
|
+
const sourceTag = source.slice(start, cursor + 1);
|
|
250
|
+
const beforeEnd = sourceTag.trimEnd();
|
|
251
|
+
return {
|
|
252
|
+
start,
|
|
253
|
+
end: cursor + 1,
|
|
254
|
+
name,
|
|
255
|
+
closing,
|
|
256
|
+
selfClosing: beforeEnd.endsWith("/>"),
|
|
257
|
+
attrs: closing ? "" : sourceTag.slice(name.length + 1, -1).replace(/\/$/, ""),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
cursor += 1;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return null;
|
|
196
264
|
}
|
|
197
265
|
|
|
198
|
-
function
|
|
199
|
-
|
|
266
|
+
function stripCommonIndent(value) {
|
|
267
|
+
const lines = String(value || "").replace(/^\n+|\n+$/g, "").split("\n");
|
|
268
|
+
const indents = lines.filter((line) => line.trim()).map((line) => line.match(/^[ \t]*/)[0].length);
|
|
269
|
+
const indent = indents.length ? Math.min(...indents) : 0;
|
|
270
|
+
return indent ? lines.map((line) => line.slice(Math.min(indent, line.match(/^[ \t]*/)[0].length))).join("\n") : lines.join("\n");
|
|
200
271
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
---
|
|
2
2
|
import config from "virtual:reallysimpledocs/config";
|
|
3
|
-
import UserMdxComponents from "virtual:reallysimpledocs/mdx-components";
|
|
4
3
|
import { getMdxPage } from "virtual:reallysimpledocs/mdx-pages";
|
|
5
4
|
import * as RsdComponents from "../../components/index.js";
|
|
6
5
|
import DocsLayout from "../components/DocsLayout.astro";
|
|
@@ -19,9 +18,8 @@ const isMdx = page.extension === ".mdx";
|
|
|
19
18
|
const mdxModule = isMdx ? await getMdxPage(page.slug) : null;
|
|
20
19
|
const MdxContent = mdxModule?.Content || mdxModule?.default;
|
|
21
20
|
const doc = isMdx ? { page, headings: getDocHeadings(config, page), html: "" } : await renderDoc(config, page);
|
|
22
|
-
const mdxComponents = { ...RsdComponents, ...UserMdxComponents };
|
|
23
21
|
---
|
|
24
22
|
|
|
25
23
|
<DocsLayout page={page} headings={doc.headings}>
|
|
26
|
-
{MdxContent ? <MdxContent components={
|
|
24
|
+
{MdxContent ? <MdxContent components={RsdComponents} /> : <Fragment set:html={doc.html} />}
|
|
27
25
|
</DocsLayout>
|
|
@@ -2,12 +2,12 @@ import config from "virtual:reallysimpledocs/config";
|
|
|
2
2
|
import { getLlmDocs } from "../lib/docs.js";
|
|
3
3
|
import { getSite } from "../lib/site.js";
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export function GET({ site }) {
|
|
6
6
|
const siteData = getSite(config);
|
|
7
7
|
const siteUrl = site?.toString().replace(/\/$/, "") || siteData.url || "";
|
|
8
8
|
const lines = [];
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
getLlmDocs(config).forEach((doc) => {
|
|
11
11
|
lines.push(`# ${doc.title}`, `Source: ${siteUrl}${doc.path}`, "");
|
|
12
12
|
lines.push(doc.content, "", "---", "");
|
|
13
13
|
});
|
|
@@ -2,9 +2,9 @@ import config from "virtual:reallysimpledocs/config";
|
|
|
2
2
|
import { flattenMenuSlugs, getLlmDocs, getManifest } from "../lib/docs.js";
|
|
3
3
|
import { getSite } from "../lib/site.js";
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export function GET({ site }) {
|
|
6
6
|
const manifest = getManifest(config);
|
|
7
|
-
const docs =
|
|
7
|
+
const docs = getLlmDocs(config);
|
|
8
8
|
const bySlug = new Map(docs.map((doc) => [doc.slug, doc]));
|
|
9
9
|
const siteData = getSite(config);
|
|
10
10
|
const siteUrl = site?.toString().replace(/\/$/, "") || siteData.url || "";
|
|
@@ -8,8 +8,8 @@ export function getStaticPaths() {
|
|
|
8
8
|
}));
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export
|
|
12
|
-
return new Response(
|
|
11
|
+
export function GET({ props }) {
|
|
12
|
+
return new Response(getMarkdownExport(config, props.page), {
|
|
13
13
|
headers: {
|
|
14
14
|
"content-type": "text/markdown; charset=utf-8",
|
|
15
15
|
},
|
|
@@ -2,12 +2,14 @@ import lunr from "lunr";
|
|
|
2
2
|
import config from "virtual:reallysimpledocs/config";
|
|
3
3
|
import { getSearchDocs } from "../lib/docs.js";
|
|
4
4
|
|
|
5
|
-
export
|
|
6
|
-
const documents =
|
|
5
|
+
export function GET() {
|
|
6
|
+
const documents = getSearchDocs(config);
|
|
7
7
|
const index = lunr(function () {
|
|
8
8
|
this.ref("slug");
|
|
9
9
|
this.field("title", { boost: 10 });
|
|
10
|
+
this.field("breadcrumb", { boost: 8 });
|
|
10
11
|
this.field("body");
|
|
12
|
+
this.field("code", { boost: 0.2 });
|
|
11
13
|
this.metadataWhitelist = ["position"];
|
|
12
14
|
|
|
13
15
|
documents.forEach((doc) => this.add(doc));
|
|
@@ -16,7 +18,14 @@ export async function GET() {
|
|
|
16
18
|
return new Response(
|
|
17
19
|
JSON.stringify({
|
|
18
20
|
index,
|
|
19
|
-
documents: documents.map(({ slug, title, path, body }) => ({
|
|
21
|
+
documents: documents.map(({ slug, title, breadcrumb, path, body, code }) => ({
|
|
22
|
+
slug,
|
|
23
|
+
title,
|
|
24
|
+
breadcrumb,
|
|
25
|
+
path,
|
|
26
|
+
body,
|
|
27
|
+
code,
|
|
28
|
+
})),
|
|
20
29
|
}),
|
|
21
30
|
{
|
|
22
31
|
headers: {
|
package/CHANGELOG.md
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
|
-
|
|
7
|
-
## [1.0.0-beta.4] - 2026-06-16
|
|
8
|
-
|
|
9
|
-
### Added
|
|
10
|
-
|
|
11
|
-
- Added MDX documentation components for callouts, code blocks, code groups, previews, tabs, steps, and cards.
|
|
12
|
-
- Added automatic MDX component availability in docs pages, so common ReallySimpleDocs components do not need page-level imports.
|
|
13
|
-
- Added custom `mdxComponents` registration for project-specific MDX components.
|
|
14
|
-
- Added custom `markdownSerializers` registration so project-specific MDX components can control Markdown, search, and LLM export output.
|
|
15
|
-
- Added Markdown export serializers for ReallySimpleDocs components so generated Markdown, search content, and `llms-full.txt` avoid raw MDX component noise.
|
|
16
|
-
- Added customizable sidebar header, sidebar footer, and content header component overrides.
|
|
17
|
-
- Added breadcrumb rendering from the docs navigation tree.
|
|
18
|
-
- Added Lunr-powered command search with result previews and highlighted matches.
|
|
19
|
-
- Added Basecoat style selection through the ReallySimpleDocs integration config.
|
|
20
|
-
|
|
21
|
-
### Changed
|
|
22
|
-
|
|
23
|
-
- Ported ReallySimpleDocs to an Astro integration with generated docs routes, assets, Markdown exports, search index, and Basecoat styling.
|
|
24
|
-
- Updated docs content to use Markdown by default and MDX only for richer component examples.
|
|
25
|
-
- Updated code examples to use inline component children instead of passing source code as props.
|
|
26
|
-
- Changed empty table-of-contents behavior so the right panel is hidden instead of rendering an empty “On this page” section.
|
|
27
|
-
- Changed breadcrumbs to render in the sticky content header for nested pages.
|
|
28
|
-
- Updated the docs overview and component pages to explain component usage, Markdown output, and Basecoat interoperability.
|
|
29
|
-
|
|
30
|
-
### Fixed
|
|
31
|
-
|
|
32
|
-
- Fixed Shiki code block colors across light and dark mode.
|
|
33
|
-
- Fixed copy-code button layout for regular code blocks and tabbed code blocks.
|
|
34
|
-
- Fixed callout rendering in Markdown and MDX pages.
|
|
35
|
-
- Fixed sidebar and header markup to follow Basecoat component patterns.
|
package/src/css/overrides.css
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/* Custom CSS overrides (loaded last). */
|
package/src/css/sources.css
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@source "../runtime/**/*.{astro,js,ts}";
|