diff --git a/docs/app/components/DocsDemo.vue b/docs/app/components/DocsDemo.vue
index e81d5b3..8e1962c 100644
--- a/docs/app/components/DocsDemo.vue
+++ b/docs/app/components/DocsDemo.vue
@@ -1,4 +1,5 @@
-
-
+
diff --git a/docs/app/components/DocsParamsTable.vue b/docs/app/components/DocsParamsTable.vue
index 1ac9099..3ddae0a 100644
--- a/docs/app/components/DocsParamsTable.vue
+++ b/docs/app/components/DocsParamsTable.vue
@@ -6,7 +6,7 @@ defineProps<{
-
+
diff --git a/docs/app/components/DocsPropsTable.vue b/docs/app/components/DocsPropsTable.vue
index 0cd4c7e..399e4ee 100644
--- a/docs/app/components/DocsPropsTable.vue
+++ b/docs/app/components/DocsPropsTable.vue
@@ -8,7 +8,7 @@ defineProps<{
-
+
diff --git a/docs/app/composables/useDocs.ts b/docs/app/composables/useDocs.ts
index 6a7e348..56d4a55 100644
--- a/docs/app/composables/useDocs.ts
+++ b/docs/app/composables/useDocs.ts
@@ -2,6 +2,7 @@ import metadata from '#docs/metadata';
import type {
CategoryMeta,
ComponentMeta,
+ DocSection,
DocsMetadata,
GuideSection,
ItemMeta,
@@ -13,7 +14,8 @@ import type {
export type DocEntry
= | { kind: 'api'; pkg: PackageMeta; category: CategoryMeta; item: ItemMeta }
| { kind: 'components'; pkg: PackageMeta; component: ComponentMeta }
- | { kind: 'guide'; pkg: PackageMeta; section: GuideSection };
+ | { kind: 'guide'; pkg: PackageMeta; section: GuideSection }
+ | { kind: 'doc'; pkg: PackageMeta; section: DocSection };
export interface SearchResult {
pkg: PackageMeta;
@@ -62,11 +64,25 @@ export function useDocs() {
return pkg.sections.length;
}
+ /** The hand-authored intro section for a package, if any. */
+ function getIntro(pkg: PackageMeta): DocSection | undefined {
+ return pkg.docs.find(s => s.isIntro);
+ }
+
+ /** Non-intro doc sections (the "Guide" list shown in the sidebar). */
+ function getDocSections(pkg: PackageMeta): DocSection[] {
+ return pkg.docs.filter(s => !s.isIntro);
+ }
+
/** Resolve any `/:package/:slug` route to a normalised entry. */
function resolveEntry(packageSlug: string, slug: string): DocEntry | undefined {
const pkg = getPackage(packageSlug);
if (!pkg) return undefined;
+ // Hand-authored doc sections take precedence over auto-generated leaves.
+ const docSection = pkg.docs.find(s => !s.isIntro && s.slug === slug);
+ if (docSection) return { kind: 'doc', pkg, section: docSection };
+
if (pkg.kind === 'api') {
for (const category of pkg.categories) {
const item = category.items.find(i => i.slug === slug);
@@ -139,6 +155,8 @@ export function useDocs() {
countEntries,
resolveEntry,
firstEntrySlug,
+ getIntro,
+ getDocSections,
search,
getTotalItems,
};
diff --git a/docs/app/composables/useMarkdown.ts b/docs/app/composables/useMarkdown.ts
index aa3bf0e..c7ac126 100644
--- a/docs/app/composables/useMarkdown.ts
+++ b/docs/app/composables/useMarkdown.ts
@@ -9,10 +9,10 @@ export interface Heading {
export function slugHeading(text: string): string {
return text
.toLowerCase()
- .replace(/`/g, '')
- .replace(/[^\w\s-]/g, '')
+ .replaceAll('`', '')
+ .replaceAll(/[^\w\s-]/g, '')
.trim()
- .replace(/\s+/g, '-');
+ .replaceAll(/\s+/g, '-');
}
/** Collect h2/h3 headings for the table of contents. */
@@ -28,11 +28,13 @@ export function extractHeadings(markdown: string): Heading[] {
}
if (inFence) continue;
- const m = line.match(/^(#{2,3})\s+(.+?)\s*#*$/);
+ const m = line.match(/^(#{2,3})\s+(\S.*)$/);
if (!m) continue;
const depth = m[1]!.length;
- const text = m[2]!.replace(/`/g, '').trim();
+ // Strip an optional ATX closing run (a single space then trailing `#`s) without
+ // a backtracking-prone pattern, then drop inline code ticks.
+ const text = m[2]!.replace(/ #+ *$/, '').replaceAll('`', '').trim();
let id = slugHeading(text);
const count = seen.get(id) ?? 0;
seen.set(id, count + 1);
@@ -51,7 +53,7 @@ export function renderMarkdown(markdown: string): string {
const renderer = new marked.Renderer();
renderer.heading = function ({ tokens, depth }) {
const inner = this.parser.parseInline(tokens);
- const plain = inner.replace(/<[^>]+>/g, '');
+ const plain = inner.replaceAll(/<[^>]+>/g, '');
let id = slugHeading(plain);
const count = seen.get(id) ?? 0;
seen.set(id, count + 1);
diff --git a/docs/app/layouts/default.vue b/docs/app/layouts/default.vue
index 34f51b5..0c454ea 100644
--- a/docs/app/layouts/default.vue
+++ b/docs/app/layouts/default.vue
@@ -1,4 +1,4 @@
-