@astrojs/starlight 0.5.6 → 0.6.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.
Files changed (76) hide show
  1. package/404.astro +17 -17
  2. package/CHANGELOG.md +40 -2
  3. package/components/CallToAction.astro +32 -35
  4. package/components/ContentPanel.astro +18 -18
  5. package/components/EditLink.astro +23 -23
  6. package/components/FallbackContentNotice.astro +16 -18
  7. package/components/Footer.astro +28 -34
  8. package/components/HeadSEO.astro +69 -75
  9. package/components/Header.astro +47 -51
  10. package/components/Hero.astro +108 -121
  11. package/components/Icons.ts +73 -71
  12. package/components/LanguageSelect.astro +31 -31
  13. package/components/LastUpdated.astro +16 -18
  14. package/components/MarkdownContent.astro +105 -117
  15. package/components/MobileMenuToggle.astro +80 -80
  16. package/components/PrevNextLinks.astro +60 -60
  17. package/components/RightSidebar.astro +17 -17
  18. package/components/RightSidebarPanel.astro +41 -41
  19. package/components/Search.astro +293 -306
  20. package/components/Select.astro +64 -65
  21. package/components/Sidebar.astro +23 -23
  22. package/components/SidebarSublist.astro +96 -95
  23. package/components/SiteTitle.astro +65 -63
  24. package/components/SkipLink.astro +17 -17
  25. package/components/SocialIcons.astro +36 -34
  26. package/components/TableOfContents/MobileTableOfContents.astro +124 -124
  27. package/components/TableOfContents/TableOfContentsList.astro +64 -69
  28. package/components/TableOfContents/generateToC.ts +41 -43
  29. package/components/TableOfContents/starlight-toc.ts +84 -90
  30. package/components/TableOfContents.astro +8 -8
  31. package/components/ThemeProvider.astro +28 -32
  32. package/components/ThemeSelect.astro +66 -71
  33. package/global.d.ts +3 -3
  34. package/index.astro +1 -1
  35. package/index.ts +52 -59
  36. package/integrations/asides.ts +109 -111
  37. package/integrations/sitemap.ts +10 -13
  38. package/integrations/virtual-user-config.ts +37 -37
  39. package/layout/Page.astro +84 -72
  40. package/layout/PageFrame.astro +67 -69
  41. package/layout/TwoColumnContent.astro +40 -42
  42. package/package.json +2 -2
  43. package/schema.ts +126 -113
  44. package/schemas/favicon.ts +40 -0
  45. package/schemas/head.ts +12 -23
  46. package/schemas/i18n.ts +146 -160
  47. package/schemas/logo.ts +22 -22
  48. package/schemas/prevNextLink.ts +14 -14
  49. package/schemas/tableOfContents.ts +14 -18
  50. package/style/asides.css +27 -27
  51. package/style/props.css +168 -172
  52. package/style/reset.css +13 -13
  53. package/style/shiki.css +11 -11
  54. package/style/util.css +30 -30
  55. package/translations/fr.json +21 -21
  56. package/translations/index.ts +4 -4
  57. package/translations/it.json +20 -20
  58. package/translations/tr.json +1 -1
  59. package/translations/zh.json +0 -1
  60. package/user-components/Card.astro +50 -54
  61. package/user-components/CardGrid.astro +21 -21
  62. package/user-components/Icon.astro +19 -21
  63. package/user-components/TabItem.astro +3 -3
  64. package/user-components/Tabs.astro +113 -117
  65. package/user-components/rehype-tabs.ts +72 -72
  66. package/utils/base.ts +6 -6
  67. package/utils/git.ts +55 -55
  68. package/utils/head.ts +56 -54
  69. package/utils/i18n.ts +3 -3
  70. package/utils/localizedUrl.ts +25 -25
  71. package/utils/navigation.ts +225 -203
  72. package/utils/routing.ts +74 -85
  73. package/utils/slugs.ts +41 -51
  74. package/utils/translations.ts +26 -32
  75. package/utils/user-config.ts +278 -278
  76. package/virtual.d.ts +8 -8
