make sidebar and toc

This commit is contained in:
Philip Ahlqvist 2024-01-10 22:11:33 +01:00
parent 7847d58841
commit c2c1d4807a
16 changed files with 360 additions and 181 deletions

BIN
bun.lockb

Binary file not shown.

View file

@ -8,6 +8,7 @@
"@thewebforge/astro-code-blocks": "^0.2.0", "@thewebforge/astro-code-blocks": "^0.2.0",
"astro": "^4.0.3", "astro": "^4.0.3",
"astro-preload": "^1.1.2", "astro-preload": "^1.1.2",
"markdown-wasm": "^1.2.0",
"rehype-autolink-headings": "^7.1.0", "rehype-autolink-headings": "^7.1.0",
"rehype-slug": "^6.0.0", "rehype-slug": "^6.0.0",
"sass": "^1.69.5" "sass": "^1.69.5"

View file

@ -1,30 +1,25 @@
--- ---
// TableOfContents.astro // TableOfContents.astro
import TableOfContentsHeading from "./TableOfContentsHeading.astro"; import TableOfContentsHeading from "./TableOfContentsHeading.astro";
import TocItem from '../utils/generateToc.ts';
const { headings } = Astro.props; const { headings } = Astro.props;
const toc = headings && headings.length ? buildToc(headings) : []; const toc = headings && headings.length ? TocItem(headings) : [];
console.log(toc);
function buildToc(headings) {
const toc = [];
const parentHeadings = new Map();
headings.forEach((h) => {
const heading = { ...h, subheadings: [] };
parentHeadings.set(heading.depth, heading);
// Change 2 to 1 if your markdown includes your <h1>
// if (heading.depth === 2) {
// toc.push(heading);
// } else {
// parentHeadings.get(heading.depth - 1).subheadings.push(heading);
// }
toc.push(heading);
});
return toc;
}
--- ---
<nav class="toc"> <nav class="toc">
<ul> <h2 id="on-this-page">On this page</h2>
{toc.map((heading) => <TableOfContentsHeading heading={heading} />)} <ul>
</ul> {toc.map((heading) => <TableOfContentsHeading heading={heading}/>)}
</nav> </ul>
</nav>
<style>
.toc ul {
list-style: none;
padding-inline-start: 0;
border-left: 1px solid var(--secondary-background);
}
</style>

View file

@ -8,12 +8,39 @@ const { heading } = Astro.props;
{heading.text} {heading.text}
</a> </a>
{ {
heading.subheadings.length > 0 && ( heading.children.length > 0 && (
<ul> <ul>
{heading.subheadings.map((subheading) => ( {heading.children.map((child) => (
<Astro.self heading={subheading} /> <Astro.self heading={child} />
))} ))}
</ul> </ul>
) )
} }
</li> </li>
<style>
li a {
display: inline-block;
font-size: 0.875rem;
width: 100%;
/* margin-left: 0.3rem; */
padding: 0.5em 0.5rem;
border-radius: 0.25em;
}
li a.active {
color: var(--header-text);
background-color: var(--tertiary-background);
}
li a:hover {
color: var(--header-text);
/* border-left: 1px solid var(--secondary-background); */
background-color: var(--tertiary-background);
}
li ul {
list-style: none;
padding-inline-start: 1rem;
/* border-left: 1px solid var(--secondary-background); */
}
</style>

View file

