|
1 | 1 | 'use client'; |
2 | 2 |
|
3 | 3 | import * as React from 'react'; |
| 4 | +import GitHubSlugger from 'github-slugger'; |
4 | 5 | import { List, X } from 'lucide-react'; |
5 | 6 | import { cn } from '@/lib/utils'; |
6 | 7 |
|
@@ -56,9 +57,9 @@ export function TableOfContents({ items = [] }: TableOfContentsProps) { |
56 | 57 | <div className="space-y-2"> |
57 | 58 | <p className="font-medium">On This Page</p> |
58 | 59 | <ul className="m-0 list-none text-sm"> |
59 | | - {items.map((item) => ( |
| 60 | + {items.map((item, index) => ( |
60 | 61 | <li |
61 | | - key={item.id} |
| 62 | + key={`${item.id}-${index}`} |
62 | 63 | className={cn( |
63 | 64 | 'mt-0 pt-2', |
64 | 65 | item.level === 3 && 'pl-4' |
@@ -125,19 +126,19 @@ export function MobileTOC({ items = [] }: TableOfContentsProps) { |
125 | 126 | ); |
126 | 127 | } |
127 | 128 |
|
128 | | -// Utility to extract headings from content |
| 129 | +// Utility to extract headings from content (use @/lib/mdx extractHeadings for doc pages). |
| 130 | +// Uses github-slugger so ids are unique and match rehype-slug output. |
129 | 131 | export function extractHeadings(content: string): TocItem[] { |
130 | 132 | const headingRegex = /^(#{2,3})\s+(.+)$/gm; |
131 | 133 | const headings: TocItem[] = []; |
| 134 | + const slugger = new GitHubSlugger(); |
132 | 135 | let match; |
133 | 136 |
|
134 | 137 | while ((match = headingRegex.exec(content)) !== null) { |
135 | 138 | const level = match[1].length; |
136 | | - const text = match[2]; |
137 | | - const id = text |
138 | | - .toLowerCase() |
139 | | - .replace(/[^a-z0-9]+/g, '-') |
140 | | - .replace(/(^-|-$)/g, ''); |
| 139 | + const rawText = match[2]; |
| 140 | + const text = rawText.replace(/\*\*/g, '').replace(/`/g, '').trim(); |
| 141 | + const id = slugger.slug(text); |
141 | 142 |
|
142 | 143 | headings.push({ id, text, level }); |
143 | 144 | } |
|
0 commit comments