@@ -5,46 +5,48 @@ import Icon from '../user-components/Icon.astro';
5
5
  type Platform = keyof NonNullable<typeof config.social>;
6
6
 
7
7
  const labels: Record<Platform, string> = {
8
- github: 'GitHub',
9
- discord: 'Discord',
10
- twitter: 'Twitter',
11
- mastodon: 'Mastodon',
12
- codeberg: 'Codeberg',
13
- youtube: 'YouTube',
14
- threads: 'Threads',
15
- linkedin: 'LinkedIn',
16
- twitch: 'Twitch',
8
+ github: 'GitHub',
9
+ gitlab: 'GitLab',
10
+ discord: 'Discord',
11
+ twitter: 'Twitter',
12
+ mastodon: 'Mastodon',
13
+ codeberg: 'Codeberg',
14
+ youtube: 'YouTube',
15
+ threads: 'Threads',
16
+ linkedin: 'LinkedIn',
17
+ twitch: 'Twitch',
17
18
  };
18
19
 
19
- const links = Object.entries(config.social || {}).filter(([, url]) =>
20
- Boolean(url)
21
- ) as [platform: Platform, url: string][];
20
+ const links = Object.entries(config.social || {}).filter(([, url]) => Boolean(url)) as [
21
+ platform: Platform,
22
+ url: string,
23
+ ][];
22
24
  ---
23
25
 
24
26
  {
25
- links.length > 0 && (
26
- <>
27
- {links.map(([platform, url]) => (
28
- <a href={url} rel="me">
29
- <span class="sr-only">{labels[platform]}</span>
30
- <Icon name={platform} />
31
- </a>
32
- ))}
33
- <div class="divider" />
34
- </>
35
- )
27
+ links.length > 0 && (
28
+ <>
29
+ {links.map(([platform, url]) => (
30
+ <a href={url} rel="me">
31
+ <span class="sr-only">{labels[platform]}</span>
32
+ <Icon name={platform} />
33
+ </a>
34
+ ))}
35
+ <div class="divider" />
36
+ </>
37
+ )
36
38
  }
37
39
 
38
40
  <style>
39
- a {
40
- display: flex;
41
- color: var(--sl-color-text-accent);
42
- }
43
- a:hover {
44
- opacity: 0.66;
45
- }
46
- .divider {
47
- height: 2rem;
48
- border-inline-end: 1px solid var(--sl-color-gray-5);
49
- }
41
+ a {
42
+ display: flex;
43
+ color: var(--sl-color-text-accent);
44
+ }
45
+ a:hover {
46
+ opacity: 0.66;
47
+ }
48
+ .divider {
49
+ height: 2rem;
50
+ border-inline-end: 1px solid var(--sl-color-gray-5);
51
+ }
50
52
  </style>
@@ -5,10 +5,10 @@ import TableOfContentsList from './TableOfContentsList.astro';
5
5
  import type { TocItem } from './generateToC';
6
6
 
7
7
  interface Props {
8
- toc: TocItem[];
9
- locale: string | undefined;
10
- maxHeadingLevel: number;
11
- minHeadingLevel: number;
8
+ toc: TocItem[];
9
+ locale: string | undefined;
10
+ maxHeadingLevel: number;
11
+ minHeadingLevel: number;
12
12
  }
13
13
 
14
14
  const { locale, toc, maxHeadingLevel, minHeadingLevel } = Astro.props;
@@ -16,138 +16,138 @@ const t = useTranslations(locale);
16
16
  ---
17
17
 
18
18
  <mobile-starlight-toc data-min-h={minHeadingLevel} data-max-h={maxHeadingLevel}>