@ -10,7 +10,7 @@ This document outlines how to configure SDK daemon settings and what options are
The easiest way to configure the settings is by editing the `daemon_settings.yml` file (may need to be created) that resides in the default [lbrynet directory](https://lbry.com/faq/lbry-directories). These settings can also be configured via the [settings_set](https://lbry.tech/api/sdk#settings_set) API call. The [settings_get](https://lbry.tech/api/sdk#settings_get) API call can be used to retrieve current values. Some values will require an SDK restart after being set via the API call. The easiest way to configure the settings is by editing the `daemon_settings.yml` file (may need to be created) that resides in the default [lbrynet directory](https://lbry.com/faq/lbry-directories). These settings can also be configured via the [settings_set](https://lbry.tech/api/sdk#settings_set) API call. The [settings_get](https://lbry.tech/api/sdk#settings_get) API call can be used to retrieve current values. Some values will require an SDK restart after being set via the API call.
Sample daemon_settings.yml file: Sample daemon_settings.yml file:
``` ```yaml
tcp_port: 3335 tcp_port: 3335
lbryum_servers: ['spv11.lbry.com:50001','spv19.lbry.com:50001'] lbryum_servers: ['spv11.lbry.com:50001','spv19.lbry.com:50001']
download_directory: 'c:\lbry\Downloads' download_directory: 'c:\lbry\Downloads'
@ -30,14 +30,14 @@ Configuration options are organized by their respective areas: Files, Wallet, Ne
| download_dir | string | local downloads folder | 'c:\lbry\lbrynet\' | Location of downloaded output files | | download_dir | string | local downloads folder | 'c:\lbry\lbrynet\' | Location of downloaded output files |
### Wallet ### Wallet
| Setting | Format | Default value | Sample Values | Description | | Setting | Format | Default value | Sample Values | Description |
|-----------------|--------|-------------------------------------------------------|----------------------------|------------------------------------------------------------| |-----------------|---------|-------------------------------------------------------|-------------------------------------|---------------------------------------------------------------------------------------------------|
| blockchain_name | string | 'lbrycrd_main' | 'lbrycrd_regtest' | Blockchain network to connect to | | blockchain_name | string | 'lbrycrd_main' | 'lbrycrd_regtest' | Blockchain network to connect to |
| lbryum_servers | list | [spv11.lbry.com:50001,spv19.lbry.com:50001] | ["mylbryum.lbry.com:50001] | SPV wallet server address(Default servers are spv11-spv19) | | lbryum_servers | list | [spv11.lbry.com:50001,spv19.lbry.com:50001] | ["mylbryum.lbry.com:50001] | SPV wallet server address(Default servers are spv11-spv19) |
| wallet_dir | string | [varies by OS](https://lbry.com/faq/lbry-directories) | 'c:\lbry\lbryum\' | Wallet data location | | wallet_dir | string | [varies by OS](https://lbry.com/faq/lbry-directories) | 'c:\lbry\lbryum\' | Wallet data location |
| max_key_fee | json | \{'currency': 'USD', 'amount': 50.0} | \{'currency': 'LBC', 'amount': 5.0} | Max payment allowed for content | | max_key_fee | json | \{'currency': 'USD', 'amount': 50.0} | \{'currency': 'LBC', 'amount': 5.0} | Max payment allowed for content |
| wallet | string | 'lbryum' | 'lbrycrd' | Choice of wallet software, SPV (lbryum) vs full node (lbrycrd). Currently only lbryum supported | | wallet | string | 'lbryum' | 'lbrycrd' | Choice of wallet software, SPV (lbryum) vs full node (lbrycrd). Currently only lbryum supported |
| use_keyring | boolean | false | true | Store wallet password in keyring (not currently available) | | use_keyring | boolean | false | true | Store wallet password in keyring (not currently available) |

View file

@ -0,0 +1,157 @@
---
import Layout from './Layout.astro';
import { markdown } from '@astropub/md';
import { getCollection } from "astro:content";
import TableOfContents from '../components/TableOfContents.astro';
import '../styles/markdown.css';
const { frontmatter, headings, collection } = Astro.props;
const { pathname } = Astro.url;
const items = await getCollection(collection) || [];
const description = await markdown(frontmatter.description);
const isActive = (href: string)=>{
return href === pathname || href === pathname.split('/').slice(0,2).join('/');
}
---
<Layout title={frontmatter.title}>
<div class="wrapper">
<section class="sidebar">
<div class="container">
{items.length ? (
<summary>
<a class="title" href={`/${collection}`}>{collection.charAt(0).toUpperCase() + collection.slice(1)}</a>
</summary>
<ul>
{items.map(item=> (
<li class:list={[isActive(`/${item.collection}/${item.slug}`) ? "active": ""]}><a href={`/${item.collection}/${item.slug}`}>{item.data.title}</a></li>
))}
</ul>
) : null}
</div>
</section>
<div class="main">
<div class="content">
<aside class="toc">
<TableOfContents headings={headings} />
</aside>
<div class="markdown-body">
<h1>{frontmatter.title}</h1>
<h3>{description}</h3>
<slot/>
</div>
</div>
</div>
</div>
<style>
.wrapper {
display: flex;
--sidebar-width: 300px;
/* max-width: 1000px; */
}
.wrapper .sidebar {
position: fixed;
width: var(--sidebar-width);
height: 100vh;
overflow-y: auto;
inset-block: 0;
inset-inline-start: 0;
/* padding: 20px; */
padding-top: var(--nav-height);
background-color: var(--secondary-background);
}
.wrapper .sidebar .container {
padding: 1rem 1rem 0;
}
.wrapper .sidebar summary .title {
/* font-size: 1.5rem; */
font-weight: bold;
}
.wrapper .sidebar ul {
list-style: none;
margin: 0.5rem;
padding-inline-start: 0;
/* border-left: 1px solid var(--background); */
}
.wrapper .sidebar ul li {
/* width: fit-content; */
/* height: fit-content; */
/* padding: 10px 20px; */
border-left: 1px solid var(--background);
}
.wrapper .sidebar ul li a {
display: inline-block;
font-size: 0.875rem;
width: 100%;
margin-left: 0.3rem;
padding: 0.5em 0.5rem;
border-radius: 0.25em;
}
.wrapper .sidebar ul li.active {
border-left: 1px solid var(--header-text);
}
.wrapper .sidebar ul li.active a {
background-color: var(--tertiary-background);
}
.wrapper .sidebar ul li.active a, .wrapper .sidebar ul li a:hover {
color: var(--header-text);
}
.wrapper .main {
width: 100vw;
padding-inline-start: var(--sidebar-width);
}
.wrapper .main .content {
display: flex;
/* width: fit-content; */
/* max-width: 100%; */
order: 1;
margin: 20px 0 0 20px;
}
.wrapper .main aside {
display: flex;
align-items: center;
order: 2;
width: fit-content;
max-width: 25%;
margin: 0 auto;
}
.wrapper .markdown-body {
/* margin: 10px 20px; */
max-width: 75%;
}
.wrapper .toc {
display: flex;
flex-direction: column;
}
@media only screen and (max-width: 750px) {
.wrapper .sidebar {
transform: translateX(-80%);
}
.wrapper .main {
padding-inline-start: calc(var(--sidebar-width) * 0.2);
}
}
</style>
</Layout>

View file

@ -8,48 +8,74 @@ import '../styles/markdown.css';
const { frontmatter, headings, collection } = Astro.props; const { frontmatter, headings, collection } = Astro.props;
const items = await getCollection(collection) || []; const items = collection ? await getCollection(collection) : [];
const description = await markdown(frontmatter.description) const description = await markdown(frontmatter.description);
--- ---
<Layout title={frontmatter.title}> <Layout title={frontmatter.title}>
<div class="wrapper"> <div class="wrapper">
<section> <div class="main">
{items.length ? ( <div class="content">
<a href={`/${collection}`}>{collection.charAt(0).toUpperCase() + collection.slice(1)}</a> {headings && (
<ul> <aside class="toc">
{items.map(item=> ( <TableOfContents headings={headings} />
<li><a href={`/${item.collection}/${item.slug}`}>{item.data.title}</a></li> </aside>
))} )}
</ul> <div class="markdown-body">
) : null} <h1>{frontmatter.title}</h1>
</section> <h3>{description}</h3>
<div class="markdown-body"> <slot/>
<h1>{frontmatter.title}</h1> </div>
<h3>{description}</h3> </div>
<slot/>
</div> </div>
<section class="toc">
<TableOfContents headings={headings} />
</section>
</div> </div>
<style> <style>
.wrapper { .wrapper {
display: flex; display: flex;
margin: 20px;
/* max-width: 1000px; */ /* max-width: 1000px; */
} }
.wrapper .main {
width: 100vw;
}
.wrapper .main .content {
display: flex;
/* width: fit-content; */
/* max-width: 100%; */
order: 1;
margin: 20px 0 0 20px;
}
.wrapper .main aside {
display: flex;
align-items: center;
order: 2;
width: fit-content;
max-width: 25%;
margin: 0 auto;
}
.wrapper .markdown-body { .wrapper .markdown-body {
margin: 10px 20px; /* margin: 10px 20px; */
max-width: 1000px; max-width: 75%;
} }
.wrapper .toc { .wrapper .toc {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
@media only screen and (max-width: 750px) {
.wrapper .sidebar {
transform: translateX(-80%);
}
.wrapper .main {
padding-inline-start: calc(400px * 0.2);
}
}
</style> </style>
</Layout> </Layout>

View file

@ -1,5 +1,5 @@
--- ---
import Markdown from '../layouts/Markdown.astro'; import Collections from '../layouts/Collections.astro';
const frontmatter = { const frontmatter = {
title: "Find the LBRY specification, API documentation, our Contributors guide, and more in the Resources area.", title: "Find the LBRY specification, API documentation, our Contributors guide, and more in the Resources area.",
@ -7,6 +7,6 @@ const frontmatter = {
} }
--- ---
<Markdown title="Resources" frontmatter={frontmatter} collection="resources"> <Collections title="Resources" frontmatter={frontmatter} collection="resources">
</Markdown> </Collections>

View file

@ -1,6 +1,6 @@
--- ---
import { getCollection } from 'astro:content'; import { getCollection } from 'astro:content';
import Markdown from '../../layouts/Markdown.astro'; import Markdown from '../../layouts/Collections.astro';
export async function getStaticPaths() { export async function getStaticPaths() {
const entries = await getCollection('resources'); const entries = await getCollection('resources');

View file

@ -3,19 +3,32 @@ import Markdown from '../layouts/Markdown.astro';
import rehypeAutolinkHeadings from 'rehype-autolink-headings'; import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import rehypeSlug from 'rehype-slug'; import rehypeSlug from 'rehype-slug';
import markdown from 'markdown-wasm';
// import { markdown } from '@astropub/md'
import { markdown } from '@astropub/md' let content = await (await fetch('https://raw.githubusercontent.com/lbryio/spec/master/index.md')).text();
// const md = await markdown(content.replace(`---
// layout: spec
const content = await (await fetch('https://raw.githubusercontent.com/lbryio/spec/master/index.md')).text(); // ---`, ''), {
const md = await markdown(content.replace(`--- // rehypePlugins: [
// [rehypeSlug as any],
// [rehypeAutolinkHeadings as any, { behavior: 'wrap' }]
// ]
// });
content = content.replace(`---
layout: spec layout: spec
---`, ''), { ---`, '')
rehypePlugins: [
[rehypeSlug as any], // Extract the headings from the markdown
[rehypeAutolinkHeadings as any, { behavior: 'wrap' }] const headings = content.match(/(?<flag>#{1,6})\s+(?<content>.+)/g).map(heading=>{
] const text = heading.replaceAll('#', '').replace(' ', '');
return {
depth: heading.replace(/[^#]/g, "").length, // Get number of '#' a heading has
slug: text.replaceAll(' ', '-').toLowerCase(), // URL friendly path
text
}
}); });
--- ---
@ -23,6 +36,6 @@ layout: spec
<Markdown frontmatter={{ <Markdown frontmatter={{
title: "Spec", title: "Spec",
description: "*Alex Grintsvayg (grin@lbry.com), Jeremy Kauffman (jeremy@lbry.com)*" description: "*Alex Grintsvayg (grin@lbry.com), Jeremy Kauffman (jeremy@lbry.com)*"
}}> }} headings={headings}>
{md} {<Fragment set:html={markdown.parse(content)}/>}
</Markdown> </Markdown>

View file

@ -1,5 +1,5 @@
--- ---
import Markdown from '../layouts/Markdown.astro'; import Markdown from '../layouts/Collections.astro';
const frontmatter = { const frontmatter = {
description: "Find the LBRY specification, API documentation, our Contributor's guide, and more in the Resources area." description: "Find the LBRY specification, API documentation, our Contributor's guide, and more in the Resources area."

View file

@ -1,6 +1,6 @@
--- ---
import { getCollection } from 'astro:content'; import { getCollection } from 'astro:content';
import Markdown from '../../layouts/Markdown.astro'; import Markdown from '../../layouts/Collections.astro';
export async function getStaticPaths() { export async function getStaticPaths() {
const entries = await getCollection('tutorials'); const entries = await getCollection('tutorials');

View file

@ -6,7 +6,7 @@ header {
header nav { header nav {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
height: 75px; height: var(--nav-height);
padding: 0 20px; padding: 0 20px;
} }

View file

@ -7,6 +7,7 @@
--tertiary-background: #041523; --tertiary-background: #041523;
--header-text: #FAFAFA; --header-text: #FAFAFA;
--body-text: #DDDDDD; --body-text: #DDDDDD;
--nav-height: 75px;
--astro-code-color-text: white; --astro-code-color-text: white;
--astro-code-color-background: black; --astro-code-color-background: black;
@ -30,7 +31,7 @@
html { html {
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
margin-top: 75px; margin-top: var(--nav-height);
scroll-behavior: smooth; scroll-behavior: smooth;
} }
@ -56,6 +57,7 @@ main {
footer { footer {
width: 100vw; width: 100vw;
overflow: hidden; overflow: hidden;
z-index: 10;
} }
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {

View file

@ -3,10 +3,9 @@ h1 > a, h2 > a, h3 > a, h4 > a, h5 > a, h6 > a {
} }
/* Source: https://github.com/sindresorhus/github-markdown-css */ /* Source: https://github.com/sindresorhus/github-markdown-css */
/* With some modifications */
@media (prefers-color-scheme: dark) { .markdown-body {
.markdown-body,
[data-theme="dark"] {
/*dark*/ /*dark*/
color-scheme: dark; color-scheme: dark;
--color-prettylights-syntax-comment: #8b949e; --color-prettylights-syntax-comment: #8b949e;
@ -42,10 +41,14 @@ h1 > a, h2 > a, h3 > a, h4 > a, h5 > a, h6 > a {
--color-fg-default: #e6edf3; --color-fg-default: #e6edf3;
--color-fg-muted: #7d8590; --color-fg-muted: #7d8590;
--color-fg-subtle: #6e7681; --color-fg-subtle: #6e7681;
--color-canvas-default: #0d1117; /* --color-canvas-default: #0d1117; */
--color-canvas-subtle: #161b22; --color-canvas-default: var(--tertiary-background);
--color-border-default: #30363d; /* --color-canvas-subtle: #161b22; */
--color-border-muted: #21262d; --color-canvas-subtle: var(--secondary-background);
/* --color-border-default: #30363d; */
--color-border-default: var(--secondary-background);
/* --color-border-muted: #21262d; */
--color-border-muted: var(--tertiary-background);
--color-neutral-muted: rgba(110,118,129,0.4); --color-neutral-muted: rgba(110,118,129,0.4);
--color-accent-fg: #2f81f7; --color-accent-fg: #2f81f7;
--color-accent-emphasis: #1f6feb; --color-accent-emphasis: #1f6feb;
@ -53,104 +56,7 @@ h1 > a, h2 > a, h3 > a, h4 > a, h5 > a, h6 > a {
--color-attention-subtle: rgba(187,128,9,0.15); --color-attention-subtle: rgba(187,128,9,0.15);
--color-danger-fg: #f85149; --color-danger-fg: #f85149;
--color-done-fg: #a371f7; --color-done-fg: #a371f7;
} }
}
@media (prefers-color-scheme: light) {
.markdown-body,
[data-theme="light"] {
/*light*/
color-scheme: light;
--color-prettylights-syntax-comment: #6e7781;
--color-prettylights-syntax-constant: #0550ae;
--color-prettylights-syntax-entity: #6639ba;
--color-prettylights-syntax-storage-modifier-import: #24292f;
--color-prettylights-syntax-entity-tag: #116329;
--color-prettylights-syntax-keyword: #cf222e;
--color-prettylights-syntax-string: #0a3069;
--color-prettylights-syntax-variable: #953800;
--color-prettylights-syntax-brackethighlighter-unmatched: #82071e;
--color-prettylights-syntax-invalid-illegal-text: #f6f8fa;
--color-prettylights-syntax-invalid-illegal-bg: #82071e;
--color-prettylights-syntax-carriage-return-text: #f6f8fa;
--color-prettylights-syntax-carriage-return-bg: #cf222e;
--color-prettylights-syntax-string-regexp: #116329;
--color-prettylights-syntax-markup-list: #3b2300;
--color-prettylights-syntax-markup-heading: #0550ae;
--color-prettylights-syntax-markup-italic: #24292f;
--color-prettylights-syntax-markup-bold: #24292f;
--color-prettylights-syntax-markup-deleted-text: #82071e;
--color-prettylights-syntax-markup-deleted-bg: #ffebe9;
--color-prettylights-syntax-markup-inserted-text: #116329;
--color-prettylights-syntax-markup-inserted-bg: #dafbe1;
--color-prettylights-syntax-markup-changed-text: #953800;
--color-prettylights-syntax-markup-changed-bg: #ffd8b5;
--color-prettylights-syntax-markup-ignored-text: #eaeef2;
--color-prettylights-syntax-markup-ignored-bg: #0550ae;
--color-prettylights-syntax-meta-diff-range: #8250df;
--color-prettylights-syntax-brackethighlighter-angle: #57606a;
--color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;
--color-prettylights-syntax-constant-other-reference-link: #0a3069;
--color-fg-default: #1F2328;
--color-fg-muted: #656d76;
--color-fg-subtle: #6e7781;
--color-canvas-default: #ffffff;
--color-canvas-subtle: #f6f8fa;
--color-border-default: #d0d7de;
--color-border-muted: hsla(210,18%,87%,1);
--color-neutral-muted: rgba(175,184,193,0.2);
--color-accent-fg: #0969da;
--color-accent-emphasis: #0969da;
--color-attention-fg: #9a6700;
--color-attention-subtle: #fff8c5;
--color-danger-fg: #d1242f;
--color-done-fg: #8250df;
--color-prettylights-syntax-comment: #6e7781;
--color-prettylights-syntax-constant: #0550ae;
--color-prettylights-syntax-entity: #6639ba;
--color-prettylights-syntax-storage-modifier-import: #24292f;
--color-prettylights-syntax-entity-tag: #116329;
--color-prettylights-syntax-keyword: #cf222e;
--color-prettylights-syntax-string: #0a3069;
--color-prettylights-syntax-variable: #953800;
--color-prettylights-syntax-brackethighlighter-unmatched: #82071e;
--color-prettylights-syntax-invalid-illegal-text: #f6f8fa;
--color-prettylights-syntax-invalid-illegal-bg: #82071e;
--color-prettylights-syntax-carriage-return-text: #f6f8fa;
--color-prettylights-syntax-carriage-return-bg: #cf222e;
--color-prettylights-syntax-string-regexp: #116329;
--color-prettylights-syntax-markup-list: #3b2300;
--color-prettylights-syntax-markup-heading: #0550ae;
--color-prettylights-syntax-markup-italic: #24292f;
--color-prettylights-syntax-markup-bold: #24292f;
--color-prettylights-syntax-markup-deleted-text: #82071e;
--color-prettylights-syntax-markup-deleted-bg: #ffebe9;
--color-prettylights-syntax-markup-inserted-text: #116329;
--color-prettylights-syntax-markup-inserted-bg: #dafbe1;
--color-prettylights-syntax-markup-changed-text: #953800;
--color-prettylights-syntax-markup-changed-bg: #ffd8b5;
--color-prettylights-syntax-markup-ignored-text: #eaeef2;
--color-prettylights-syntax-markup-ignored-bg: #0550ae;
--color-prettylights-syntax-meta-diff-range: #8250df;
--color-prettylights-syntax-brackethighlighter-angle: #57606a;
--color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;
--color-prettylights-syntax-constant-other-reference-link: #0a3069;
--color-fg-default: #1F2328;
--color-fg-muted: #656d76;
--color-fg-subtle: #6e7781;
--color-canvas-default: #ffffff;
--color-canvas-subtle: #f6f8fa;
--color-border-default: #d0d7de;
--color-border-muted: hsla(210,18%,87%,1);
--color-neutral-muted: rgba(175,184,193,0.2);
--color-accent-fg: #0969da;
--color-accent-emphasis: #0969da;
--color-attention-fg: #9a6700;
--color-attention-subtle: #fff8c5;
--color-danger-fg: #d1242f;
--color-done-fg: #8250df;
}
}
.markdown-body { .markdown-body {
font-size: 16px; font-size: 16px;

52
src/utils/generateToc.ts Normal file
View file

@ -0,0 +1,52 @@
// Source: https://github.com/withastro/docs/blob/882e0b0a9d16d1c822cb8c230a62a4bfcd308605/src/util/generateToc.ts
import type { MarkdownHeading } from 'astro';
export interface TocItem extends MarkdownHeading {
children: TocItem[];
}
function diveChildren(item: TocItem, depth: number): TocItem[] {
if (depth === 1) {
return item.children;
} else {
// e.g., 2
return diveChildren(item.children[item.children.length - 1], depth - 1);
}
}
export default function generateToc(headings: MarkdownHeading[], title = 'Overview') {
// const overview = { depth: 2, slug: 'overview', text: title };
headings = [...headings.filter(({ depth }) => depth > 1 && depth < 4)];
const toc: Array<TocItem> = [];
for (const heading of headings) {
if (toc.length === 0) {
toc.push({
...heading,
children: [],
});
} else {
const lastItemInToc = toc[toc.length - 1];
if (heading.depth < lastItemInToc.depth) {
throw new Error(`Orphan heading found: ${heading.text}.`);
}
if (heading.depth === lastItemInToc.depth) {
// same depth
toc.push({
...heading,
children: [],
});
} else {
// higher depth
// push into children, or children' children alike
const gap = heading.depth - lastItemInToc.depth;
const target = diveChildren(lastItemInToc, gap);
target.push({
...heading,
children: [],
});
}
}
}
return toc;
}