From 9d2024b11c7c24ec8112b5019504fc44b4e1a297 Mon Sep 17 00:00:00 2001
From: Jacky Zhao <j.zhao2k19@gmail.com>
Date: Tue, 13 Jun 2023 05:41:42 +0000
Subject: [PATCH] taglist, mermaid

---
 /dev/null                               |   10 --
 quartz/plugins/transformers/latex.ts    |    3 
 quartz/resources.tsx                    |   28 +++++++
 quartz/components/index.ts              |    4 
 quartz/plugins/transformers/ofm.ts      |   35 ++++++++
 quartz/plugins/transformers/toc.ts      |    4 
 quartz/components/Head.tsx              |    3 
 quartz/components/TableOfContents.tsx   |   45 +++++++---
 quartz/path.ts                          |    6 -
 quartz/components/TagList.tsx           |   42 ++++++++++
 quartz/plugins/emitters/contentPage.tsx |    8 +-
 quartz.config.ts                        |    2 
 12 files changed, 149 insertions(+), 41 deletions(-)

diff --git a/quartz.config.ts b/quartz.config.ts
index d1fe184..5868449 100644
--- a/quartz.config.ts
+++ b/quartz.config.ts
@@ -58,7 +58,7 @@
       Plugin.ContentPage({
         head: Component.Head(),
         header: [Component.PageTitle(), Component.Spacer(), Component.Darkmode()],
-        body: [Component.ArticleTitle(), Component.ReadingTime(), Component.TableOfContents(), Component.Content()]
+        body: [Component.ArticleTitle(), Component.ReadingTime(), Component.TagList(), Component.TableOfContents(), Component.Content()]
       })
     ]
   },
diff --git a/quartz/components/Head.tsx b/quartz/components/Head.tsx
index 16125da..cc9dd77 100644
--- a/quartz/components/Head.tsx
+++ b/quartz/components/Head.tsx
@@ -1,4 +1,5 @@
 import { resolveToRoot } from "../path"
+import { JSResourceToScriptElement } from "../resources"
 import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 
 function Head({ fileData, externalResources }: QuartzComponentProps) {
@@ -25,7 +26,7 @@
     <link rel="preconnect" href="https://fonts.googleapis.com" />
     <link rel="preconnect" href="https://fonts.gstatic.com" />
     {css.map(href => <link key={href} href={href} rel="stylesheet" type="text/css" spa-preserve />)}
-    {js.filter(resource => resource.loadTime === "beforeDOMReady").map(resource => <script key={resource.src} {...resource} spa-preserve />)}
+    {js.filter(resource => resource.loadTime === "beforeDOMReady").map(res => JSResourceToScriptElement(res, true))}
   </head>
 }
 
diff --git a/quartz/components/TableOfContents.tsx b/quartz/components/TableOfContents.tsx
index 531c61d..19f26ef 100644
--- a/quartz/components/TableOfContents.tsx
+++ b/quartz/components/TableOfContents.tsx
@@ -1,21 +1,38 @@
 import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 import style from "./styles/toc.scss"
 