19
- <nav aria-labelledby="starlight__on-this-page--mobile" class="lg:hidden">
20
- <details id="starlight__mobile-toc">
21
- <summary id="starlight__on-this-page--mobile" class="flex">
22
- <div class="toggle flex">
23
- {t('tableOfContents.onThisPage')}
24
- <Icon name={'right-caret'} class="caret" size="1rem" />
25
- </div>
26
- <span class="display-current">{toc[0]?.text}</span>
27
- </summary>
28
- <div class="dropdown">
29
- <TableOfContentsList toc={toc} isMobile />
30
- </div>
31
- </details>
32
- </nav>
19
+ <nav aria-labelledby="starlight__on-this-page--mobile" class="lg:hidden">
20
+ <details id="starlight__mobile-toc">
21
+ <summary id="starlight__on-this-page--mobile" class="flex">
22
+ <div class="toggle flex">
23
+ {t('tableOfContents.onThisPage')}
24
+ <Icon name={'right-caret'} class="caret" size="1rem" />
25
+ </div>
26
+ <span class="display-current">{toc[0]?.text}</span>
27
+ </summary>
28
+ <div class="dropdown">
29
+ <TableOfContentsList toc={toc} isMobile />
30
+ </div>
31
+ </details>
32
+ </nav>
33
33
  </mobile-starlight-toc>
34
34
 
35
35
  <style>
36
- nav {
37
- position: fixed;
38
- top: calc(var(--sl-nav-height) - 1px);
39
- inset-inline: 0;
40
- border-top: 1px solid var(--sl-color-gray-5);
41
- background-color: var(--sl-color-bg-nav);
42
- }
43
- @media (min-width: 50rem) {
44
- nav {
45
- inset-inline-start: var(--sl-content-inline-start, 0);
46
- }
47
- }
36
+ nav {
37
+ position: fixed;
38
+ top: calc(var(--sl-nav-height) - 1px);
39
+ inset-inline: 0;
40
+ border-top: 1px solid var(--sl-color-gray-5);
41
+ background-color: var(--sl-color-bg-nav);
42
+ }
43
+ @media (min-width: 50rem) {
44
+ nav {
45
+ inset-inline-start: var(--sl-content-inline-start, 0);
46
+ }
47
+ }
48
48
 
49
- summary {
50
- gap: 0.5rem;
51
- align-items: center;
52
- height: var(--sl-mobile-toc-height);
53
- border-bottom: 1px solid var(--sl-color-hairline-shade);
54
- padding: 0.5rem 1rem;
55
- font-size: var(--sl-text-xs);
56
- outline-offset: var(--sl-outline-offset-inside);
57
- }
58
- summary::marker,
59
- summary::-webkit-details-marker {
60
- display: none;
61
- }
49
+ summary {
50
+ gap: 0.5rem;
51
+ align-items: center;
52
+ height: var(--sl-mobile-toc-height);
53
+ border-bottom: 1px solid var(--sl-color-hairline-shade);
54
+ padding: 0.5rem 1rem;
55
+ font-size: var(--sl-text-xs);
56
+ outline-offset: var(--sl-outline-offset-inside);
57
+ }
58
+ summary::marker,
59
+ summary::-webkit-details-marker {
60
+ display: none;
61
+ }
62
62
 
63
- .toggle {
64
- flex-shrink: 0;
65
- gap: 1rem;
66
- align-items: center;
67
- justify-content: space-between;
68
- border: 1px solid var(--sl-color-gray-5);
69
- border-radius: 0.5rem;
70
- padding-block: 0.5rem;
71
- padding-inline-start: 0.75rem;
72
- padding-inline-end: 0.5rem;
73
- line-height: 1;
74
- background-color: var(--sl-color-black);
75
- user-select: none;
76
- cursor: pointer;
77
- }
78
- details[open] .toggle {
79
- color: var(--sl-color-white);
80
- border-color: var(--sl-color-accent);
81
- }
82
- details .toggle:hover {
83
- color: var(--sl-color-white);
84
- border-color: var(--sl-color-gray-2);
85
- }
63
+ .toggle {
64
+ flex-shrink: 0;
65
+ gap: 1rem;
66
+ align-items: center;
67
+ justify-content: space-between;
68
+ border: 1px solid var(--sl-color-gray-5);
69
+ border-radius: 0.5rem;
70
+ padding-block: 0.5rem;
71
+ padding-inline-start: 0.75rem;
72
+ padding-inline-end: 0.5rem;
73
+ line-height: 1;
74
+ background-color: var(--sl-color-black);
75
+ user-select: none;
76
+ cursor: pointer;
77
+ }
78
+ details[open] .toggle {
79
+ color: var(--sl-color-white);
80
+ border-color: var(--sl-color-accent);
81
+ }
82
+ details .toggle:hover {
83
+ color: var(--sl-color-white);
84
+ border-color: var(--sl-color-gray-2);
85
+ }
86
86
 
