From fcd81353f88b613e5e93c089e10e530d08695b3f Mon Sep 17 00:00:00 2001
From: Jacky Zhao <j.zhao2k19@gmail.com>
Date: Thu, 01 Jun 2023 23:48:38 +0000
Subject: [PATCH] heading linking

---
 quartz/styles/base.scss            |   22 ++++++++--
 package-lock.json                  |   65 ++++++++++++++++++++++++++++++++
 quartz/plugins/transformers/gfm.ts |   15 ++++++-
 quartz/plugins/transformers/ofm.ts |    4 +-
 package.json                       |    3 +
 quartz/components/Head.tsx         |    1 
 quartz/path.ts                     |    5 ++
 7 files changed, 104 insertions(+), 11 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index a9e186e..0b292c0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
         "chalk": "^4.1.2",
         "cli-spinner": "^0.2.10",
         "esbuild-sass-plugin": "^2.9.0",
+        "github-slugger": "^2.0.0",
         "globby": "^13.1.4",
         "gray-matter": "^4.0.3",
         "hast-util-to-jsx-runtime": "^1.2.0",
@@ -23,9 +24,11 @@
         "preact": "^10.14.1",
         "preact-render-to-string": "^6.0.3",
         "pretty-time": "^1.1.0",
+        "rehype-autolink-headings": "^6.1.1",
         "rehype-katex": "^6.0.3",
         "rehype-pretty-code": "^0.9.6",
         "rehype-raw": "^6.1.1",
+        "rehype-slug": "^5.1.0",
         "remark": "^14.0.2",
         "remark-frontmatter": "^4.0.1",
         "remark-gfm": "^3.0.1",
@@ -1565,6 +1568,11 @@
         "node": "6.* || 8.* || >= 10.*"
       }
     },
+    "node_modules/github-slugger": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
+      "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="
+    },
     "node_modules/glob": {
       "version": "10.2.6",
       "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.6.tgz",
@@ -1737,6 +1745,27 @@
         "url": "https://opencollective.com/unified"
       }
     },
+    "node_modules/hast-util-has-property": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-2.0.1.tgz",
+      "integrity": "sha512-X2+RwZIMTMKpXUzlotatPzWj8bspCymtXH3cfG3iQKV+wPF53Vgaqxi/eLqGck0wKq1kS9nvoB1wchbCPEL8sg==",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-heading-rank": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-2.1.1.tgz",
+      "integrity": "sha512-iAuRp+ESgJoRFJbSyaqsfvJDY6zzmFoEnL1gtz1+U8gKtGGj1p0CVlysuUAUjq95qlZESHINLThwJzNGmgGZxA==",
+      "dependencies": {
+        "@types/hast": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
     "node_modules/hast-util-is-element": {
       "version": "2.1.3",
       "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-2.1.3.tgz",
@@ -3229,6 +3258,24 @@
         "node": ">=8.10.0"
       }
     },
+    "node_modules/rehype-autolink-headings": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/rehype-autolink-headings/-/rehype-autolink-headings-6.1.1.tgz",
+      "integrity": "sha512-NMYzZIsHM3sA14nC5rAFuUPIOfg+DFmf9EY1YMhaNlB7+3kK/ZlE6kqPfuxr1tsJ1XWkTrMtMoyHosU70d35mA==",
+      "dependencies": {
+        "@types/hast": "^2.0.0",
+        "extend": "^3.0.0",
+        "hast-util-has-property": "^2.0.0",
+        "hast-util-heading-rank": "^2.0.0",
+        "hast-util-is-element": "^2.0.0",
+        "unified": "^10.0.0",
+        "unist-util-visit": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
     "node_modules/rehype-katex": {
       "version": "6.0.3",
       "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-6.0.3.tgz",
@@ -3275,6 +3322,24 @@
         "url": "https://opencollective.com/unified"
       }
     },