-function TableOfContents({ fileData }: QuartzComponentProps) {
-  if (!fileData.toc) {
-    return null
-  }
 
-  return <details class="toc" open>
-    <summary><h3>Table of Contents</h3></summary>
-    <ul>
-      {fileData.toc.map(tocEntry => <li key={tocEntry.slug} class={`depth-${tocEntry.depth}`}>
-        <a href={`#${tocEntry.slug}`}>{tocEntry.text}</a>
-      </li>)}
-    </ul>
-  </details>
+interface Options {
+  layout: 'modern' | 'quartz-3'
 }
 
-TableOfContents.css = style
+const defaultOptions: Options = {
+  layout: 'quartz-3'
+}
 
-export default (() => TableOfContents) satisfies QuartzComponentConstructor
+export default ((opts?: Partial<Options>) => {
+  const layout = opts?.layout ?? defaultOptions.layout
+  if (layout === "modern") {
+    return function() {
+      return null // TODO (make this look like nextra)
+    }
+  } else {
+    function TableOfContents({ fileData }: QuartzComponentProps) {
+      if (!fileData.toc) {
+        return null
+      }
+
+      return <details class="toc" open>
+        <summary><h3>Table of Contents</h3></summary>
+        <ul>
+          {fileData.toc.map(tocEntry => <li key={tocEntry.slug} class={`depth-${tocEntry.depth}`}>
+            <a href={`#${tocEntry.slug}`}>{tocEntry.text}</a>
+          </li>)}
+        </ul>
+      </details>
+    }
+
+    TableOfContents.css = style
+    return TableOfContents
+  }
+}) satisfies QuartzComponentConstructor
diff --git a/quartz/components/TagList.tsx b/quartz/components/TagList.tsx
new file mode 100644
index 0000000..a462e95
--- /dev/null
+++ b/quartz/components/TagList.tsx
@@ -0,0 +1,42 @@
+import { resolveToRoot } from "../path"
+import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
+import { slug as slugAnchor } from 'github-slugger'
+
+function TagList({ fileData }: QuartzComponentProps) {
+  const tags = fileData.frontmatter?.tags
+  const slug = fileData.slug!
+  const baseDir = resolveToRoot(slug)
+  if (tags) {
+    return <ul class="tags">{tags.map(tag => {
+      const display = `#${tag}`
+      const linkDest = baseDir + `/tags/${slugAnchor(tag)}`
+      return <li>
+        <a href={linkDest}>{display}</a>
+      </li>
+    })}</ul>
+  } else {
+    return null
+  }
+}
+
+TagList.css = `
+.tags {
+  list-style: none;
+  display: flex;
+  padding-left: 0;
+  gap: 0.4rem;
+
+  & > li {
+    display: inline-block;
+    margin: 0;
+
+    & > a {
+      border-radius: 8px;
+      border: var(--lightgray) 1px solid;
+      padding: 0.2rem 0.5rem;
+    }
+  }
+}
+`
+
+export default (() => TagList) satisfies QuartzComponentConstructor
diff --git a/quartz/components/index.ts b/quartz/components/index.ts
index 5fde7c3..72350fe 100644
--- a/quartz/components/index.ts
+++ b/quartz/components/index.ts
@@ -6,6 +6,7 @@
 import ReadingTime from "./ReadingTime"
 import Spacer from "./Spacer"
 import TableOfContents from "./TableOfContents"
+import TagList from "./TagList"
 
 export {
   ArticleTitle,
@@ -15,5 +16,6 @@
   PageTitle,
   ReadingTime,
   Spacer,
-  TableOfContents
+  TableOfContents,
+  TagList
 } 
diff --git a/quartz/path.ts b/quartz/path.ts
index bece770..87f1a9d 100644
--- a/quartz/path.ts
+++ b/quartz/path.ts
@@ -1,7 +1,5 @@
 import path from 'path'
-import SlugAnchor from 'github-slugger'
-
-export const slugAnchor = new SlugAnchor()
+import { slug as slugAnchor } from 'github-slugger'
 
 function slugSegment(s: string): string {
   return s.replace(/\s/g, '-')
@@ -9,7 +7,7 @@
 
 export function slugify(s: string): string {
   const [fp, anchor] = s.split("#", 2)
-  const sluggedAnchor = anchor === undefined ? "" : "#" + slugAnchor.slug(anchor)
+  const sluggedAnchor = anchor === undefined ? "" : "#" + slugAnchor(anchor)
   const withoutFileExt = fp.replace(new RegExp(path.extname(fp) + '$'), '')
   const rawSlugSegments = withoutFileExt.split(path.sep)
   const slugParts: string = rawSlugSegments
diff --git a/quartz/plugins/emitters/contentPage.tsx b/quartz/plugins/emitters/contentPage.tsx
index 9b789ef..039b5cc 100644
--- a/quartz/plugins/emitters/contentPage.tsx
+++ b/quartz/plugins/emitters/contentPage.tsx
@@ -1,4 +1,4 @@
-import { StaticResources } from "../../resources"
+import { JSResourceToScriptElement, StaticResources } from "../../resources"
 import { EmitCallback, QuartzEmitterPlugin } from "../types"
 import { ProcessedContent } from "../vfile"
 import { render } from "preact-render-to-string"
@@ -37,9 +37,9 @@
         const pageResources: StaticResources = {
           css: [baseDir + "/index.css", ...resources.css],
           js: [
-            { src: baseDir + "/prescript.js", loadTime: "beforeDOMReady" },
+            { src: baseDir + "/prescript.js", loadTime: "beforeDOMReady", contentType: "external" },
             ...resources.js,
-            { src: baseDir + "/postscript.js", loadTime: "afterDOMReady", type: 'module' }
+            { src: baseDir + "/postscript.js", loadTime: "afterDOMReady", moduleType: 'module', contentType: "external" }
           ]
         }
 
@@ -63,7 +63,7 @@
               </Body>
             </div>
           </body>
-          {pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(resource => <script key={resource.src} {...resource} />)}
+          {pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(res => JSResourceToScriptElement(res))}
         </html>
 
         const fp = file.data.slug + ".html"
diff --git a/quartz/plugins/transformers/latex.ts b/quartz/plugins/transformers/latex.ts
index 86af69f..3140ab4 100644
--- a/quartz/plugins/transformers/latex.ts
+++ b/quartz/plugins/transformers/latex.ts
@@ -23,7 +23,8 @@
       {
         // fix copy behaviour: https://github.com/KaTeX/KaTeX/blob/main/contrib/copy-tex/README.md
         src: "https://cdn.jsdelivr.net/npm/katex@0.16.7/dist/contrib/copy-tex.min.js",
-        loadTime: "afterDOMReady"
+        loadTime: "afterDOMReady",
+        contentType: 'external'
       }
     ]
   }
diff --git a/quartz/plugins/transformers/ofm.ts b/quartz/plugins/transformers/ofm.ts
index 1733b94..1b4e07a 100644
--- a/quartz/plugins/transformers/ofm.ts
+++ b/quartz/plugins/transformers/ofm.ts
@@ -1,6 +1,6 @@
 import { PluggableList } from "unified"
 import { QuartzTransformerPlugin } from "../types"
-import { Root, HTML, BlockContent, DefinitionContent } from 'mdast'
+import { Root, HTML, BlockContent, DefinitionContent, Code } from 'mdast'
 import { findAndReplace } from "mdast-util-find-and-replace"
 import { slugify } from "../../path"
 import rehypeRaw from "rehype-raw"
@@ -11,12 +11,14 @@
   highlight: boolean
   wikilinks: boolean
   callouts: boolean
+  mermaid: boolean
 }
 
 const defaultOptions: Options = {
   highlight: true,
   wikilinks: true,
-  callouts: true
+  callouts: true,
+  mermaid: false,
 }
 
 const icons = {
@@ -246,11 +248,38 @@
           }
         })
       }