87
- :global([dir='rtl']) .caret {
88
- transform: rotateZ(180deg);
89
- }
90
- details[open] .caret {
91
- transform: rotateZ(90deg);
92
- }
87
+ :global([dir='rtl']) .caret {
88
+ transform: rotateZ(180deg);
89
+ }
90
+ details[open] .caret {
91
+ transform: rotateZ(90deg);
92
+ }
93
93
 
94
- .display-current {
95
- white-space: nowrap;
96
- text-overflow: ellipsis;
97
- overflow: hidden;
98
- color: var(--sl-color-white);
99
- }
94
+ .display-current {
95
+ white-space: nowrap;
96
+ text-overflow: ellipsis;
97
+ overflow: hidden;
98
+ color: var(--sl-color-white);
99
+ }
100
100
 
101
- .dropdown {
102
- --border-top: 1px;
103
- margin-top: calc(-1 * var(--border-top));
104
- border: var(--border-top) solid var(--sl-color-gray-6);
105
- border-top-color: var(--sl-color-hairline-shade);
106
- max-height: calc(85vh - var(--sl-nav-height) - var(--sl-mobile-toc-height));
107
- overflow-y: auto;
108
- background-color: var(--sl-color-black);
109
- box-shadow: var(--sl-shadow-md);
110
- }
101
+ .dropdown {
102
+ --border-top: 1px;
103
+ margin-top: calc(-1 * var(--border-top));
104
+ border: var(--border-top) solid var(--sl-color-gray-6);
105
+ border-top-color: var(--sl-color-hairline-shade);
106
+ max-height: calc(85vh - var(--sl-nav-height) - var(--sl-mobile-toc-height));
107
+ overflow-y: auto;
108
+ background-color: var(--sl-color-black);
109
+ box-shadow: var(--sl-shadow-md);
110
+ }
111
111
  </style>
112
112
 
113
113
  <script>
114
- import { StarlightTOC } from './starlight-toc';
114
+ import { StarlightTOC } from './starlight-toc';
115
115
 
116
- class MobileStarlightTOC extends StarlightTOC {
117
- override set current(link: HTMLAnchorElement) {
118
- super.current = link;
119
- const display = this.querySelector('.display-current') as HTMLSpanElement;
120
- if (display) display.textContent = link.textContent;
121
- }
116
+ class MobileStarlightTOC extends StarlightTOC {
117
+ override set current(link: HTMLAnchorElement) {
118
+ super.current = link;
119
+ const display = this.querySelector('.display-current') as HTMLSpanElement;
120
+ if (display) display.textContent = link.textContent;
121
+ }
122
122
 
123
- constructor() {
124
- super({ smallViewport: true });
125
- const details = this.querySelector('details');
126
- if (!details) return;
127
- const closeToC = () => {
128
- details.open = false;
129
- };
130
- // Close the table of contents whenever a link is clicked.
131
- details.querySelectorAll('a').forEach((a) => {
132
- a.addEventListener('click', closeToC);
133
- });
134
- // Close the table of contents when a user clicks outside of it.
135
- window.addEventListener('click', (e) => {
136
- if (!details.contains(e.target as Node)) closeToC();
137
- });
138
- // Or when they press the escape key.
139
- window.addEventListener('keydown', (e) => {
140
- if (e.key === 'Escape' && details.open) {
141
- const hasFocus = details.contains(document.activeElement);
142
- closeToC();
143
- if (hasFocus) {
144
- const summary = details.querySelector('summary');
145
- if (summary) summary.focus();
146
- }
147
- }
148
- });
149
- }
150
- }
123
+ constructor() {
124
+ super({ smallViewport: true });
125
+ const details = this.querySelector('details');
126
+ if (!details) return;
127
+ const closeToC = () => {
128
+ details.open = false;
129
+ };
130
+ // Close the table of contents whenever a link is clicked.
131
+ details.querySelectorAll('a').forEach((a) => {
132
+ a.addEventListener('click', closeToC);
133
+ });
134
+ // Close the table of contents when a user clicks outside of it.
135
+ window.addEventListener('click', (e) => {
136
+ if (!details.contains(e.target as Node)) closeToC();
137
+ });
138
+ // Or when they press the escape key.
139
+ window.addEventListener('keydown', (e) => {
140
+ if (e.key === 'Escape' && details.open) {
141
+ const hasFocus = details.contains(document.activeElement);
142
+ closeToC();
143
+ if (hasFocus) {
144
+ const summary = details.querySelector('summary');
145
+ if (summary) summary.focus();
146
+ }
147
+ }
148
+ });
149
+ }
150
+ }
151
151
 
