toc
This commit is contained in:
		@@ -1,21 +1,6 @@
 | 
				
			|||||||
import { QuartzConfig } from "./quartz/cfg"
 | 
					import { QuartzConfig } from "./quartz/cfg"
 | 
				
			||||||
import Body from "./quartz/components/Body"
 | 
					import * as Component from "./quartz/components"
 | 
				
			||||||
import Darkmode from "./quartz/components/Darkmode"
 | 
					import * as Plugin from "./quartz/plugins"
 | 
				
			||||||
import Head from "./quartz/components/Head"
 | 
					 | 
				
			||||||
import PageTitle from "./quartz/components/PageTitle"
 | 
					 | 
				
			||||||
import Spacer from "./quartz/components/Spacer"
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  ContentPage,
 | 
					 | 
				
			||||||
  CreatedModifiedDate,
 | 
					 | 
				
			||||||
  Description,
 | 
					 | 
				
			||||||
  FrontMatter,
 | 
					 | 
				
			||||||
  GitHubFlavoredMarkdown,
 | 
					 | 
				
			||||||
  Katex,
 | 
					 | 
				
			||||||
  ObsidianFlavoredMarkdown,
 | 
					 | 
				
			||||||
  RemoveDrafts,
 | 
					 | 
				
			||||||
  ResolveLinks,
 | 
					 | 
				
			||||||
  SyntaxHighlighting
 | 
					 | 
				
			||||||
} from "./quartz/plugins"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const config: QuartzConfig = {
 | 
					const config: QuartzConfig = {
 | 
				
			||||||
  configuration: {
 | 
					  configuration: {
 | 
				
			||||||
@@ -54,25 +39,26 @@ const config: QuartzConfig = {
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  plugins: {
 | 
					  plugins: {
 | 
				
			||||||
    transformers: [
 | 
					    transformers: [
 | 
				
			||||||
      new FrontMatter(),
 | 
					      new Plugin.FrontMatter(),
 | 
				
			||||||
      new Katex(),
 | 
					      new Plugin.Description(),
 | 
				
			||||||
      new Description(),
 | 
					      new Plugin.TableOfContents({ showByDefault: true }),
 | 
				
			||||||
      new CreatedModifiedDate({
 | 
					      new Plugin.CreatedModifiedDate({
 | 
				
			||||||
        priority: ['frontmatter', 'filesystem'] // you can add 'git' here for last modified from Git but this makes the build slower
 | 
					        priority: ['frontmatter', 'filesystem'] // you can add 'git' here for last modified from Git but this makes the build slower
 | 
				
			||||||
      }),
 | 
					      }),
 | 
				
			||||||
      new SyntaxHighlighting(),
 | 
					      new Plugin.GitHubFlavoredMarkdown(),
 | 
				
			||||||
      new GitHubFlavoredMarkdown(),
 | 
					      new Plugin.ObsidianFlavoredMarkdown(),
 | 
				
			||||||
      new ObsidianFlavoredMarkdown(),
 | 
					      new Plugin.ResolveLinks(),
 | 
				
			||||||
      new ResolveLinks(),
 | 
					      new Plugin.SyntaxHighlighting(),
 | 
				
			||||||
 | 
					      new Plugin.Katex(),
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    filters: [
 | 
					    filters: [
 | 
				
			||||||
      new RemoveDrafts()
 | 
					      new Plugin.RemoveDrafts()
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    emitters: [
 | 
					    emitters: [
 | 
				
			||||||
      new ContentPage({
 | 
					      new Plugin.ContentPage({
 | 
				
			||||||
        head: Head,
 | 
					        head: Component.Head,
 | 
				
			||||||
        header: [PageTitle, Spacer, Darkmode],
 | 
					        header: [Component.PageTitle, Component.Spacer, Component.Darkmode],
 | 
				
			||||||
        body: Body
 | 
					        body: [Component.ArticleTitle, Component.ReadingTime, Component.TableOfContents, Component.Content]
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								quartz/components/ArticleTitle.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								quartz/components/ArticleTitle.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					import { QuartzComponentProps } from "./types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function ArticleTitle({ fileData }: QuartzComponentProps) {
 | 
				
			||||||
 | 
					  const title = fileData.frontmatter?.title
 | 
				
			||||||
 | 
					  const displayTitle = fileData.slug === "index" ? undefined : title
 | 
				
			||||||
 | 
					  if (displayTitle) {
 | 
				
			||||||
 | 
					    return <h1>{displayTitle}</h1>
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    return null
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,13 +2,8 @@ import clipboardScript from './scripts/clipboard.inline'
 | 
				
			|||||||
import clipboardStyle from './styles/clipboard.scss'
 | 
					import clipboardStyle from './styles/clipboard.scss'
 | 
				
			||||||
import { QuartzComponentProps } from "./types"
 | 
					import { QuartzComponentProps } from "./types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Body({ fileData, children }: QuartzComponentProps) {
 | 
					export default function Body({ children }: QuartzComponentProps) {
 | 
				
			||||||
  const title = fileData.frontmatter?.title
 | 
					 | 
				
			||||||
  const displayTitle = fileData.slug === "index" ? undefined : title
 | 
					 | 
				
			||||||
  return <article>
 | 
					  return <article>
 | 
				
			||||||
    <div class="top-section">
 | 
					 | 
				
			||||||
      {displayTitle && <h1>{displayTitle}</h1>}
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
    {children}
 | 
					    {children}
 | 
				
			||||||
  </article>
 | 
					  </article>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9
									
								
								quartz/components/Content.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								quartz/components/Content.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					import { QuartzComponentProps } from "./types"
 | 
				
			||||||
 | 
					import { Fragment, jsx, jsxs } from 'preact/jsx-runtime'
 | 
				
			||||||
 | 
					import { toJsxRuntime } from "hast-util-to-jsx-runtime"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Content({ tree }: QuartzComponentProps) {
 | 
				
			||||||
 | 
					  // @ts-ignore (preact makes it angry)
 | 
				
			||||||
 | 
					  const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
 | 
				
			||||||
 | 
					  return content
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,3 @@
 | 
				
			|||||||
import style from './styles/header.scss'
 | 
					 | 
				
			||||||
import { QuartzComponentProps } from "./types"
 | 
					import { QuartzComponentProps } from "./types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Header({ children }: QuartzComponentProps) {
 | 
					export default function Header({ children }: QuartzComponentProps) {
 | 
				
			||||||
@@ -7,4 +6,18 @@ export default function Header({ children }: QuartzComponentProps) {
 | 
				
			|||||||
  </header>
 | 
					  </header>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Header.css = style
 | 
					Header.css = `
 | 
				
			||||||
 | 
					header {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: row;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  margin: 1em 0 2em 0;
 | 
				
			||||||
 | 
					  & > h1 {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					header > h1 {
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  flex: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								quartz/components/ReadingTime.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								quartz/components/ReadingTime.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					import { QuartzComponentProps } from "./types"
 | 
				
			||||||
 | 
					import readingTime from "reading-time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function ReadingTime({ fileData }: QuartzComponentProps) {
 | 
				
			||||||
 | 
					  const text = fileData.text
 | 
				
			||||||
 | 
					  const isHomePage = fileData.slug === "index"
 | 
				
			||||||
 | 
					  if (text && !isHomePage) {
 | 
				
			||||||
 | 
					    const { text: timeTaken, words } = readingTime(text)
 | 
				
			||||||
 | 
					    return <p class="reading-time">{words} words, {timeTaken}</p>
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    return null
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ReadingTime.css = `
 | 
				
			||||||
 | 
					.reading-time {
 | 
				
			||||||
 | 
					  margin-top: 0;
 | 
				
			||||||
 | 
					  opacity: 0.5;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
							
								
								
									
										24
									
								
								quartz/components/TableOfContents.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								quartz/components/TableOfContents.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					import { QuartzComponentProps } from "./types"
 | 
				
			||||||
 | 
					import style from "./styles/toc.scss"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function TableOfContents({ fileData, position }: QuartzComponentProps) {
 | 
				
			||||||
 | 
					  if (!fileData.toc) {
 | 
				
			||||||
 | 
					    return null
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (position === 'body') {
 | 
				
			||||||
 | 
					    // TODO: animate this
 | 
				
			||||||
 | 
					    return <details className="toc" open>
 | 
				
			||||||
 | 
					      <summary><h3>Table of Contents</h3></summary>
 | 
				
			||||||
 | 
					      <ul>
 | 
				
			||||||
 | 
					        {fileData.toc.map(tocEntry => <li key={tocEntry.slug} className={`depth-${tocEntry.depth}`}>
 | 
				
			||||||
 | 
					          <a href={`#${tocEntry.slug}`}>{tocEntry.text}</a>
 | 
				
			||||||
 | 
					        </li>)}
 | 
				
			||||||
 | 
					      </ul>
 | 
				
			||||||
 | 
					    </details>
 | 
				
			||||||
 | 
					  } else if (position === 'sidebar') {
 | 
				
			||||||
 | 
					    // TODO
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TableOfContents.css = style
 | 
				
			||||||
							
								
								
									
										19
									
								
								quartz/components/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								quartz/components/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					import ArticleTitle from "./ArticleTitle"
 | 
				
			||||||
 | 
					import Content from "./Content"
 | 
				
			||||||
 | 
					import Darkmode from "./Darkmode"
 | 
				
			||||||
 | 
					import Head from "./Head"
 | 
				
			||||||
 | 
					import PageTitle from "./PageTitle"
 | 
				
			||||||
 | 
					import ReadingTime from "./ReadingTime"
 | 
				
			||||||
 | 
					import Spacer from "./Spacer"
 | 
				
			||||||
 | 
					import TableOfContents from "./TableOfContents"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {
 | 
				
			||||||
 | 
					  ArticleTitle,
 | 
				
			||||||
 | 
					  Content,
 | 
				
			||||||
 | 
					  Darkmode,
 | 
				
			||||||
 | 
					  Head,
 | 
				
			||||||
 | 
					  PageTitle,
 | 
				
			||||||
 | 
					  ReadingTime,
 | 
				
			||||||
 | 
					  Spacer,
 | 
				
			||||||
 | 
					  TableOfContents
 | 
				
			||||||
 | 
					} 
 | 
				
			||||||
@@ -1,10 +0,0 @@
 | 
				
			|||||||
header {
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  flex-direction: row;
 | 
					 | 
				
			||||||
  align-items: center;
 | 
					 | 
				
			||||||
  margin: 1em 0 2em 0;
 | 
					 | 
				
			||||||
  & > h1 {
 | 
					 | 
				
			||||||
    margin: 0;
 | 
					 | 
				
			||||||
    flex: auto;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										27
									
								
								quartz/components/styles/toc.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								quartz/components/styles/toc.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					details.toc {
 | 
				
			||||||
 | 
					  & summary {
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &::marker {
 | 
				
			||||||
 | 
					      color: var(--dark);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    & > * {
 | 
				
			||||||
 | 
					      padding-left: 0.25rem;
 | 
				
			||||||
 | 
					      display: inline-block;
 | 
				
			||||||
 | 
					      margin: 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  & ul {
 | 
				
			||||||
 | 
					    list-style: none;
 | 
				
			||||||
 | 
					    margin: 0.5rem 1.25rem;
 | 
				
			||||||
 | 
					    padding: 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @for $i from 1 through 6 {
 | 
				
			||||||
 | 
					    & .depth-#{$i} {
 | 
				
			||||||
 | 
					      padding-left: calc(1rem * #{$i});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,12 +2,15 @@ import { ComponentType, JSX } from "preact"
 | 
				
			|||||||
import { StaticResources } from "../resources"
 | 
					import { StaticResources } from "../resources"
 | 
				
			||||||
import { QuartzPluginData } from "../plugins/vfile"
 | 
					import { QuartzPluginData } from "../plugins/vfile"
 | 
				
			||||||
import { GlobalConfiguration } from "../cfg"
 | 
					import { GlobalConfiguration } from "../cfg"
 | 
				
			||||||
 | 
					import { Node } from "hast"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type QuartzComponentProps = {
 | 
					export type QuartzComponentProps = {
 | 
				
			||||||
  externalResources: StaticResources
 | 
					  externalResources: StaticResources
 | 
				
			||||||
  fileData: QuartzPluginData
 | 
					  fileData: QuartzPluginData
 | 
				
			||||||
  cfg: GlobalConfiguration
 | 
					  cfg: GlobalConfiguration
 | 
				
			||||||
  children: QuartzComponent[] | JSX.Element[]
 | 
					  children: QuartzComponent[] | JSX.Element[]
 | 
				
			||||||
 | 
					  tree: Node<QuartzPluginData>
 | 
				
			||||||
 | 
					  position?: 'sidebar' | 'header' | 'body'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type QuartzComponent = ComponentType<QuartzComponentProps> & {
 | 
					export type QuartzComponent = ComponentType<QuartzComponentProps> & {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import path from 'path'
 | 
					import path from 'path'
 | 
				
			||||||
import SlugAnchor from 'github-slugger'
 | 
					import SlugAnchor from 'github-slugger'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const slugAnchor = new SlugAnchor()
 | 
					export const slugAnchor = new SlugAnchor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function slugSegment(s: string): string {
 | 
					function slugSegment(s: string): string {
 | 
				
			||||||
  return s.replace(/\s/g, '-')
 | 
					  return s.replace(/\s/g, '-')
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,19 +1,18 @@
 | 
				
			|||||||
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
 | 
					 | 
				
			||||||
import { StaticResources } from "../../resources"
 | 
					import { StaticResources } from "../../resources"
 | 
				
			||||||
import { EmitCallback, QuartzEmitterPlugin } from "../types"
 | 
					import { EmitCallback, QuartzEmitterPlugin } from "../types"
 | 
				
			||||||
import { ProcessedContent } from "../vfile"
 | 
					import { ProcessedContent } from "../vfile"
 | 
				
			||||||
import { Fragment, jsx, jsxs } from 'preact/jsx-runtime'
 | 
					 | 
				
			||||||
import { render } from "preact-render-to-string"
 | 
					import { render } from "preact-render-to-string"
 | 
				
			||||||
import { GlobalConfiguration } from "../../cfg"
 | 
					import { GlobalConfiguration } from "../../cfg"
 | 
				
			||||||
import { QuartzComponent } from "../../components/types"
 | 
					import { QuartzComponent } from "../../components/types"
 | 
				
			||||||
import { resolveToRoot } from "../../path"
 | 
					import { resolveToRoot } from "../../path"
 | 
				
			||||||
import Header from "../../components/Header"
 | 
					import Header from "../../components/Header"
 | 
				
			||||||
import { QuartzComponentProps } from "../../components/types"
 | 
					import { QuartzComponentProps } from "../../components/types"
 | 
				
			||||||
 | 
					import Body from "../../components/Body"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Options {
 | 
					interface Options {
 | 
				
			||||||
  head: QuartzComponent
 | 
					  head: QuartzComponent
 | 
				
			||||||
  header: QuartzComponent[],
 | 
					  header: QuartzComponent[],
 | 
				
			||||||
  body: QuartzComponent
 | 
					  body: QuartzComponent[]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ContentPage extends QuartzEmitterPlugin {
 | 
					export class ContentPage extends QuartzEmitterPlugin {
 | 
				
			||||||
@@ -26,17 +25,14 @@ export class ContentPage extends QuartzEmitterPlugin {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getQuartzComponents(): QuartzComponent[] {
 | 
					  getQuartzComponents(): QuartzComponent[] {
 | 
				
			||||||
    return [this.opts.head, Header, ...this.opts.header, this.opts.body]
 | 
					    return [this.opts.head, Header, ...this.opts.header, ...this.opts.body]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emit: EmitCallback): Promise<string[]> {
 | 
					  async emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emit: EmitCallback): Promise<string[]> {
 | 
				
			||||||
    const fps: string[] = []
 | 
					    const fps: string[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const { head: Head, header, body: Body } = this.opts
 | 
					    const { head: Head, header, body } = this.opts
 | 
				
			||||||
    for (const [tree, file] of content) {
 | 
					    for (const [tree, file] of content) {
 | 
				
			||||||
      // @ts-ignore (preact makes it angry)
 | 
					 | 
				
			||||||
      const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const baseDir = resolveToRoot(file.data.slug!)
 | 
					      const baseDir = resolveToRoot(file.data.slug!)
 | 
				
			||||||
      const pageResources: StaticResources = {
 | 
					      const pageResources: StaticResources = {
 | 
				
			||||||
        css: [baseDir + "/index.css", ...resources.css],
 | 
					        css: [baseDir + "/index.css", ...resources.css],
 | 
				
			||||||
@@ -51,7 +47,8 @@ export class ContentPage extends QuartzEmitterPlugin {
 | 
				
			|||||||
        fileData: file.data,
 | 
					        fileData: file.data,
 | 
				
			||||||
        externalResources: pageResources,
 | 
					        externalResources: pageResources,
 | 
				
			||||||
        cfg,
 | 
					        cfg,
 | 
				
			||||||
        children: [content]
 | 
					        children: [],
 | 
				
			||||||
 | 
					        tree
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const doc = <html>
 | 
					      const doc = <html>
 | 
				
			||||||
@@ -59,10 +56,10 @@ export class ContentPage extends QuartzEmitterPlugin {
 | 
				
			|||||||
        <body>
 | 
					        <body>
 | 
				
			||||||
          <div id="quartz-root" class="page">
 | 
					          <div id="quartz-root" class="page">
 | 
				
			||||||
            <Header {...componentData} >
 | 
					            <Header {...componentData} >
 | 
				
			||||||
              {header.map(HeaderComponent => <HeaderComponent {...componentData}/>)}
 | 
					              {header.map(HeaderComponent => <HeaderComponent {...componentData} position="header" />)}
 | 
				
			||||||
            </Header>
 | 
					            </Header>
 | 
				
			||||||
            <Body {...componentData}>
 | 
					            <Body {...componentData}>
 | 
				
			||||||
              {content}
 | 
					              {body.map(BodyComponent => <BodyComponent {...componentData } position="body" />)}
 | 
				
			||||||
            </Body>
 | 
					            </Body>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </body>
 | 
					        </body>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ export class Description extends QuartzTransformerPlugin {
 | 
				
			|||||||
  name = "Description"
 | 
					  name = "Description"
 | 
				
			||||||
  opts: Options
 | 
					  opts: Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(opts?: Options) {
 | 
					  constructor(opts?: Partial<Options>) {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
    this.opts = { ...defaultOptions, ...opts }
 | 
					    this.opts = { ...defaultOptions, ...opts }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@ export class FrontMatter extends QuartzTransformerPlugin {
 | 
				
			|||||||
  name = "FrontMatter"
 | 
					  name = "FrontMatter"
 | 
				
			||||||
  opts: Options
 | 
					  opts: Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(opts?: Options) {
 | 
					  constructor(opts?: Partial<Options>) {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
    this.opts = { ...defaultOptions, ...opts }
 | 
					    this.opts = { ...defaultOptions, ...opts }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ export class GitHubFlavoredMarkdown extends QuartzTransformerPlugin {
 | 
				
			|||||||
  name = "GitHubFlavoredMarkdown"
 | 
					  name = "GitHubFlavoredMarkdown"
 | 
				
			||||||
  opts: Options
 | 
					  opts: Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(opts?: Options) {
 | 
					  constructor(opts?: Partial<Options>) {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
    this.opts = { ...defaultOptions, ...opts }
 | 
					    this.opts = { ...defaultOptions, ...opts }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,3 +6,4 @@ export { Description } from './description'
 | 
				
			|||||||
export { ResolveLinks } from './links'
 | 
					export { ResolveLinks } from './links'
 | 
				
			||||||
export { ObsidianFlavoredMarkdown } from './ofm'
 | 
					export { ObsidianFlavoredMarkdown } from './ofm'
 | 
				
			||||||
export { SyntaxHighlighting } from './syntax'
 | 
					export { SyntaxHighlighting } from './syntax'
 | 
				
			||||||
 | 
					export { TableOfContents } from './toc'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ export class CreatedModifiedDate extends QuartzTransformerPlugin {
 | 
				
			|||||||
  name = "CreatedModifiedDate"
 | 
					  name = "CreatedModifiedDate"
 | 
				
			||||||
  opts: Options
 | 
					  opts: Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(opts?: Options) {
 | 
					  constructor(opts?: Partial<Options>) {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
    this.opts = {
 | 
					    this.opts = {
 | 
				
			||||||
      ...defaultOptions,
 | 
					      ...defaultOptions,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ export class ResolveLinks extends QuartzTransformerPlugin {
 | 
				
			|||||||
  name = "LinkProcessing"
 | 
					  name = "LinkProcessing"
 | 
				
			||||||
  opts: Options
 | 
					  opts: Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(opts?: Options) {
 | 
					  constructor(opts?: Partial<Options>) {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
    this.opts = { ...defaultOptions, ...opts }
 | 
					    this.opts = { ...defaultOptions, ...opts }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -93,7 +93,7 @@ export class ObsidianFlavoredMarkdown extends QuartzTransformerPlugin {
 | 
				
			|||||||
  name = "ObsidianFlavoredMarkdown"
 | 
					  name = "ObsidianFlavoredMarkdown"
 | 
				
			||||||
  opts: Options
 | 
					  opts: Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(opts?: Options) {
 | 
					  constructor(opts?: Partial<Options>) {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
    this.opts = { ...defaultOptions, ...opts }
 | 
					    this.opts = { ...defaultOptions, ...opts }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										72
									
								
								quartz/plugins/transformers/toc.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								quartz/plugins/transformers/toc.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					import { PluggableList } from "unified"
 | 
				
			||||||
 | 
					import { QuartzTransformerPlugin } from "../types"
 | 
				
			||||||
 | 
					import { Root } from "mdast"
 | 
				
			||||||
 | 
					import { visit } from "unist-util-visit"
 | 
				
			||||||
 | 
					import { toString } from "mdast-util-to-string"
 | 
				
			||||||
 | 
					import { slugAnchor } from "../../path"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface Options {
 | 
				
			||||||
 | 
					  maxDepth: 1 | 2 | 3 | 4 | 5 | 6,
 | 
				
			||||||
 | 
					  minEntries: 1,
 | 
				
			||||||
 | 
					  showByDefault: boolean
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const defaultOptions: Options = {
 | 
				
			||||||
 | 
					  maxDepth: 3,
 | 
				
			||||||
 | 
					  minEntries: 1,
 | 
				
			||||||
 | 
					  showByDefault: true,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface TocEntry {
 | 
				
			||||||
 | 
					  depth: number,
 | 
				
			||||||
 | 
					  text: string,
 | 
				
			||||||
 | 
					  slug: string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class TableOfContents extends QuartzTransformerPlugin {
 | 
				
			||||||
 | 
					  name = "TableOfContents"
 | 
				
			||||||
 | 
					  opts: Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(opts?: Partial<Options>) {
 | 
				
			||||||
 | 
					    super()
 | 
				
			||||||
 | 
					    this.opts = { ...defaultOptions, ...opts }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  markdownPlugins(): PluggableList {
 | 
				
			||||||
 | 
					    return [() => {
 | 
				
			||||||
 | 
					      return async (tree: Root, file) => {
 | 
				
			||||||
 | 
					        const display = file.data.frontmatter?.enableToc ?? this.opts.showByDefault
 | 
				
			||||||
 | 
					        if (display) {
 | 
				
			||||||
 | 
					          const toc: TocEntry[] = []
 | 
				
			||||||
 | 
					          let highestDepth: number = this.opts.maxDepth
 | 
				
			||||||
 | 
					          visit(tree, 'heading', (node) => {
 | 
				
			||||||
 | 
					            if (node.depth <= this.opts.maxDepth) {
 | 
				
			||||||
 | 
					              const text = toString(node)
 | 
				
			||||||
 | 
					              highestDepth = Math.min(highestDepth, node.depth)
 | 
				
			||||||
 | 
					              toc.push({
 | 
				
			||||||
 | 
					                depth: node.depth,
 | 
				
			||||||
 | 
					                text,
 | 
				
			||||||
 | 
					                slug: slugAnchor.slug(text)
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (toc.length > this.opts.minEntries) {
 | 
				
			||||||
 | 
					            file.data.toc = toc.map(entry => ({ ...entry, depth: entry.depth - highestDepth }))
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  htmlPlugins(): PluggableList {
 | 
				
			||||||
 | 
					    return []
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					declare module 'vfile' {
 | 
				
			||||||
 | 
					  interface DataMap {
 | 
				
			||||||
 | 
					    toc: TocEntry[]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Reference in New Issue
	
	Block a user