reallysimpledocs 1.0.0-beta.4 → 1.0.0-beta.5
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 +35 -35
- package/package.json +10 -13
- package/src/astro/index.js +46 -98
- package/src/astro/preview-source.js +90 -0
- package/src/components/index.js +1 -0
- package/src/css/custom.css +252 -33
- package/src/css/styles/luma.css +11 -0
- package/src/css/styles/lyra.css +11 -0
- package/src/css/styles/maia.css +11 -0
- package/src/css/styles/mira.css +11 -0
- package/src/css/styles/nova.css +11 -0
- package/src/css/styles/rhea.css +11 -0
- package/src/css/styles/sera.css +11 -0
- package/src/css/styles/vega.css +11 -0
- package/src/runtime/components/BaseLayout.astro +20 -27
- package/src/runtime/components/CommandDialog.astro +30 -32
- package/src/runtime/components/DefaultContentHeader.astro +0 -17
- package/src/runtime/components/DefaultHead.astro +120 -0
- package/src/runtime/components/DefaultHeadContent.astro +2 -0
- package/src/runtime/components/DefaultSidebarHeader.astro +1 -1
- package/src/runtime/components/DocsLayout.astro +114 -165
- 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 +2 -16
- package/src/runtime/components/mdx/CodeGroup.astro +7 -11
- 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/docs.js +130 -102
- package/src/runtime/lib/highlight.js +1 -14
- package/src/runtime/lib/macros.js +10 -5
- package/src/runtime/lib/markdown-export.js +134 -171
- 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
|
@@ -5,10 +5,6 @@ const { lang = "text", title, class: className = "" } = Astro.props;
|
|
|
5
5
|
const slotHtml = Astro.slots.has("default") ? await Astro.slots.render("default") : "";
|
|
6
6
|
const rawCode = decodeSlotText(slotHtml);
|
|
7
7
|
const html = await highlightCode(rawCode, lang);
|
|
8
|
-
const classes = [
|
|
9
|
-
"relative my-6 w-full max-w-full overflow-hidden rounded-lg",
|
|
10
|
-
className,
|
|
11
|
-
];
|
|
12
8
|
|
|
13
9
|
function decodeSlotText(value) {
|
|
14
10
|
return String(value || "")
|
|
@@ -23,17 +19,7 @@ function decodeSlotText(value) {
|
|
|
23
19
|
}
|
|
24
20
|
---
|
|
25
21
|
|
|
26
|
-
<div
|
|
27
|
-
|
|
28
|
-
data-code-title={title || undefined}
|
|
29
|
-
class:list={classes}
|
|
30
|
-
>
|
|
31
|
-
{
|
|
32
|
-
title && (
|
|
33
|
-
<header class="flex h-10 items-center gap-2 border-b border-border/50 bg-muted/50 px-3 text-sm font-medium">
|
|
34
|
-
<span class="mr-auto">{title}</span>
|
|
35
|
-
</header>
|
|
36
|
-
)
|
|
37
|
-
}
|
|
22
|
+
<div class:list={["code-block", className]} data-code-title={title || undefined}>
|
|
23
|
+
{title && <header><span>{title}</span></header>}
|
|
38
24
|
<Fragment set:html={html} />
|
|
39
25
|
</div>
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
-
const {
|
|
2
|
+
const { class: className = "" } = Astro.props;
|
|
3
3
|
const id = `code-group-${Math.random().toString(36).slice(2)}`;
|
|
4
4
|
const slotHtml = Astro.slots.has("default") ? await Astro.slots.render("default") : "";
|
|
5
5
|
const normalizedTabs = parseCodeTabs(slotHtml);
|
|
6
|
-
const defaultValue =
|
|
7
|
-
const classes = [
|
|
8
|
-
"relative my-6 w-full max-w-full overflow-hidden rounded-lg [&>[role=tabpanel]]:border-t-0 [&_[data-code-block]]:my-0 [&_[data-code-block]]:min-w-0 [&_[data-code-block]]:rounded-none [&_[data-code-block]>header]:hidden",
|
|
9
|
-
className,
|
|
10
|
-
];
|
|
6
|
+
const defaultValue = normalizedTabs[0]?.value;
|
|
11
7
|
|
|
12
8
|
function parseCodeTabs(html) {
|
|
13
9
|
const blocks = [];
|
|
14
10
|
const pattern =
|
|
15
|
-
/<div\b(?=[^>]*\
|
|
11
|
+
/<div\b(?=[^>]*\bclass="[^"]*\bcode-block\b[^"]*")(?=[^>]*\bdata-code-title="([^"]*)")[^>]*>([\s\S]*?)<\/div>/g;
|
|
16
12
|
let match;
|
|
17
13
|
while ((match = pattern.exec(html))) {
|
|
18
14
|
const label = decodeHtml(match[1]).trim();
|
|
@@ -36,10 +32,10 @@ function decodeHtml(value) {
|
|
|
36
32
|
}
|
|
37
33
|
---
|
|
38
34
|
|
|
39
|
-
<div class:list={
|
|
40
|
-
<header
|
|
41
|
-
<div class="tabs
|
|
42
|
-
<nav
|
|
35
|
+
<div class:list={["code-group", className]}>
|
|
36
|
+
<header>
|
|
37
|
+
<div class="tabs">
|
|
38
|
+
<nav role="tablist" aria-orientation="horizontal" data-variant="line">
|
|
43
39
|
{
|
|
44
40
|
normalizedTabs.map((tab, index) => {
|
|
45
41
|
const selected = tab.value === defaultValue;
|
|
@@ -4,15 +4,13 @@ import { highlightCode } from "../../lib/highlight.js";
|
|
|
4
4
|
const { code = "", lang = "html", title = "Preview", class: className = "" } = Astro.props;
|
|
5
5
|
const id = `preview-${Math.random().toString(36).slice(2)}`;
|
|
6
6
|
const html = await highlightCode(code, lang);
|
|
7
|
+
const previewClassName = className || "w-fit";
|
|
7
8
|
---
|
|
8
9
|
|
|
9
|
-
<section
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
>
|
|
13
|
-
<header class="flex h-10 items-center gap-2 border-b border-border/50 bg-muted/50 px-3 text-sm font-medium">
|
|
14
|
-
<div class="tabs my-0 min-w-0 max-w-full gap-0">
|
|
15
|
-
<nav class="h-full justify-start rounded-none bg-transparent p-0" role="tablist" aria-orientation="horizontal" data-variant="line">
|
|
10
|
+
<section class="preview">
|
|
11
|
+
<header>
|
|
12
|
+
<div class="tabs">
|
|
13
|
+
<nav role="tablist" aria-orientation="horizontal" data-variant="line">
|
|
16
14
|
<button
|
|
17
15
|
type="button"
|
|
18
16
|
role="tab"
|
|
@@ -43,9 +41,8 @@ const html = await highlightCode(code, lang);
|
|
|
43
41
|
aria-labelledby={`${id}-preview-tab`}
|
|
44
42
|
tabindex="-1"
|
|
45
43
|
aria-selected="true"
|
|
46
|
-
class="flex min-h-64 w-full items-center justify-center p-6"
|
|
47
44
|
>
|
|
48
|
-
<div class={
|
|
45
|
+
<div class={previewClassName}>
|
|
49
46
|
<slot />
|
|
50
47
|
</div>
|
|
51
48
|
</section>
|
|
@@ -58,10 +55,7 @@ const html = await highlightCode(code, lang);
|
|
|
58
55
|
aria-selected="false"
|
|
59
56
|
hidden
|
|
60
57
|
>
|
|
61
|
-
<div
|
|
62
|
-
class="relative my-6 w-full max-w-full overflow-hidden rounded-lg"
|
|
63
|
-
data-code-block
|
|
64
|
-
>
|
|
58
|
+
<div class="code-block">
|
|
65
59
|
<Fragment set:html={html} />
|
|
66
60
|
</div>
|
|
67
61
|
</section>
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
const { class: className = "" } = Astro.props;
|
|
3
|
-
const classes = [
|
|
4
|
-
"mb-12 mt-6 [counter-reset:step] md:ml-4 md:border-l md:pl-8 [&>section]:mb-10 [&>section]:[counter-increment:step] [&>section:last-child]:mb-0 [&>section>*+*]:mt-6 [&>section>*]:leading-relaxed [&>section>*:first-child]:mt-0 [&>section>*:last-child]:mb-0 [&>section>h3+*]:mt-4 [&>section>h3]:relative [&>section>h3]:scroll-m-28 [&>section>h3]:text-lg [&>section>h3]:font-medium [&>section>h3]:tracking-tight [&>section>h3]:before:mr-4 [&>section>h3]:before:inline-flex [&>section>h3]:before:size-7 [&>section>h3]:before:items-center [&>section>h3]:before:justify-center [&>section>h3]:before:rounded-full [&>section>h3]:before:bg-muted [&>section>h3]:before:text-sm [&>section>h3]:before:font-medium [&>section>h3]:before:text-muted-foreground [&>section>h3]:before:content-[counter(step)] md:[&>section>h3]:before:absolute md:[&>section>h3]:before:left-0 md:[&>section>h3]:before:top-0 md:[&>section>h3]:before:mr-0 md:[&>section>h3]:before:-translate-x-[2.875rem]",
|
|
5
|
-
className,
|
|
6
|
-
];
|
|
7
3
|
---
|
|
8
4
|
|
|
9
|
-
<div role="list" class:list={
|
|
5
|
+
<div role="list" class:list={["steps", className]}>
|
|
10
6
|
<slot />
|
|
11
7
|
</div>
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
---
|
|
2
|
-
const { tabs = [],
|
|
2
|
+
const { tabs = [], variant, class: className = "" } = Astro.props;
|
|
3
3
|
const id = `tabs-${Math.random().toString(36).slice(2)}`;
|
|
4
|
-
const
|
|
4
|
+
const configuredTabs = typeof tabs === "string" ? JSON.parse(tabs) : tabs;
|
|
5
|
+
const normalizedTabs = configuredTabs.map((tab, index) => ({
|
|
5
6
|
...tab,
|
|
6
7
|
slot: tab.slot || String(index),
|
|
7
8
|
value: tab.value || tab.slot || String(index),
|
|
8
9
|
}));
|
|
9
|
-
const defaultValue =
|
|
10
|
+
const defaultValue = normalizedTabs[0]?.value;
|
|
10
11
|
---
|
|
11
12
|
|
|
12
|
-
<div class:list={["tabs
|
|
13
|
-
<nav role="tablist" aria-orientation=
|
|
13
|
+
<div class:list={["tabs", className]}>
|
|
14
|
+
<nav role="tablist" aria-orientation="horizontal" data-variant={variant}>
|
|
14
15
|
{
|
|
15
16
|
normalizedTabs.map((tab, index) => {
|
|
16
17
|
const selected = tab.value === defaultValue;
|
|
@@ -21,8 +22,7 @@ const defaultValue = defaultTab || normalizedTabs.find((tab) => !tab.disabled)?.
|
|
|
21
22
|
id={`${id}-tab-${index + 1}`}
|
|
22
23
|
aria-controls={`${id}-panel-${index + 1}`}
|
|
23
24
|
aria-selected={selected ? "true" : "false"}
|
|
24
|
-
tabindex={selected
|
|
25
|
-
disabled={tab.disabled}
|
|
25
|
+
tabindex={selected ? "0" : "-1"}
|
|
26
26
|
>
|
|
27
27
|
{tab.label}
|
|
28
28
|
</button>
|
package/src/runtime/lib/docs.js
CHANGED
|
@@ -2,9 +2,9 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { marked } from "marked";
|
|
4
4
|
import GithubSlugger from "github-slugger";
|
|
5
|
+
import { codeToHtml } from "shiki";
|
|
5
6
|
import * as lucideIcons from "lucide-static";
|
|
6
|
-
import {
|
|
7
|
-
import { mdxToMarkdown } from "./markdown-export.js";
|
|
7
|
+
import { normalizeDocMarkdown } from "./markdown-export.js";
|
|
8
8
|
import { markdownPath, routePath } from "./paths.js";
|
|
9
9
|
|
|
10
10
|
export function getManifest(config) {
|
|
@@ -83,14 +83,8 @@ export function getDocMarkdown(config, page) {
|
|
|
83
83
|
return fs.readFileSync(filePath, "utf8");
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
export
|
|
87
|
-
|
|
88
|
-
if (page.extension === ".mdx") return await mdxToMarkdown(source, config);
|
|
89
|
-
return `${source.trim()}\n`;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export async function getMarkdownBodyExport(config, page) {
|
|
93
|
-
return parseDocMarkdown(await getMarkdownExport(config, page), page.title).body.trim();
|
|
86
|
+
export function getMarkdownExport(config, page) {
|
|
87
|
+
return `${getNormalizedDocMarkdown(config, page).trim()}\n`;
|
|
94
88
|
}
|
|
95
89
|
|
|
96
90
|
export async function renderDoc(config, page) {
|
|
@@ -124,7 +118,7 @@ export async function renderDoc(config, page) {
|
|
|
124
118
|
};
|
|
125
119
|
|
|
126
120
|
const tokens = marked.lexer(content);
|
|
127
|
-
await
|
|
121
|
+
await highlightCode(tokens);
|
|
128
122
|
const html = marked.parser(tokens, { renderer });
|
|
129
123
|
return { page, html, headings };
|
|
130
124
|
}
|
|
@@ -159,46 +153,6 @@ export function getNavigation(config, page) {
|
|
|
159
153
|
};
|
|
160
154
|
}
|
|
161
155
|
|
|
162
|
-
export function getBreadcrumbs(config, page) {
|
|
163
|
-
const pages = getPages(config);
|
|
164
|
-
const bySlug = new Map(pages.map((entry) => [entry.slug, entry]));
|
|
165
|
-
const trail = [];
|
|
166
|
-
|
|
167
|
-
const walk = (items = [], ancestors = []) => {
|
|
168
|
-
for (const item of items) {
|
|
169
|
-
if (item?.type === "group") {
|
|
170
|
-
const result = walk(item.items || [], ancestors);
|
|
171
|
-
if (result) return result;
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (item?.type === "submenu") {
|
|
176
|
-
const result = walk(item.items || [], item.label ? [...ancestors, { label: item.label }] : ancestors);
|
|
177
|
-
if (result) return result;
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const slug = typeof item === "string" ? item : getPageItemSlug(item);
|
|
182
|
-
if (slug !== page.slug) continue;
|
|
183
|
-
const matchedPage = bySlug.get(slug);
|
|
184
|
-
if (!matchedPage) return ancestors;
|
|
185
|
-
return [
|
|
186
|
-
...ancestors,
|
|
187
|
-
{
|
|
188
|
-
label: item.label || matchedPage.title,
|
|
189
|
-
url: matchedPage.path,
|
|
190
|
-
},
|
|
191
|
-
];
|
|
192
|
-
}
|
|
193
|
-
return null;
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
const manifest = getManifest(config);
|
|
197
|
-
const found = walk(manifest.menu || []);
|
|
198
|
-
if (found) trail.push(...found);
|
|
199
|
-
return trail;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
156
|
export function resolveIcon(icon) {
|
|
203
157
|
if (!icon || typeof icon !== "string") return "";
|
|
204
158
|
const trimmed = icon.trim();
|
|
@@ -229,35 +183,28 @@ export function buildMenus(config, currentSlug) {
|
|
|
229
183
|
icon,
|
|
230
184
|
url: page.path,
|
|
231
185
|
label: item.label || page.title,
|
|
186
|
+
badge: item.badge,
|
|
232
187
|
current: page.slug === currentSlug,
|
|
233
188
|
keywords: page.title,
|
|
234
|
-
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
const processExternalLink = (item) => {
|
|
238
|
-
if (!item?.url || !item?.label) return null;
|
|
239
|
-
const external = isExternalUrl(item.url);
|
|
240
|
-
const attrs = {
|
|
241
|
-
...item.attrs,
|
|
242
|
-
target: external ? item.attrs?.target || "_blank" : item.attrs?.target,
|
|
243
|
-
rel: external ? item.attrs?.rel || "noopener" : item.attrs?.rel,
|
|
244
|
-
};
|
|
245
|
-
return {
|
|
246
|
-
type: "item",
|
|
247
|
-
icon: resolveIcon(item.icon),
|
|
248
|
-
url: item.url,
|
|
249
|
-
label: item.label,
|
|
250
|
-
external,
|
|
251
|
-
attrs,
|
|
252
|
-
keywords: item.label,
|
|
189
|
+
attrs: item.attrs,
|
|
253
190
|
};
|
|
254
191
|
};
|
|
255
192
|
|
|
256
193
|
const processItem = (item) => {
|
|
257
194
|
if (typeof item === "string") return processSlug(item);
|
|
195
|
+
if (item?.url && item?.label) {
|
|
196
|
+
return {
|
|
197
|
+
type: "item",
|
|
198
|
+
icon: resolveIcon(item.icon),
|
|
199
|
+
url: item.url,
|
|
200
|
+
label: item.label,
|
|
201
|
+
badge: item.badge,
|
|
202
|
+
external: /^https?:\/\//.test(item.url),
|
|
203
|
+
attrs: item.attrs,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
258
206
|
const slug = getPageItemSlug(item);
|
|
259
207
|
if (slug) return processSlug(slug, item);
|
|
260
|
-
if (item?.url) return processExternalLink(item);
|
|
261
208
|
if (item?.type === "submenu") {
|
|
262
209
|
const items = (item.items || []).map(processItem).filter(Boolean);
|
|
263
210
|
return {
|
|
@@ -287,37 +234,90 @@ export function buildMenus(config, currentSlug) {
|
|
|
287
234
|
icon: item.icon,
|
|
288
235
|
url: item.url,
|
|
289
236
|
label: item.label,
|
|
290
|
-
external: item.external,
|
|
291
|
-
attrs: item.attrs,
|
|
292
237
|
keywords: item.keywords,
|
|
238
|
+
attrs: item.attrs,
|
|
293
239
|
})),
|
|
294
240
|
}));
|
|
295
241
|
|
|
296
242
|
return { sidebar, command };
|
|
297
243
|
}
|
|
298
244
|
|
|
299
|
-
export
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
content: await getMarkdownBodyExport(config, page),
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
return docs;
|
|
245
|
+
export function getLlmDocs(config) {
|
|
246
|
+
return getPages(config).map((page) => ({
|
|
247
|
+
...page,
|
|
248
|
+
content: getNormalizedDocMarkdown(config, page, { includeTitle: false }).trim(),
|
|
249
|
+
}));
|
|
308
250
|
}
|
|
309
251
|
|
|
310
|
-
export
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
252
|
+
export function getSearchDocs(config) {
|
|
253
|
+
const pages = getPages(config);
|
|
254
|
+
const trails = getPageTrails(config, pages);
|
|
255
|
+
|
|
256
|
+
return pages.map((page) => {
|
|
257
|
+
const markdown = getNormalizedDocMarkdown(config, page, { includeTitle: false });
|
|
258
|
+
const { prose, code } = splitFencedMarkdown(markdown);
|
|
259
|
+
const trail = trails.get(page.slug) || [page.title];
|
|
260
|
+
|
|
261
|
+
return {
|
|
314
262
|
slug: page.slug,
|
|
315
263
|
title: page.title,
|
|
264
|
+
breadcrumb: trail.length > 1 ? trail.join(" › ") : "",
|
|
316
265
|
path: page.path,
|
|
317
|
-
body: markdownPlainText(
|
|
266
|
+
body: markdownPlainText(prose),
|
|
267
|
+
code: markdownPlainText(code),
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function getPageTrails(config, pages) {
|
|
273
|
+
const manifest = getManifest(config);
|
|
274
|
+
const pagesBySlug = new Map(pages.map((page) => [page.slug, page]));
|
|
275
|
+
const trails = new Map();
|
|
276
|
+
|
|
277
|
+
const pageLabel = (item, slug) => {
|
|
278
|
+
if (typeof item === "object" && item?.label) return item.label;
|
|
279
|
+
return pagesBySlug.get(slug)?.title || fallbackLabelFromSlug(slug);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const walk = (items = [], parents = []) => {
|
|
283
|
+
items.forEach((item) => {
|
|
284
|
+
if (typeof item === "string") {
|
|
285
|
+
trails.set(item, [...parents, pageLabel(item, item)]);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (item?.type === "submenu") {
|
|
290
|
+
walk(item.items || [], [...parents, item.label].filter(Boolean));
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const slug = getPageItemSlug(item);
|
|
295
|
+
if (slug) trails.set(slug, [...parents, pageLabel(item, slug)]);
|
|
318
296
|
});
|
|
319
|
-
}
|
|
320
|
-
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
(manifest.menu || []).forEach((group) => {
|
|
300
|
+
if (group?.type === "group") walk(group.items || []);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
return trails;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function splitFencedMarkdown(markdown) {
|
|
307
|
+
const code = [];
|
|
308
|
+
const prose = String(markdown || "").replace(/```[^\n]*\n([\s\S]*?)```/g, (_, block) => {
|
|
309
|
+
code.push(block);
|
|
310
|
+
return "\n";
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
return { prose, code: code.join("\n\n") };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function getNormalizedDocMarkdown(config, page, { includeTitle = true } = {}) {
|
|
317
|
+
const source = getDocMarkdown(config, page);
|
|
318
|
+
const parsed = parseDocMarkdown(source, page.title);
|
|
319
|
+
const body = normalizeDocMarkdown(renderNunjucksCompat(parsed.body), collectMdxExports(source));
|
|
320
|
+
return includeTitle ? `# ${parsed.title}\n\n${body}` : body;
|
|
321
321
|
}
|
|
322
322
|
|
|
323
323
|
function flattenSidebarItems(items = []) {
|
|
@@ -329,10 +329,6 @@ export function getPageItemSlug(item) {
|
|
|
329
329
|
return item.slug || item.page || "";
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
function isExternalUrl(url) {
|
|
333
|
-
return /^[a-z][a-z0-9+.-]*:/i.test(String(url || ""));
|
|
334
|
-
}
|
|
335
|
-
|
|
336
332
|
function parseDocMarkdown(source, fallbackTitle) {
|
|
337
333
|
const content = normalizeDocSource(source);
|
|
338
334
|
const tokens = marked.lexer(content);
|
|
@@ -355,6 +351,15 @@ function markdownPlainText(markdown) {
|
|
|
355
351
|
return tokensText(marked.lexer(renderNunjucksCompat(normalizeDocSource(markdown))));
|
|
356
352
|
}
|
|
357
353
|
|
|
354
|
+
function collectMdxExports(source) {
|
|
355
|
+
const values = new Map();
|
|
356
|
+
const pattern = /export\s+const\s+([A-Za-z_$][\w$]*)\s*=\s*`([\s\S]*?)`;/g;
|
|
357
|
+
for (const match of String(source || "").matchAll(pattern)) {
|
|
358
|
+
values.set(match[1], match[2].replace(/\\n/g, "\n").replace(/\\`/g, "`"));
|
|
359
|
+
}
|
|
360
|
+
return values;
|
|
361
|
+
}
|
|
362
|
+
|
|
358
363
|
function normalizeDocSource(source) {
|
|
359
364
|
const withoutFrontmatter = String(source || "")
|
|
360
365
|
.trimStart()
|
|
@@ -366,7 +371,7 @@ function normalizeDocSource(source) {
|
|
|
366
371
|
.trimStart();
|
|
367
372
|
}
|
|
368
373
|
|
|
369
|
-
async function
|
|
374
|
+
async function highlightCode(tokens) {
|
|
370
375
|
for (const token of tokens) {
|
|
371
376
|
if (token.type === "code") {
|
|
372
377
|
const html = await highlightToken(token);
|
|
@@ -380,24 +385,33 @@ async function highlightMarkdownCode(tokens) {
|
|
|
380
385
|
}
|
|
381
386
|
|
|
382
387
|
for (const childTokens of nestedTokenLists(token)) {
|
|
383
|
-
await
|
|
388
|
+
await highlightCode(childTokens);
|
|
384
389
|
}
|
|
385
390
|
}
|
|
386
391
|
}
|
|
387
392
|
|
|
388
393
|
async function highlightToken(token) {
|
|
389
|
-
const lang = normalizeLanguage(token.lang);
|
|
390
394
|
try {
|
|
391
|
-
return
|
|
395
|
+
return await codeToHtml(token.text, {
|
|
396
|
+
lang: normalizeLanguage(token.lang),
|
|
397
|
+
themes: {
|
|
398
|
+
light: "github-light",
|
|
399
|
+
dark: "github-dark",
|
|
400
|
+
},
|
|
401
|
+
defaultColor: false,
|
|
402
|
+
});
|
|
392
403
|
} catch {
|
|
393
|
-
return
|
|
404
|
+
return codeToHtml(token.text, {
|
|
405
|
+
lang: "text",
|
|
406
|
+
themes: {
|
|
407
|
+
light: "github-light",
|
|
408
|
+
dark: "github-dark",
|
|
409
|
+
},
|
|
410
|
+
defaultColor: false,
|
|
411
|
+
});
|
|
394
412
|
}
|
|
395
413
|
}
|
|
396
414
|
|
|
397
|
-
function wrapCodeBlock(html) {
|
|
398
|
-
return `<div class="relative my-6 w-full max-w-full overflow-hidden rounded-lg" data-code-block>${html}</div>`;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
415
|
function normalizeLanguage(lang) {
|
|
402
416
|
const value = lang?.trim().split(/\s+/)[0].toLowerCase() || "text";
|
|
403
417
|
if (value === "njk") return "html";
|
|
@@ -427,6 +441,7 @@ function plainText(tokens) {
|
|
|
427
441
|
function tokensText(tokens) {
|
|
428
442
|
return tokens
|
|
429
443
|
.map((token) => {
|
|
444
|
+
if (token.type === "html") return htmlText(token.raw || token.text || "");
|
|
430
445
|
const childTokens = [];
|
|
431
446
|
if (token.tokens) childTokens.push(token.tokens);
|
|
432
447
|
if (token.items) childTokens.push(...token.items.flatMap((item) => (item.tokens ? [item.tokens] : [])));
|
|
@@ -438,3 +453,16 @@ function tokensText(tokens) {
|
|
|
438
453
|
.replace(/\s+/g, " ")
|
|
439
454
|
.trim();
|
|
440
455
|
}
|
|
456
|
+
|
|
457
|
+
function htmlText(html) {
|
|
458
|
+
return String(html || "")
|
|
459
|
+
.replace(/<script\b[\s\S]*?<\/script>/gi, " ")
|
|
460
|
+
.replace(/<style\b[\s\S]*?<\/style>/gi, " ")
|
|
461
|
+
.replace(/<[^>]+>/g, " ")
|
|
462
|
+
.replace(/ /g, " ")
|
|
463
|
+
.replace(/&/g, "&")
|
|
464
|
+
.replace(/</g, "<")
|
|
465
|
+
.replace(/>/g, ">")
|
|
466
|
+
.replace(/"/g, '"')
|
|
467
|
+
.replace(/'/g, "'");
|
|
468
|
+
}
|
|
@@ -3,7 +3,7 @@ import { codeToHtml } from "shiki";
|
|
|
3
3
|
export async function highlightCode(code, lang = "text") {
|
|
4
4
|
const value = String(code || "");
|
|
5
5
|
const language = String(lang || "text").toLowerCase() === "mdx" ? "tsx" : String(lang || "text");
|
|
6
|
-
|
|
6
|
+
return codeToHtml(value, {
|
|
7
7
|
lang: language,
|
|
8
8
|
themes: {
|
|
9
9
|
light: "github-light",
|
|
@@ -11,17 +11,4 @@ export async function highlightCode(code, lang = "text") {
|
|
|
11
11
|
},
|
|
12
12
|
defaultColor: false,
|
|
13
13
|
});
|
|
14
|
-
return addCodeLayoutClasses(html);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function addCodeLayoutClasses(html) {
|
|
18
|
-
return String(html)
|
|
19
|
-
.replace(
|
|
20
|
-
/<pre class="/,
|
|
21
|
-
'<pre class="!my-0 max-w-full overflow-x-auto !rounded-none !border-0 ',
|
|
22
|
-
)
|
|
23
|
-
.replace(
|
|
24
|
-
/<code>/,
|
|
25
|
-
'<code class="block min-w-full w-max px-4 py-3 text-[13px]">',
|
|
26
|
-
);
|
|
27
14
|
}
|
|
@@ -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 = [],
|