152
- customElements.define('mobile-starlight-toc', MobileStarlightTOC);
152
+ customElements.define('mobile-starlight-toc', MobileStarlightTOC);
153
153
  </script>
@@ -2,82 +2,77 @@
2
2
  import type { TocItem } from './generateToC';
3
3
 
4
4
  interface Props {
5
- toc: TocItem[];
6
- depth?: number;
7
- isMobile?: boolean;
5
+ toc: TocItem[];
6
+ depth?: number;
7
+ isMobile?: boolean;
8
8
  }
9
9
 
10
10
  const { toc, isMobile = false, depth = 0 } = Astro.props;
11
11
  ---
12
12
 
13
13
  <ul class:list={{ isMobile }}>
14
- {
15
- toc.map((heading) => (
16
- <li>
17
- <a href={'#' + heading.slug} aria-current={heading.current && 'true'}>
18
- <span>{heading.text}</span>
19
- </a>
20
- {heading.children.length > 0 && (
21
- <Astro.self
22
- toc={heading.children}
23
- depth={depth + 1}
24
- isMobile={isMobile}
25
- />
26
- )}
27
- </li>
28
- ))
29
- }
14
+ {
15
+ toc.map((heading) => (
16
+ <li>
17
+ <a href={'#' + heading.slug} aria-current={heading.current && 'true'}>
18
+ <span>{heading.text}</span>
19
+ </a>
20
+ {heading.children.length > 0 && (
21
+ <Astro.self toc={heading.children} depth={depth + 1} isMobile={isMobile} />
22
+ )}
23
+ </li>
24
+ ))
25
+ }
30
26
  </ul>
31
27
 
32
28
  <style define:vars={{ depth }}>