+    "node_modules/rehype-slug": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/rehype-slug/-/rehype-slug-5.1.0.tgz",
+      "integrity": "sha512-Gf91dJoXneiorNEnn+Phx97CO7oRMrpi+6r155tTxzGuLtm+QrI4cTwCa9e1rtePdL4i9tSO58PeSS6HWfgsiw==",
+      "dependencies": {
+        "@types/hast": "^2.0.0",
+        "github-slugger": "^2.0.0",
+        "hast-util-has-property": "^2.0.0",
+        "hast-util-heading-rank": "^2.0.0",
+        "hast-util-to-string": "^2.0.0",
+        "unified": "^10.0.0",
+        "unist-util-visit": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
     "node_modules/remark": {
       "version": "14.0.3",
       "resolved": "https://registry.npmjs.org/remark/-/remark-14.0.3.tgz",
diff --git a/package.json b/package.json
index 46c0c2a..3217520 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
     "chalk": "^4.1.2",
     "cli-spinner": "^0.2.10",
     "esbuild-sass-plugin": "^2.9.0",
+    "github-slugger": "^2.0.0",
     "globby": "^13.1.4",
     "gray-matter": "^4.0.3",
     "hast-util-to-jsx-runtime": "^1.2.0",
@@ -38,9 +39,11 @@
     "preact": "^10.14.1",
     "preact-render-to-string": "^6.0.3",
     "pretty-time": "^1.1.0",
+    "rehype-autolink-headings": "^6.1.1",
     "rehype-katex": "^6.0.3",
     "rehype-pretty-code": "^0.9.6",
     "rehype-raw": "^6.1.1",
+    "rehype-slug": "^5.1.0",
     "remark": "^14.0.2",
     "remark-frontmatter": "^4.0.1",
     "remark-gfm": "^3.0.1",
diff --git a/quartz/components/Head.tsx b/quartz/components/Head.tsx
index c4deb8b..868294d 100644
--- a/quartz/components/Head.tsx
+++ b/quartz/components/Head.tsx
@@ -26,7 +26,6 @@
     <link rel="icon" href={iconPath} />
     <meta name="description" content={description} />
     <meta name="generator" content="Quartz" />
-    <base href={slug} />
     <link rel="preconnect" href="https://fonts.googleapis.com" />
     <link rel="preconnect" href="https://fonts.gstatic.com" />
     <link rel="stylesheet" type="text/css" href={stylePath} />
diff --git a/quartz/path.ts b/quartz/path.ts
index 4127fc0..4f3bfa7 100644
--- a/quartz/path.ts
+++ b/quartz/path.ts
@@ -1,4 +1,7 @@
 import path from 'path'
+import SlugAnchor from 'github-slugger'
+
+const slugAnchor = new SlugAnchor()
 
 function slugSegment(s: string): string {
   return s.replace(/\s/g, '-')
@@ -6,7 +9,7 @@
 
 export function slugify(s: string): string {
   const [fp, anchor] = s.split("#", 2)
-  const sluggedAnchor = anchor === undefined ? "" : "#" + slugSegment(anchor)
+  const sluggedAnchor = anchor === undefined ? "" : "#" + slugAnchor.slug(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/transformers/gfm.ts b/quartz/plugins/transformers/gfm.ts
index ef3a944..55dbda2 100644
--- a/quartz/plugins/transformers/gfm.ts
+++ b/quartz/plugins/transformers/gfm.ts
@@ -2,13 +2,17 @@
 import remarkGfm from "remark-gfm"
 import smartypants from 'remark-smartypants'
 import { QuartzTransformerPlugin } from "../types"
+import rehypeSlug from "rehype-slug"
+import rehypeAutolinkHeadings from "rehype-autolink-headings/lib"
 
 export interface Options {
   enableSmartyPants: boolean
+  linkHeadings: boolean
 }
 
 const defaultOptions: Options = {
-  enableSmartyPants: true
+  enableSmartyPants: true,
+  linkHeadings: true
 }
 
 export class GitHubFlavoredMarkdown extends QuartzTransformerPlugin {
@@ -25,6 +29,13 @@
   }
 
   htmlPlugins(): PluggableList {
-    return []
+    return this.opts.linkHeadings
+      ? [rehypeSlug, [rehypeAutolinkHeadings, {
+        behavior: 'append', content: {
+          type: 'text',
+          value: 'ยง'
+        }
+      }]]
+      : []
   }
 }
diff --git a/quartz/plugins/transformers/ofm.ts b/quartz/plugins/transformers/ofm.ts
index 9215e2c..7569797 100644
--- a/quartz/plugins/transformers/ofm.ts
+++ b/quartz/plugins/transformers/ofm.ts
@@ -42,9 +42,9 @@
 
             } else {
               const [path, rawHeader, rawAlias] = capture
-              const header = rawHeader?.slice(1).trim() ?? ""
+              const anchor = rawHeader?.slice(1).trim() ?? ""
               const alias = rawAlias?.slice(1).trim() ?? path
-              const url = slugify(path.trim() + header)
+              const url = slugify(path.trim() + anchor)
               return {
                 type: 'link',
                 url,
diff --git a/quartz/styles/base.scss b/quartz/styles/base.scss
index 99613f9..5f56b0f 100644
--- a/quartz/styles/base.scss
+++ b/quartz/styles/base.scss
@@ -85,12 +85,24 @@
   font-weight: revert;
   margin: 2rem 0 0;
 
-  &:hover > .hanchor {
-    color: var(--secondary);
-  }
-
   article > & > a {
-    color: var(--dark)
+    color: var(--dark);
+    &.internal {
+      background-color: transparent;
+    }
+  }
+}
+
+h1, h2, h3, h4, h5, h6 {
+  &[id] > a {
+    margin: 0 0.5rem;
+    opacity: 0;
+    transition: opacity 0.2s ease;
+    font-family: var(--codeFont);
+    user-select: none;
+  }
+  &[id]:hover > a {
+    opacity: 1;
   }
 }
 

--
Gitblit v1.10.0