@scribdown/markdown-renderer 0.2.0 → 0.3.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/dist/index.js CHANGED
@@ -1172,10 +1172,12 @@ var SOURCE_LINE_HAST_PROPERTY = SOURCE_LINE_DATA_ATTRIBUTE3.replace(
1172
1172
  /-([a-z])/gu,
1173
1173
  (_match, letter) => letter.toUpperCase()
1174
1174
  );
1175
- var BLOCK_CONTAINER_NODE_TYPES = /* @__PURE__ */ new Set(["list", "listItem", "blockquote"]);
1175
+ function isBlockContainerNode(node) {
1176
+ return node.type === "blockquote" || node.type === "list" || node.type === "listItem";
1177
+ }
1176
1178
  function remarkSourceLine() {
1177
1179
  return (tree) => {
1178
- const blockNodes = tree.children ?? [];
1180
+ const blockNodes = tree.children;
1179
1181
  blockNodes.forEach((blockNode) => {
1180
1182
  annotateSourceLine(blockNode);
1181
1183
  annotateNestedBlockSourceLines(blockNode);
@@ -1194,10 +1196,10 @@ function annotateSourceLine(node) {
1194
1196
  node.data = nodeData;
1195
1197
  }
1196
1198
  function annotateNestedBlockSourceLines(node) {
1197
- if (!BLOCK_CONTAINER_NODE_TYPES.has(node.type)) {
1199
+ if (!isBlockContainerNode(node)) {
1198
1200
  return;
1199
1201
  }
1200
- const childNodes = node.children ?? [];
1202
+ const childNodes = node.children;
1201
1203
  childNodes.forEach((childNode) => {
1202
1204
  annotateSourceLine(childNode);
1203
1205
  annotateNestedBlockSourceLines(childNode);
@@ -1217,29 +1219,12 @@ import {
1217
1219
  IMAGE_FRAME_FAILED_CLASS_NAME,
1218
1220
  IMAGE_FRAME_LOADED_CLASS_NAME
1219
1221
  } from "@scribdown/shared";
1222
+ import { SKIP, visit } from "unist-util-visit";
1220
1223
 
1221
1224
  // src/core/ast.ts
1222
- function visitMarkdownNode(node, visitor) {
1223
- visitor(node);
1224
- if (!node.children) {
1225
- return;
1226
- }
1227
- const childNodes = node.children;
1228
- for (let childIndex = 0; childIndex < childNodes.length; childIndex += 1) {
1229
- visitMarkdownNode(childNodes[childIndex], visitor);
1230
- }
1231
- }
1225
+ import { toString as mdastToString } from "mdast-util-to-string";
1232
1226
  function extractNodeText(node) {
1233
- if (node.type === "text" || node.type === "inlineCode") {
1234
- return node.value ?? "";
1235
- }
1236
- if (isImageNode(node)) {
1237
- return node.alt ?? "";
1238
- }
1239
- if (!node.children) {
1240
- return "";
1241
- }
1242
- return node.children.map(extractNodeText).join("");
1227
+ return mdastToString(node, { includeHtml: false });
1243
1228
  }
1244
1229
  function isImageNode(node) {
1245
1230
  return node.type === "image" || node.type === "imageReference";
@@ -1742,30 +1727,23 @@ function createMarkdownImageViewerCaption(imageElement, imageSource) {
1742
1727
  var IMAGE_HYDRATED_DATA_KEY = "scribdownImageHydrated";
1743
1728
  function remarkImageFigures() {
1744
1729
  return (tree) => {
1745
- transformImageFigures(tree);
1730
+ visit(tree, "paragraph", (node, index, parent) => {
1731
+ if (parent === void 0 || index === void 0) {
1732
+ return;
1733
+ }
1734
+ const imageFigureNode = createImageFigureNode(node);
1735
+ if (imageFigureNode) {
1736
+ parent.children[index] = imageFigureNode;
1737
+ return SKIP;
1738
+ }
1739
+ });
1740
+ visit(tree, isImageNode, (node) => {
1741
+ decorateImageNode(node);
1742
+ });
1746
1743
  };
1747
1744
  }
1748
- function transformImageFigures(node) {
1749
- if (!node.children) {
1750
- return;
1751
- }
1752
- const childNodes = node.children;
1753
- for (let childIndex = 0; childIndex < childNodes.length; childIndex += 1) {
1754
- const childNode = childNodes[childIndex];
1755
- const imageFigureNode = createImageFigureNode(childNode);
1756
- if (imageFigureNode) {
1757
- childNodes[childIndex] = imageFigureNode;
1758
- continue;
1759
- }
1760
- if (isImageNode(childNode)) {
1761
- decorateImageNode(childNode);
1762
- continue;
1763
- }
1764
- transformImageFigures(childNode);
1765
- }
1766
- }
1767
1745
  function createImageFigureNode(node) {
1768
- if (node.type !== "paragraph" || !node.children || node.children.length !== 1) {
1746
+ if (node.children.length !== 1) {
1769
1747
  return void 0;
1770
1748
  }
1771
1749
  const onlyChild = node.children[0];
@@ -1808,7 +1786,7 @@ function createImageFrameNode(imageNode) {
1808
1786
  };
1809
1787
  }
1810
1788
  function createImageCaptionNodes(imageNode) {
1811
- const imageTitle = typeof imageNode.title === "string" ? imageNode.title.trim() : "";
1789
+ const imageTitle = imageNode.type === "image" && typeof imageNode.title === "string" ? imageNode.title.trim() : "";
1812
1790
  if (imageTitle.length === 0) {
1813
1791
  return [];
1814
1792
  }
@@ -1827,7 +1805,7 @@ function createImageCaptionNodes(imageNode) {
1827
1805
  }
1828
1806
  function createImageFallbackNode(imageNode) {
1829
1807
  const fallbackText = imageNode.alt?.trim() || "\u56FE\u7247\u52A0\u8F7D\u5931\u8D25";
1830
- const fallbackSource = imageNode.url ?? imageNode.identifier ?? "";
1808
+ const fallbackSource = imageNode.type === "image" ? imageNode.url : imageNode.identifier;
1831
1809
  return {
1832
1810
  type: "imageFallback",
1833
1811
  data: {
@@ -1916,41 +1894,37 @@ import {
1916
1894
  VIDEO_FRAME_FAILED_CLASS_NAME,
1917
1895
  VIDEO_FRAME_LOADED_CLASS_NAME
1918
1896
  } from "@scribdown/shared";
1897
+ import { classnames } from "hast-util-classnames";
1898
+ import { SKIP as SKIP2, visit as visit2 } from "unist-util-visit";
1919
1899
  var VIDEO_HYDRATED_DATA_KEY = "scribdownVideoHydrated";
1920
1900
  var VIDEO_FALLBACK_DEFAULT_TEXT = "\u89C6\u9891\u52A0\u8F7D\u5931\u8D25";
1921
1901
  function rehypeVideoFigures() {
1922
1902
  return (tree) => {
1923
- transformVideoFigures(tree);
1903
+ visit2(tree, "element", (node, index, parent) => {
1904
+ if (parent === void 0 || index === void 0) {
1905
+ return;
1906
+ }
1907
+ if (isHastVideoElement(node)) {
1908
+ parent.children[index] = createVideoFigureHast(node);
1909
+ return SKIP2;
1910
+ }
1911
+ const standaloneVideo = extractStandaloneParagraphVideo(node);
1912
+ if (standaloneVideo) {
1913
+ parent.children[index] = createVideoFigureHast(standaloneVideo);
1914
+ return SKIP2;
1915
+ }
1916
+ });
1924
1917
  };
1925
1918
  }
1926
- function transformVideoFigures(node) {
1927
- if (!Array.isArray(node.children)) {
1928
- return;
1929
- }
1930
- const childNodes = node.children;
1931
- for (let childIndex = 0; childIndex < childNodes.length; childIndex += 1) {
1932
- const childNode = childNodes[childIndex];
1933
- if (isHastVideoElement(childNode)) {
1934
- childNodes[childIndex] = createVideoFigureHast(childNode);
1935
- continue;
1936
- }
1937
- const standaloneVideo = extractStandaloneParagraphVideo(childNode);
1938
- if (standaloneVideo) {
1939
- childNodes[childIndex] = createVideoFigureHast(standaloneVideo);
1940
- continue;
1941
- }
1942
- transformVideoFigures(childNode);
1943
- }
1944
- }
1945
1919
  function isHastVideoElement(node) {
1946
1920
  return node.type === "element" && node.tagName === "video";
1947
1921
  }
1948
1922
  function extractStandaloneParagraphVideo(node) {
1949
- if (node.type !== "element" || node.tagName !== "p" || !Array.isArray(node.children)) {
1923
+ if (node.tagName !== "p") {
1950
1924
  return void 0;
1951
1925
  }
1952
1926
  const significantChildren = node.children.filter((childNode) => {
1953
- if (childNode.type === "text" && typeof childNode.value === "string") {
1927
+ if (childNode.type === "text") {
1954
1928
  return childNode.value.trim().length > 0;
1955
1929
  }
1956
1930
  return true;
@@ -1978,25 +1952,15 @@ function createVideoFigureHast(videoNode) {
1978
1952
  };
1979
1953
  }
1980
1954
  function decorateHastVideoElement(videoNode) {
1981
- const properties = videoNode.properties ?? {};
1982
- const existingClassName = properties.className;
1983
- const classList = Array.isArray(existingClassName) ? existingClassName.map(String) : typeof existingClassName === "string" ? [existingClassName] : [];
1984
- if (!classList.includes(VIDEO_ELEMENT_CLASS_NAME)) {
1985
- classList.push(VIDEO_ELEMENT_CLASS_NAME);
1986
- }
1987
- properties.className = classList;
1988
- videoNode.properties = properties;
1955
+ classnames(videoNode, VIDEO_ELEMENT_CLASS_NAME);
1989
1956
  }
1990
1957
  function readHastVideoSourceUrl(videoNode) {
1991
- const properties = videoNode.properties ?? {};
1992
- if (typeof properties.src === "string") {
1993
- return properties.src;
1994
- }
1995
- if (Array.isArray(videoNode.children)) {
1996
- for (const childNode of videoNode.children) {
1997
- if (childNode.type === "element" && childNode.tagName === "source" && typeof childNode.properties?.src === "string") {
1998
- return childNode.properties.src;
1999
- }
1958
+ if (typeof videoNode.properties.src === "string") {
1959
+ return videoNode.properties.src;
1960
+ }
1961
+ for (const childNode of videoNode.children) {
1962
+ if (childNode.type === "element" && childNode.tagName === "source" && typeof childNode.properties.src === "string") {
1963
+ return childNode.properties.src;
2000
1964
  }
2001
1965
  }
2002
1966
  return "";
@@ -2278,31 +2242,22 @@ function createScribdownSanitizeSchema() {
2278
2242
  }
2279
2243
 
2280
2244
  // src/syntax/definition-lists.ts
2245
+ import { visit as visit3 } from "unist-util-visit";
2281
2246
  var DEFINITION_LIST_PARAGRAPH_PATTERN = /^\s*([^\n:][^\n]*)\n\s*:\s+([^\n]+)\s*$/u;
2282
2247
  function remarkDefinitionLists() {
2283
2248
  return (tree) => {
2284
- transformDefinitionLists(tree);
2249
+ visit3(tree, "paragraph", (node, index, parent) => {
2250
+ if (parent === void 0 || index === void 0) {
2251
+ return;
2252
+ }
2253
+ const definitionListNode = createDefinitionListNode(node);
2254
+ if (definitionListNode) {
2255
+ parent.children[index] = definitionListNode;
2256
+ }
2257
+ });
2285
2258
  };
2286
2259
  }
2287
- function transformDefinitionLists(node) {
2288
- if (!node.children) {
2289
- return;
2290
- }
2291
- const childNodes = node.children;
2292
- for (let childIndex = 0; childIndex < childNodes.length; childIndex += 1) {
2293
- const childNode = childNodes[childIndex];
2294
- const definitionListNode = createDefinitionListNode(childNode);
2295
- if (definitionListNode) {
2296
- childNodes[childIndex] = definitionListNode;
2297
- continue;
2298
- }
2299
- transformDefinitionLists(childNode);
2300
- }
2301
- }
2302
2260
  function createDefinitionListNode(node) {
2303
- if (node.type !== "paragraph") {
2304
- return void 0;
2305
- }
2306
2261
  const paragraphText = extractNodeText(node);
2307
2262
  const definitionMatch = DEFINITION_LIST_PARAGRAPH_PATTERN.exec(paragraphText);
2308
2263
  if (!definitionMatch) {
@@ -2341,25 +2296,21 @@ function createDefinitionListNode(node) {
2341
2296
 
2342
2297
  // src/syntax/frame.ts
2343
2298
  import { DETAILS_CLASS_NAME as DETAILS_CLASS_NAME2, FRAME_CLASS_NAME as FRAME_CLASS_NAME2, TOC_CLASS_NAME as TOC_CLASS_NAME2 } from "@scribdown/shared";
2299
+ import { classnames as classnames2 } from "hast-util-classnames";
2300
+ import { visit as visit4 } from "unist-util-visit";
2344
2301
  function rehypeFrameClass() {
2345
2302
  return (tree) => {
2346
- tagContentDetails(tree);
2303
+ visit4(tree, "element", (node) => {
2304
+ if (node.tagName !== "details") {
2305
+ return;
2306
+ }
2307
+ const classNames = normalizeClassNames(node.properties.className);
2308
+ if (!classNames.includes(TOC_CLASS_NAME2)) {
2309
+ classnames2(node, DETAILS_CLASS_NAME2, FRAME_CLASS_NAME2);
2310
+ }
2311
+ });
2347
2312
  };
2348
2313
  }
2349
- function tagContentDetails(node) {
2350
- if (node.type === "element" && node.tagName === "details") {
2351
- const classNames = normalizeClassNames(node.properties?.className);
2352
- const isTocDetails = classNames.includes(TOC_CLASS_NAME2);
2353
- if (!isTocDetails) {
2354
- addClassNames(node, [DETAILS_CLASS_NAME2, FRAME_CLASS_NAME2]);
2355
- }
2356
- }
2357
- if (Array.isArray(node.children)) {
2358
- for (const childNode of node.children) {
2359
- tagContentDetails(childNode);
2360
- }
2361
- }
2362
- }
2363
2314
  function normalizeClassNames(className) {
2364
2315
  if (Array.isArray(className)) {
2365
2316
  return className.filter((item) => typeof item === "string");
@@ -2369,15 +2320,6 @@ function normalizeClassNames(className) {
2369
2320
  }
2370
2321
  return [];
2371
2322
  }
2372
- function addClassNames(node, classNamesToAdd) {
2373
- const mergedClassNames = normalizeClassNames(node.properties?.className);
2374
- for (const classNameToAdd of classNamesToAdd) {
2375
- if (!mergedClassNames.includes(classNameToAdd)) {
2376
- mergedClassNames.push(classNameToAdd);
2377
- }
2378
- }
2379
- node.properties = { ...node.properties, className: mergedClassNames };
2380
- }
2381
2323
 
2382
2324
  // src/syntax/frontmatter.ts
2383
2325
  import {
@@ -2394,16 +2336,16 @@ var FRONTMATTER_ARRAY_SEPARATOR = ", ";
2394
2336
  var FRONTMATTER_FALLBACK_LANGUAGE_ID = "yaml";
2395
2337
  function remarkFrontmatterMetadata() {
2396
2338
  return (tree) => {
2397
- const childNodes = tree.children ?? [];
2339
+ const childNodes = tree.children;
2398
2340
  const yamlNode = childNodes[0];
2399
- if (!yamlNode || yamlNode.type !== "yaml" || typeof yamlNode.value !== "string") {
2341
+ if (!yamlNode || yamlNode.type !== "yaml") {
2400
2342
  return;
2401
2343
  }
2402
2344
  childNodes[0] = createFrontmatterNode(yamlNode);
2403
2345
  };
2404
2346
  }
2405
2347
  function createFrontmatterNode(yamlNode) {
2406
- const yamlSource = yamlNode.value ?? "";
2348
+ const yamlSource = yamlNode.value;
2407
2349
  let parsedValue;
2408
2350
  try {
2409
2351
  parsedValue = parseYamlSource(yamlSource);
@@ -2500,28 +2442,20 @@ function formatFrontmatterScalar(value) {
2500
2442
  }
2501
2443
 
2502
2444
  // src/syntax/highlight-mark.ts
2445
+ import { SKIP as SKIP3, visit as visit5 } from "unist-util-visit";
2503
2446
  var HIGHLIGHT_MARKER_PATTERN = /==([^=\n]+?)==/g;
2504
2447
  function remarkHighlightMark() {
2505
2448
  return (tree) => {
2506
- transformHighlightMarks(tree);
2449
+ visit5(tree, "text", (node, index, parent) => {
2450
+ if (parent === void 0 || index === void 0 || !node.value.includes("==")) {
2451
+ return;
2452
+ }
2453
+ const segments = splitHighlightMarks(node.value);
2454
+ parent.children.splice(index, 1, ...segments);
2455
+ return [SKIP3, index + segments.length];
2456
+ });
2507
2457
  };
2508
2458
  }
2509
- function transformHighlightMarks(node) {
2510
- if (!node.children) {
2511
- return;
2512
- }
2513
- const nextChildren = [];
2514
- for (let childIndex = 0; childIndex < node.children.length; childIndex += 1) {
2515
- const childNode = node.children[childIndex];
2516
- if (childNode.type === "text" && typeof childNode.value === "string" && childNode.value.includes("==")) {
2517
- nextChildren.push(...splitHighlightMarks(childNode.value));
2518
- continue;
2519
- }
2520
- transformHighlightMarks(childNode);
2521
- nextChildren.push(childNode);
2522
- }
2523
- node.children = nextChildren;
2524
- }
2525
2459
  function splitHighlightMarks(textValue) {
2526
2460
  const segments = [];
2527
2461
  let cursor = 0;
@@ -2565,6 +2499,8 @@ import {
2565
2499
  TOC_SUMMARY_CLASS_NAME as TOC_SUMMARY_CLASS_NAME2,
2566
2500
  TOC_TOGGLE_CLASS_NAME as TOC_TOGGLE_CLASS_NAME2
2567
2501
  } from "@scribdown/shared";
2502
+ import GithubSlugger from "github-slugger";
2503
+ import { visit as visit6 } from "unist-util-visit";
2568
2504
  var TOC_MARKER_PATTERN = /^\s*\[toc]\s*$/i;
2569
2505
  var TOC_TOGGLE_ARIA_LABEL = "\u5C55\u5F00\u6216\u6298\u53E0\u5B50\u76EE\u5F55";
2570
2506
  var TOC_TOGGLE_HYDRATED_DATA_KEY = "scribdownTocToggleHydrated";
@@ -2579,17 +2515,14 @@ function remarkTableOfContents() {
2579
2515
  };
2580
2516
  }
2581
2517
  function collectTocHeadings(tree) {
2582
- const headingSlugCounts = /* @__PURE__ */ new Map();
2518
+ const headingSlugger = new GithubSlugger();
2583
2519
  const tocHeadings = [];
2584
2520
  const headingIndexCounts = Array.from({ length: 7 }, () => 0);
2585
2521
  let rootHeadingDepth;
2586
- visitMarkdownNode(tree, (node) => {
2587
- if (node.type !== "heading") {
2588
- return;
2589
- }
2590
- const headingDepth = node.depth ?? 2;
2522
+ visit6(tree, "heading", (node) => {
2523
+ const headingDepth = node.depth;
2591
2524
  const headingText = extractNodeText(node).trim();
2592
- const headingId = createUniqueSlug(headingText, headingSlugCounts, tocHeadings.length + 1);
2525
+ const headingId = createHeadingId(headingSlugger, headingText, tocHeadings.length + 1);
2593
2526
  const headingIndex = createHeadingIndex(
2594
2527
  headingDepth,
2595
2528
  headingIndexCounts,
@@ -2612,7 +2545,7 @@ function collectTocHeadings(tree) {
2612
2545
  className: [HEADING_MARK_CLASS_NAME2]
2613
2546
  }
2614
2547
  },
2615
- children: node.children ?? []
2548
+ children: node.children
2616
2549
  }
2617
2550
  ];
2618
2551
  tocHeadings.push({
@@ -2624,26 +2557,20 @@ function collectTocHeadings(tree) {
2624
2557
  });
2625
2558
  return tocHeadings;
2626
2559
  }
2627
- function replaceTocMarkers(node, tocHeadings) {
2628
- if (!node.children) {
2629
- return;
2630
- }
2631
- const childNodes = node.children;
2632
- for (let childIndex = 0; childIndex < childNodes.length; childIndex += 1) {
2633
- const childNode = childNodes[childIndex];
2634
- if (isTocMarkerParagraph(childNode)) {
2635
- childNodes[childIndex] = createTocNode(tocHeadings, childNode);
2636
- continue;
2560
+ function replaceTocMarkers(tree, tocHeadings) {
2561
+ visit6(tree, "paragraph", (node, index, parent) => {
2562
+ if (parent === void 0 || index === void 0 || !isTocMarkerParagraph(node)) {
2563
+ return;
2637
2564
  }
2638
- replaceTocMarkers(childNode, tocHeadings);
2639
- }
2565
+ parent.children[index] = createTocNode(tocHeadings, node);
2566
+ });
2640
2567
  }
2641
2568
  function isTocMarkerParagraph(node) {
2642
- if (node.type !== "paragraph" || !node.children || node.children.length !== 1) {
2569
+ if (node.children.length !== 1) {
2643
2570
  return false;
2644
2571
  }
2645
2572
  const onlyChild = node.children[0];
2646
- return onlyChild.type === "text" && TOC_MARKER_PATTERN.test(onlyChild.value ?? "");
2573
+ return onlyChild.type === "text" && TOC_MARKER_PATTERN.test(onlyChild.value);
2647
2574
  }
2648
2575
  function createTocNode(tocHeadings, markerNode) {
2649
2576
  const tocTree = createTocTree(tocHeadings);
@@ -2712,10 +2639,9 @@ function createTocTree(tocHeadings) {
2712
2639
  function createTocListNode(tocItems, isNested) {
2713
2640
  const tocListClassNames = isNested ? [TOC_LIST_CLASS_NAME2, TOC_LIST_NESTED_CLASS_NAME2] : [TOC_LIST_CLASS_NAME2];
2714
2641
  return {
2715
- type: "list",
2716
- ordered: true,
2717
- spread: false,
2642
+ type: "tocList",
2718
2643
  data: {
2644
+ hName: "ol",
2719
2645
  hProperties: {
2720
2646
  className: tocListClassNames
2721
2647
  }
@@ -2728,9 +2654,9 @@ function createTocListItem(tocItem) {
2728
2654
  const tocItemClassNames = createTocItemClassNames(tocItem.depth, hasChildren);
2729
2655
  const itemChildren = hasChildren ? [createTocToggleNode(), createTocLinkNode(tocItem), createTocListNode(tocItem.children, true)] : [createTocLinkNode(tocItem)];
2730
2656
  return {
2731
- type: "listItem",
2732
- spread: false,
2657
+ type: "tocItem",
2733
2658
  data: {
2659
+ hName: "li",
2734
2660
  hProperties: {
2735
2661
  dataTocIndex: tocItem.index,
2736
2662
  className: tocItemClassNames
@@ -2778,16 +2704,12 @@ function createTocLinkNode(tocItem) {
2778
2704
  ]
2779
2705
  };
2780
2706
  }
2781
- function createUniqueSlug(headingText, slugCounts, fallbackIndex) {
2782
- const normalizedSlug = normalizeSlugText(headingText);
2783
- const fallbackSlug = `${EMPTY_HEADING_SLUG_PREFIX}-${fallbackIndex}`;
2784
- const baseSlug = normalizedSlug || fallbackSlug;
2785
- const usedCount = slugCounts.get(baseSlug) ?? 0;
2786
- slugCounts.set(baseSlug, usedCount + 1);
2787
- if (usedCount === 0) {
2707
+ function createHeadingId(headingSlugger, headingText, fallbackIndex) {
2708
+ const baseSlug = headingSlugger.slug(headingText);
2709
+ if (baseSlug) {
2788
2710
  return baseSlug;
2789
2711
  }
2790
- return `${baseSlug}-${usedCount}`;
2712
+ return headingSlugger.slug(`${EMPTY_HEADING_SLUG_PREFIX}-${fallbackIndex}`);
2791
2713
  }
2792
2714
  function createHeadingIndex(headingDepth, headingIndexCounts, rootHeadingDepth) {
2793
2715
  const startDepth = Math.min(rootHeadingDepth, headingDepth);
@@ -2802,9 +2724,6 @@ function createHeadingIndex(headingDepth, headingIndexCounts, rootHeadingDepth)
2802
2724
  }
2803
2725
  return headingIndexCounts.slice(startDepth, headingDepth + 1).join(".");
2804
2726
  }
2805
- function normalizeSlugText(headingText) {
2806
- return headingText.trim().toLowerCase().replace(/<[^>]+>/g, "").replace(/[^\p{L}\p{N}\s_-]+/gu, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
2807
- }
2808
2727
  function collectTocHeadingsFromDom(rootElement) {
2809
2728
  const markdownContainer = rootElement instanceof Element && rootElement.matches(`.${SCRIBDOWN_MARKDOWN_CLASS_NAME}`) ? rootElement : rootElement.querySelector(`.${SCRIBDOWN_MARKDOWN_CLASS_NAME}`);
2810
2729
  if (!markdownContainer) {
@@ -3139,13 +3058,14 @@ function mountPageToolbar(ownerDocument, container, scrollToHeading) {
3139
3058
  ownerWindow?.scrollTo({ top: 0, behavior: "smooth" });
3140
3059
  closeMoreMenu();
3141
3060
  });
3142
- const aboutItem = ownerDocument.createElement("button");
3143
- aboutItem.type = "button";
3061
+ const aboutItem = ownerDocument.createElement("a");
3062
+ aboutItem.href = PROJECT_HOMEPAGE_URL;
3063
+ aboutItem.target = "_blank";
3064
+ aboutItem.rel = "noopener noreferrer";
3144
3065
  aboutItem.className = SCRIBDOWN_TOOLBAR_MENU_ITEM_CLASS_NAME;
3145
3066
  aboutItem.setAttribute("role", "menuitem");
3146
3067
  aboutItem.innerHTML = `${TOOLBAR_ABOUT_ICON_SVG}<span>\u5173\u4E8E</span>`;
3147
3068
  aboutItem.addEventListener("click", () => {
3148
- ownerWindow?.open(PROJECT_HOMEPAGE_URL, "_blank", "noopener,noreferrer");
3149
3069
  closeMoreMenu();
3150
3070
  });
3151
3071
  menu.appendChild(backTopItem);