33
- ul {
34
- padding: 0;
35
- list-style: none;
36
- }
37
- a {
38
- --pad-inline: 0.5rem;
39
- display: block;
40
- border-radius: 0.25rem;
41
- padding-block: 0.25rem;
42
- padding-inline: calc(1rem * var(--depth) + var(--pad-inline))
43
- var(--pad-inline);
44
- line-height: 1.25;
45
- }
46
- a[aria-current='true'],
47
- a[aria-current='true']:hover,
48
- a[aria-current='true']:focus {
49
- font-weight: 600;
50
- color: var(--sl-color-text-invert);
51
- background-color: var(--sl-color-text-accent);
52
- }
53
- .isMobile a {
54
- --pad-inline: 1rem;
55
- display: flex;
56
- justify-content: space-between;
57
- gap: var(--pad-inline);
58
- border-top: 1px solid var(--sl-color-gray-6);
59
- border-radius: 0;
60
- padding-block: 0.5rem;
61
- color: var(--sl-color-text);
62
- font-size: var(--sl-text-sm);
63
- text-decoration: none;
64
- outline-offset: var(--sl-outline-offset-inside);
65
- }
66
- .isMobile:first-child > li:first-child > a {
67
- border-top: 0;
68
- }
69
- .isMobile a[aria-current='true'],
70
- .isMobile a[aria-current='true']:hover,
71
- .isMobile a[aria-current='true']:focus {
72
- color: var(--sl-color-white);
73
- background-color: unset;
74
- }
75
- .isMobile a[aria-current='true']::after {
76
- content: '';
77
- width: 1rem;
78
- background-color: var(--sl-color-text-accent);
79
- /* Check mark SVG icon */
80
- -webkit-mask-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCAxNCAxNCc+PHBhdGggZD0nTTEwLjkxNCA0LjIwNmEuNTgzLjU4MyAwIDAgMC0uODI4IDBMNS43NCA4LjU1NyAzLjkxNCA2LjcyNmEuNTk2LjU5NiAwIDAgMC0uODI4Ljg1N2wyLjI0IDIuMjRhLjU4My41ODMgMCAwIDAgLjgyOCAwbDQuNzYtNC43NmEuNTgzLjU4MyAwIDAgMCAwLS44NTdaJy8+PC9zdmc+Cg==');
81
- mask-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCAxNCAxNCc+PHBhdGggZD0nTTEwLjkxNCA0LjIwNmEuNTgzLjU4MyAwIDAgMC0uODI4IDBMNS43NCA4LjU1NyAzLjkxNCA2LjcyNmEuNTk2LjU5NiAwIDAgMC0uODI4Ljg1N2wyLjI0IDIuMjRhLjU4My41ODMgMCAwIDAgLjgyOCAwbDQuNzYtNC43NmEuNTgzLjU4MyAwIDAgMCAwLS44NTdaJy8+PC9zdmc+Cg==');
82
- }
29
+ ul {
30
+ padding: 0;
31
+ list-style: none;
32
+ }
33
+ a {
34
+ --pad-inline: 0.5rem;
35
+ display: block;
36
+ border-radius: 0.25rem;
37
+ padding-block: 0.25rem;
38
+ padding-inline: calc(1rem * var(--depth) + var(--pad-inline)) var(--pad-inline);
39
+ line-height: 1.25;
40
+ }
41
+ a[aria-current='true'],
42
+ a[aria-current='true']:hover,
43
+ a[aria-current='true']:focus {
44
+ font-weight: 600;
45
+ color: var(--sl-color-text-invert);
46
+ background-color: var(--sl-color-text-accent);
47
+ }
48
+ .isMobile a {
49
+ --pad-inline: 1rem;
50
+ display: flex;
51
+ justify-content: space-between;
52
+ gap: var(--pad-inline);
53
+ border-top: 1px solid var(--sl-color-gray-6);
54
+ border-radius: 0;
55
+ padding-block: 0.5rem;
56
+ color: var(--sl-color-text);
57
+ font-size: var(--sl-text-sm);
58
+ text-decoration: none;
59
+ outline-offset: var(--sl-outline-offset-inside);
60
+ }
61
+ .isMobile:first-child > li:first-child > a {
62
+ border-top: 0;
63
+ }
64
+ .isMobile a[aria-current='true'],
65
+ .isMobile a[aria-current='true']:hover,
66
+ .isMobile a[aria-current='true']:focus {
67
+ color: var(--sl-color-white);
68
+ background-color: unset;
69
+ }
70
+ .isMobile a[aria-current='true']::after {
71
+ content: '';
72
+ width: 1rem;
73
+ background-color: var(--sl-color-text-accent);
74
+ /* Check mark SVG icon */
75
+ -webkit-mask-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCAxNCAxNCc+PHBhdGggZD0nTTEwLjkxNCA0LjIwNmEuNTgzLjU4MyAwIDAgMC0uODI4IDBMNS43NCA4LjU1NyAzLjkxNCA2LjcyNmEuNTk2LjU5NiAwIDAgMC0uODI4Ljg1N2wyLjI0IDIuMjRhLjU4My41ODMgMCAwIDAgLjgyOCAwbDQuNzYtNC43NmEuNTgzLjU4MyAwIDAgMCAwLS44NTdaJy8+PC9zdmc+Cg==');
76
+ mask-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCAxNCAxNCc+PHBhdGggZD0nTTEwLjkxNCA0LjIwNmEuNTgzLjU4MyAwIDAgMC0uODI4IDBMNS43NCA4LjU1NyAzLjkxNCA2LjcyNmEuNTk2LjU5NiAwIDAgMC0uODI4Ljg1N2wyLjI0IDIuMjRhLjU4My41ODMgMCAwIDAgLjgyOCAwbDQuNzYtNC43NmEuNTgzLjU4MyAwIDAgMCAwLS44NTdaJy8+PC9zdmc+Cg==');
77
+ }
83
78
  </style>