+
+      if (opts.mermaid) {
+        plugins.push(() => {
+          return (tree: Root, _file) => {
+            visit(tree, 'code', (node: Code) => {
+              if (node.lang === 'mermaid') {
+                node.data = {
+                  hProperties: {
+                    className: 'mermaid'
+                  }
+                }
+              }
+            })
+          }
+        })
+      }
+
       return plugins
     },
-
     htmlPlugins() {
       return [rehypeRaw]
+    },
+    externalResources: {
+      js: [{
+        script: `
+import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs';
+mermaid.initialize({ startOnLoad: true });
+        `,
+        loadTime: 'afterDOMReady',
+        moduleType: 'module',
+        contentType: 'inline'
+      }]
     }
   }
 }
diff --git a/quartz/plugins/transformers/toc.ts b/quartz/plugins/transformers/toc.ts
index 2141a87..172f082 100644
--- a/quartz/plugins/transformers/toc.ts
+++ b/quartz/plugins/transformers/toc.ts
@@ -2,7 +2,7 @@
 import { Root } from "mdast"
 import { visit } from "unist-util-visit"
 import { toString } from "mdast-util-to-string"
-import { slugAnchor } from "../../path"
+import { slug as slugAnchor } from 'github-slugger'
 
 export interface Options {
   maxDepth: 1 | 2 | 3 | 4 | 5 | 6,
@@ -40,7 +40,7 @@
                 toc.push({
                   depth: node.depth,
                   text,
-                  slug: slugAnchor.slug(text)
+                  slug: slugAnchor(text)
                 })
               }
             })
diff --git a/quartz/resources.ts b/quartz/resources.ts
deleted file mode 100644
index 9de0b19..0000000
--- a/quartz/resources.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export interface JSResource {
-  src: string
-  loadTime: 'beforeDOMReady' | 'afterDOMReady'
-  type?: 'module'
-}
-
-export interface StaticResources {
-  css: string[],
-  js: JSResource[]
-}
diff --git a/quartz/resources.tsx b/quartz/resources.tsx
new file mode 100644
index 0000000..78ae10b
--- /dev/null
+++ b/quartz/resources.tsx
@@ -0,0 +1,28 @@
+import { randomUUID } from "crypto"
+import { JSX } from "preact/jsx-runtime"
+
+export type JSResource = {
+  loadTime: 'beforeDOMReady' | 'afterDOMReady'
+  moduleType?: 'module'
+} & ({
+  src: string
+  contentType: 'external'
+} | {
+  script: string
+  contentType: 'inline'
+})
+
+export function JSResourceToScriptElement(resource: JSResource, preserve?: boolean): JSX.Element {
+  const scriptType = resource.moduleType ?? 'application/javascript'
+  if (resource.contentType === 'external') {
+    return <script key={resource.src} src={resource.src} type={scriptType} spa-preserve={preserve} />
+  } else {
+    const content = resource.script
+    return <script key={randomUUID()} type={scriptType} spa-preserve={preserve}>{content}</script>
+  }
+}
+
+export interface StaticResources {
+  css: string[],
+  js: JSResource[]
+}

--
Gitblit v1.10.0