reallysimpledocs 1.0.0-beta.3 → 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 +33 -31
- package/package.json +10 -5
- package/src/astro/index.js +46 -43
- package/src/astro/preview-source.js +90 -0
- package/src/components/index.js +1 -0
- package/src/css/custom.css +259 -29
- 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 +29 -14
- package/src/runtime/components/DefaultContentHeader.astro +2 -0
- package/src/runtime/components/DefaultHead.astro +120 -0
- package/src/runtime/components/DefaultHeadContent.astro +2 -0
- package/src/runtime/components/DefaultSidebarFooter.astro +2 -0
- package/src/runtime/components/DefaultSidebarHeader.astro +1 -1
- package/src/runtime/components/DocsLayout.astro +90 -122
- package/src/runtime/components/DropdownMenu.astro +1 -1
- package/src/runtime/components/Sidebar.astro +4 -0
- package/src/runtime/components/ThemeToggle.astro +21 -0
- package/src/runtime/components/mdx/Callout.astro +2 -9
- 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 -12
- 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 +122 -16
- package/src/runtime/lib/highlight.js +1 -14
- package/src/runtime/lib/macros.js +11 -2
- package/src/runtime/lib/markdown-export.js +163 -0
- package/src/runtime/lib/site.js +0 -1
- package/src/runtime/pages/search-index.ts +10 -1
- package/src/css/overrides.css +0 -1
- package/src/css/sources.css +0 -1
- package/src/runtime/scripts/copy-code.js +0 -109
|
@@ -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"
|
|
@@ -44,7 +42,7 @@ const html = await highlightCode(code, lang);
|
|
|
44
42
|
tabindex="-1"
|
|
45
43
|
aria-selected="true"
|
|
46
44
|
>
|
|
47
|
-
<div class={
|
|
45
|
+
<div class={previewClassName}>
|
|
48
46
|
<slot />
|
|
49
47
|
</div>
|
|
50
48
|
</section>
|
|
@@ -57,10 +55,7 @@ const html = await highlightCode(code, lang);
|
|
|
57
55
|
aria-selected="false"
|
|
58
56
|
hidden
|
|
59
57
|
>
|
|
60
|
-
<div
|
|
61
|
-
class="relative my-6 w-full max-w-full overflow-hidden rounded-lg"
|
|
62
|
-
data-code-block
|
|
63
|
-
>
|
|
58
|
+
<div class="code-block">
|
|
64
59
|
<Fragment set:html={html} />
|
|
65
60
|
</div>
|
|
66
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,8 +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 { normalizeDocMarkdown } from "./markdown-export.js";
|
|
7
8
|
import { markdownPath, routePath } from "./paths.js";
|
|
8
9
|
|
|
9
10
|
export function getManifest(config) {
|
|
@@ -83,7 +84,7 @@ export function getDocMarkdown(config, page) {
|
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
export function getMarkdownExport(config, page) {
|
|
86
|
-
return `${
|
|
87
|
+
return `${getNormalizedDocMarkdown(config, page).trim()}\n`;
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
export async function renderDoc(config, page) {
|
|
@@ -117,7 +118,7 @@ export async function renderDoc(config, page) {
|
|
|
117
118
|
};
|
|
118
119
|
|
|
119
120
|
const tokens = marked.lexer(content);
|
|
120
|
-
await
|
|
121
|
+
await highlightCode(tokens);
|
|
121
122
|
const html = marked.parser(tokens, { renderer });
|
|
122
123
|
return { page, html, headings };
|
|
123
124
|
}
|
|
@@ -182,13 +183,26 @@ export function buildMenus(config, currentSlug) {
|
|
|
182
183
|
icon,
|
|
183
184
|
url: page.path,
|
|
184
185
|
label: item.label || page.title,
|
|
186
|
+
badge: item.badge,
|
|
185
187
|
current: page.slug === currentSlug,
|
|
186
188
|
keywords: page.title,
|
|
189
|
+
attrs: item.attrs,
|
|
187
190
|
};
|
|
188
191
|
};
|
|
189
192
|
|
|
190
193
|
const processItem = (item) => {
|
|
191
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
|
+
}
|
|
192
206
|
const slug = getPageItemSlug(item);
|
|
193
207
|
if (slug) return processSlug(slug, item);
|
|
194
208
|
if (item?.type === "submenu") {
|
|
@@ -221,6 +235,7 @@ export function buildMenus(config, currentSlug) {
|
|
|
221
235
|
url: item.url,
|
|
222
236
|
label: item.label,
|
|
223
237
|
keywords: item.keywords,
|
|
238
|
+
attrs: item.attrs,
|
|
224
239
|
})),
|
|
225
240
|
}));
|
|
226
241
|
|
|
@@ -230,22 +245,81 @@ export function buildMenus(config, currentSlug) {
|
|
|
230
245
|
export function getLlmDocs(config) {
|
|
231
246
|
return getPages(config).map((page) => ({
|
|
232
247
|
...page,
|
|
233
|
-
content:
|
|
248
|
+
content: getNormalizedDocMarkdown(config, page, { includeTitle: false }).trim(),
|
|
234
249
|
}));
|
|
235
250
|
}
|
|
236
251
|
|
|
237
252
|
export function getSearchDocs(config) {
|
|
238
|
-
|
|
239
|
-
|
|
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
|
+
|
|
240
261
|
return {
|
|
241
262
|
slug: page.slug,
|
|
242
263
|
title: page.title,
|
|
264
|
+
breadcrumb: trail.length > 1 ? trail.join(" › ") : "",
|
|
243
265
|
path: page.path,
|
|
244
|
-
body: markdownPlainText(
|
|
266
|
+
body: markdownPlainText(prose),
|
|
267
|
+
code: markdownPlainText(code),
|
|
245
268
|
};
|
|
246
269
|
});
|
|
247
270
|
}
|
|
248
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)]);
|
|
296
|
+
});
|
|
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
|
+
}
|
|
322
|
+
|
|
249
323
|
function flattenSidebarItems(items = []) {
|
|
250
324
|
return items.flatMap((item) => (item.type === "submenu" ? flattenSidebarItems(item.items) : [item]));
|
|
251
325
|
}
|
|
@@ -277,6 +351,15 @@ function markdownPlainText(markdown) {
|
|
|
277
351
|
return tokensText(marked.lexer(renderNunjucksCompat(normalizeDocSource(markdown))));
|
|
278
352
|
}
|
|
279
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
|
+
|
|
280
363
|
function normalizeDocSource(source) {
|
|
281
364
|
const withoutFrontmatter = String(source || "")
|
|
282
365
|
.trimStart()
|
|
@@ -288,7 +371,7 @@ function normalizeDocSource(source) {
|
|
|
288
371
|
.trimStart();
|
|
289
372
|
}
|
|
290
373
|
|
|
291
|
-
async function
|
|
374
|
+
async function highlightCode(tokens) {
|
|
292
375
|
for (const token of tokens) {
|
|
293
376
|
if (token.type === "code") {
|
|
294
377
|
const html = await highlightToken(token);
|
|
@@ -302,24 +385,33 @@ async function highlightMarkdownCode(tokens) {
|
|
|
302
385
|
}
|
|
303
386
|
|
|
304
387
|
for (const childTokens of nestedTokenLists(token)) {
|
|
305
|
-
await
|
|
388
|
+
await highlightCode(childTokens);
|
|
306
389
|
}
|
|
307
390
|
}
|
|
308
391
|
}
|
|
309
392
|
|
|
310
393
|
async function highlightToken(token) {
|
|
311
|
-
const lang = normalizeLanguage(token.lang);
|
|
312
394
|
try {
|
|
313
|
-
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
|
+
});
|
|
314
403
|
} catch {
|
|
315
|
-
return
|
|
404
|
+
return codeToHtml(token.text, {
|
|
405
|
+
lang: "text",
|
|
406
|
+
themes: {
|
|
407
|
+
light: "github-light",
|
|
408
|
+
dark: "github-dark",
|
|
409
|
+
},
|
|
410
|
+
defaultColor: false,
|
|
411
|
+
});
|
|
316
412
|
}
|
|
317
413
|
}
|
|
318
414
|
|
|
319
|
-
function wrapCodeBlock(html) {
|
|
320
|
-
return `<div class="relative my-6 w-full max-w-full overflow-hidden rounded-lg" data-code-block>${html}</div>`;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
415
|
function normalizeLanguage(lang) {
|
|
324
416
|
const value = lang?.trim().split(/\s+/)[0].toLowerCase() || "text";
|
|
325
417
|
if (value === "njk") return "html";
|
|
@@ -349,6 +441,7 @@ function plainText(tokens) {
|
|
|
349
441
|
function tokensText(tokens) {
|
|
350
442
|
return tokens
|
|
351
443
|
.map((token) => {
|
|
444
|
+
if (token.type === "html") return htmlText(token.raw || token.text || "");
|
|
352
445
|
const childTokens = [];
|
|
353
446
|
if (token.tokens) childTokens.push(token.tokens);
|
|
354
447
|
if (token.items) childTokens.push(...token.items.flatMap((item) => (item.tokens ? [item.tokens] : [])));
|
|
@@ -360,3 +453,16 @@ function tokensText(tokens) {
|
|
|
360
453
|
.replace(/\s+/g, " ")
|
|
361
454
|
.trim();
|
|
362
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,5 +1,6 @@
|
|
|
1
1
|
import Search from "lucide-static/icons/search.svg?raw";
|
|
2
|
-
import
|
|
2
|
+
import ArrowUpRight from "lucide-static/icons/arrow-up-right.svg?raw";
|
|
3
|
+
import { addSvgClass, attrs, escapeHtml } from "./html.js";
|
|
3
4
|
|
|
4
5
|
export function renderSidebar({
|
|
5
6
|
id = "sidebar",
|
|
@@ -58,16 +59,24 @@ export function renderSidebarContent(items = [], parentIdPrefix = "content") {
|
|
|
58
59
|
<a${attrs({ href: item.url, "aria-current": item.current ? "page" : undefined, ...without(item.attrs, ["href", "aria-current"]) })}>
|
|
59
60
|
${item.icon || ""}
|
|
60
61
|
<span>${escapeHtml(item.label)}</span>
|
|
62
|
+
${renderSidebarMeta(item)}
|
|
61
63
|
</a>
|
|
62
64
|
</li>`;
|
|
63
65
|
})
|
|
64
66
|
.join("");
|
|
65
67
|
}
|
|
66
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
|
+
|
|
67
76
|
export function renderCommandDialog({
|
|
68
77
|
id = "command-search",
|
|
69
78
|
items = [],
|
|
70
|
-
placeholder = "Search
|
|
79
|
+
placeholder = "Search...",
|
|
71
80
|
emptyText = "No results found.",
|
|
72
81
|
open = false,
|
|
73
82
|
commandAttrs = {},
|
|
@@ -0,0 +1,163 @@
|
|
|
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
|
+
}
|
|
9
|
+
|
|
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, "");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function transformMarkdownTextSegments(markdown, transform) {
|
|
17
|
+
return String(markdown || "")
|
|
18
|
+
.split(/(```[\s\S]*?```)/g)
|
|
19
|
+
.map((part) => (part.startsWith("```") ? part : transform(part)))
|
|
20
|
+
.join("");
|
|
21
|
+
}
|
|
22
|
+
|
|
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
|
+
};
|
|
34
|
+
|
|
35
|
+
export function normalizeMdxComponents(content, context = new Map()) {
|
|
36
|
+
let out = String(content || "");
|
|
37
|
+
const previewBlocks = [];
|
|
38
|
+
|
|
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
|
+
});
|
|
44
|
+
|
|
45
|
+
for (const name of ["CodeGroup", "Tabs", "Tab", "CardGrid", "Callout", "Steps", "Code"]) {
|
|
46
|
+
out = replacePairedComponent(out, name, (attrs, children) => serializers[name](attrs, children, context));
|
|
47
|
+
}
|
|
48
|
+
|
|
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;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function replacePairedComponent(content, name, serialize) {
|
|
59
|
+
return content.replace(new RegExp(`<${name}\\b([^>]*)>([\\s\\S]*?)<\\/${name}>`, "g"), (_, attrs, children) =>
|
|
60
|
+
serialize(attrs, children),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function serializeChildren(_attrs, children, context = new Map()) {
|
|
65
|
+
return `\n\n${normalizeMdxComponents(children, context).trim()}\n\n`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function serializePreview(attrs, children, context = new Map()) {
|
|
69
|
+
const lang = getStringAttr(attrs, "lang") || "mdx";
|
|
70
|
+
const code = getExpressionAttr(attrs, "code");
|
|
71
|
+
const source = stripCommonIndent(code && context.has(code) ? context.get(code) : children).trim();
|
|
72
|
+
return `\n\n${fencedCode(lang, source)}\n\n`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function serializeCallout(attrs, children, context = new Map()) {
|
|
76
|
+
const title = getStringAttr(attrs, "title") || calloutTitle(getStringAttr(attrs, "type"));
|
|
77
|
+
const body = normalizeMdxComponents(children, context)
|
|
78
|
+
.trim()
|
|
79
|
+
.split("\n")
|
|
80
|
+
.map((line) => `> ${line}`)
|
|
81
|
+
.join("\n");
|
|
82
|
+
return `\n\n> **${title}**\n${body ? `${body}\n` : ""}\n`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function serializeSteps(_attrs, children, context = new Map()) {
|
|
86
|
+
let index = 0;
|
|
87
|
+
const steps = children.replace(/<Step\b([^>]*)>([\s\S]*?)<\/Step>/g, (_step, attrs, body) => {
|
|
88
|
+
index += 1;
|
|
89
|
+
const title = getStringAttr(attrs, "title");
|
|
90
|
+
const text = normalizeMdxComponents(body, context).trim();
|
|
91
|
+
return `${index}. ${title ? `**${title}**` : ""}${text ? `\n\n${indentMarkdown(text, " ")}` : ""}`;
|
|
92
|
+
});
|
|
93
|
+
return `\n\n${steps}\n\n`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function serializeCode(attrs, children, context = new Map()) {
|
|
97
|
+
const lang = getStringAttr(attrs, "lang") || "text";
|
|
98
|
+
const title = getStringAttr(attrs, "title");
|
|
99
|
+
const label = title ? ` ${title}` : "";
|
|
100
|
+
const code = unwrapMdxString(children, context).trim();
|
|
101
|
+
return `\n\n${fencedCode(`${lang}${label}`, code)}\n\n`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function serializeCard(attrs) {
|
|
105
|
+
const title = getStringAttr(attrs, "title") || "Card";
|
|
106
|
+
const description = getStringAttr(attrs, "description");
|
|
107
|
+
const href = getStringAttr(attrs, "href");
|
|
108
|
+
const label = description ? `**${title}**: ${description}` : `**${title}**`;
|
|
109
|
+
return href ? `\n\n- [${label}](${href})\n` : `\n\n- ${label}\n`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function getStringAttr(attrs, name) {
|
|
113
|
+
return String(attrs || "").match(new RegExp(`${name}=["']([^"']*)["']`))?.[1] || "";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getExpressionAttr(attrs, name) {
|
|
117
|
+
return String(attrs || "").match(new RegExp(`${name}=\\{([A-Za-z_$][\\w$]*)\\}`))?.[1] || "";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function getCodeAttr(attrs, context = new Map()) {
|
|
121
|
+
const direct = String(attrs || "").match(/\bcode=\{`([\s\S]*?)`\}/)?.[1] || "";
|
|
122
|
+
if (direct) return direct.replace(/\\n/g, "\n").replace(/\\`/g, "`");
|
|
123
|
+
const ref = getExpressionAttr(attrs, "code");
|
|
124
|
+
return ref && context.has(ref) ? context.get(ref) : "";
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function unwrapMdxString(value, context = new Map()) {
|
|
128
|
+
const text = String(value || "").trim();
|
|
129
|
+
const ref = text.match(/^\{([A-Za-z_$][\w$]*)\}$/)?.[1];
|
|
130
|
+
if (ref && context.has(ref)) return context.get(ref);
|
|
131
|
+
const template = text.match(/^\{`([\s\S]*)`\}$/);
|
|
132
|
+
if (template) return template[1].replace(/\\n/g, "\n").replace(/\\`/g, "`");
|
|
133
|
+
return text.replace(/^\{["']([\s\S]*)["']\}$/, "$1");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function calloutTitle(type) {
|
|
137
|
+
if (type === "warning") return "Warning";
|
|
138
|
+
if (type === "destructive") return "Danger";
|
|
139
|
+
return "Note";
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function indentMarkdown(markdown, prefix) {
|
|
143
|
+
return String(markdown || "")
|
|
144
|
+
.split("\n")
|
|
145
|
+
.map((line) => `${prefix}${line}`)
|
|
146
|
+
.join("\n");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function fencedCode(info, code) {
|
|
150
|
+
const fence = longestBacktickRun(code) >= 3 ? "`".repeat(longestBacktickRun(code) + 1) : "```";
|
|
151
|
+
return `${fence}${info}\n${code}\n${fence}`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function longestBacktickRun(value) {
|
|
155
|
+
return Math.max(0, ...String(value || "").matchAll(/`+/g).map((match) => match[0].length));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function stripCommonIndent(value) {
|
|
159
|
+
const lines = String(value || "").replace(/^\n+|\n+$/g, "").split("\n");
|
|
160
|
+
const indents = lines.filter((line) => line.trim()).map((line) => line.match(/^[ \t]*/)[0].length);
|
|
161
|
+
const indent = indents.length ? Math.min(...indents) : 0;
|
|
162
|
+
return indent ? lines.map((line) => line.slice(Math.min(indent, line.match(/^[ \t]*/)[0].length))).join("\n") : lines.join("\n");
|
|
163
|
+
}
|
package/src/runtime/lib/site.js
CHANGED
|
@@ -7,7 +7,9 @@ export function GET() {
|
|
|
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 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/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}";
|