@@ -1,58 +1,56 @@
1
1
  import type { MarkdownHeading } from 'astro';
2
2
 
3
3
  export interface TocItem extends MarkdownHeading {
4
- children: TocItem[];
5
- current?: boolean;
4
+ children: TocItem[];
5
+ current?: boolean;
6
6
  }
7
7
 
8
8
  function diveChildren(item: TocItem, depth: number): TocItem[] {
9
- if (depth === 1) {
10
- return item.children;
11
- } else if (item.children.length > 0) {
12
- return diveChildren(item.children.at(-1)!, depth - 1);
13
- } else {
14
- return [];
15
- }
9
+ if (depth === 1) {
10
+ return item.children;
11
+ } else if (item.children.length > 0) {
12
+ return diveChildren(item.children.at(-1)!, depth - 1);
13
+ } else {
14
+ return [];
15
+ }
16
16
  }
17
17
 
18
18
  interface TocOpts {
19
- minHeadingLevel: number;
20
- maxHeadingLevel: number;
21
- title?: string;
19
+ minHeadingLevel: number;
20
+ maxHeadingLevel: number;
21
+ title?: string;
22
22
  }
23
23
 
24
24
  export function generateToC(
25
- headings: MarkdownHeading[],
26
- { minHeadingLevel, maxHeadingLevel, title = 'Overview' }: TocOpts
25
+ headings: MarkdownHeading[],
26
+ { minHeadingLevel, maxHeadingLevel, title = 'Overview' }: TocOpts
27
27
  ) {
28
- const overview = { depth: 2, slug: '_top', text: title };
29
- headings = [
30
- overview,
31
- ...headings.filter(
32
- ({ depth }) => depth >= minHeadingLevel && depth <= maxHeadingLevel
33
- ),
34
- ];
35
- const toc: Array<TocItem> = [];
28
+ const overview = { depth: 2, slug: '_top', text: title };
29
+ headings = [
30
+ overview,
31
+ ...headings.filter(({ depth }) => depth >= minHeadingLevel && depth <= maxHeadingLevel),
32
+ ];
33
+ const toc: Array<TocItem> = [];
36
34
 
37
- for (const heading of headings) {
38
- if (toc.length === 0) {
39
- toc.push({ ...heading, children: [], current: true });
40
- } else {
41
- const lastItemInToc = toc.at(-1)!;
42
- if (heading.depth < lastItemInToc.depth) {
43
- throw new Error(`Orphan heading found: ${heading.text}.`);
44
- }
45
- if (heading.depth === lastItemInToc.depth) {
46
- // same depth
47
- toc.push({ ...heading, children: [] });
48
- } else {
49
- // higher depth
50
- // push into children, or children's children alike
51
- const gap = heading.depth - lastItemInToc.depth;
52
- const target = diveChildren(lastItemInToc, gap);
53
- target.push({ ...heading, children: [] });
54
- }
55
- }
56
- }
57
- return toc;
35
+ for (const heading of headings) {
36
+ if (toc.length === 0) {
37
+ toc.push({ ...heading, children: [], current: true });
38
+ } else {
39
+ const lastItemInToc = toc.at(-1)!;
40
+ if (heading.depth < lastItemInToc.depth) {
41
+ throw new Error(`Orphan heading found: ${heading.text}.`);
42
+ }
43
+ if (heading.depth === lastItemInToc.depth) {
44
+ // same depth
45
+ toc.push({ ...heading, children: [] });
46
+ } else {
47
+ // higher depth
48
+ // push into children, or children's children alike
49
+ const gap = heading.depth - lastItemInToc.depth;
50
+ const target = diveChildren(lastItemInToc, gap);
51
+ target.push({ ...heading, children: [] });
52
+ }
53
+ }
54
+ }
55
+ return toc;
58
56
  }