various polish
This commit is contained in:
		
							parent
							
								
									4c904d88ab
								
							
						
					
					
						commit
						e0ebee5aa9
					
				
					 30 changed files with 339 additions and 190 deletions
				
			
		| 
						 | 
					@ -4,12 +4,7 @@ import * as Plugin from "./quartz/plugins"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const sharedPageComponents = {
 | 
					const sharedPageComponents = {
 | 
				
			||||||
  head: Component.Head(),
 | 
					  head: Component.Head(),
 | 
				
			||||||
  header: [
 | 
					  header: [],
 | 
				
			||||||
    Component.PageTitle(),
 | 
					 | 
				
			||||||
    Component.Spacer(),
 | 
					 | 
				
			||||||
    Component.Search(),
 | 
					 | 
				
			||||||
    Component.Darkmode()
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
  footer: Component.Footer({
 | 
					  footer: Component.Footer({
 | 
				
			||||||
    authorName: "Jacky",
 | 
					    authorName: "Jacky",
 | 
				
			||||||
    links: {
 | 
					    links: {
 | 
				
			||||||
| 
						 | 
					@ -25,11 +20,15 @@ const contentPageLayout: PageLayout = {
 | 
				
			||||||
    Component.ReadingTime(),
 | 
					    Component.ReadingTime(),
 | 
				
			||||||
    Component.TagList(),
 | 
					    Component.TagList(),
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  left: [],
 | 
					  left: [
 | 
				
			||||||
 | 
					    Component.PageTitle(),
 | 
				
			||||||
 | 
					    Component.Search(),
 | 
				
			||||||
 | 
					    Component.TableOfContents(),
 | 
				
			||||||
 | 
					    Component.Darkmode()
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  right: [
 | 
					  right: [
 | 
				
			||||||
    Component.Graph(),
 | 
					    Component.Graph(),
 | 
				
			||||||
    Component.TableOfContents(),
 | 
					    Component.Backlinks(),
 | 
				
			||||||
    Component.Backlinks()
 | 
					 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,7 +36,11 @@ const listPageLayout: PageLayout = {
 | 
				
			||||||
  beforeBody: [
 | 
					  beforeBody: [
 | 
				
			||||||
    Component.ArticleTitle()
 | 
					    Component.ArticleTitle()
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  left: [],
 | 
					  left: [
 | 
				
			||||||
 | 
					    Component.PageTitle(),
 | 
				
			||||||
 | 
					    Component.Search(),
 | 
				
			||||||
 | 
					    Component.Darkmode()
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  right: [],
 | 
					  right: [],
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,6 +49,9 @@ const config: QuartzConfig = {
 | 
				
			||||||
    pageTitle: "🪴 Quartz 4.0",
 | 
					    pageTitle: "🪴 Quartz 4.0",
 | 
				
			||||||
    enableSPA: true,
 | 
					    enableSPA: true,
 | 
				
			||||||
    enablePopovers: true,
 | 
					    enablePopovers: true,
 | 
				
			||||||
 | 
					    analytics: {
 | 
				
			||||||
 | 
					      provider: 'plausible',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    canonicalUrl: "quartz.jzhao.xyz",
 | 
					    canonicalUrl: "quartz.jzhao.xyz",
 | 
				
			||||||
    ignorePatterns: ["private", "templates"],
 | 
					    ignorePatterns: ["private", "templates"],
 | 
				
			||||||
    theme: {
 | 
					    theme: {
 | 
				
			||||||
| 
						 | 
					@ -102,16 +108,16 @@ const config: QuartzConfig = {
 | 
				
			||||||
        ...contentPageLayout,
 | 
					        ...contentPageLayout,
 | 
				
			||||||
        pageBody: Component.Content(),
 | 
					        pageBody: Component.Content(),
 | 
				
			||||||
      }),
 | 
					      }),
 | 
				
			||||||
      Plugin.TagPage({
 | 
					 | 
				
			||||||
        ...sharedPageComponents,
 | 
					 | 
				
			||||||
        ...listPageLayout,
 | 
					 | 
				
			||||||
        pageBody: Component.TagContent(),
 | 
					 | 
				
			||||||
      }),
 | 
					 | 
				
			||||||
      Plugin.FolderPage({
 | 
					      Plugin.FolderPage({
 | 
				
			||||||
        ...sharedPageComponents,
 | 
					        ...sharedPageComponents,
 | 
				
			||||||
        ...listPageLayout,
 | 
					        ...listPageLayout,
 | 
				
			||||||
        pageBody: Component.FolderContent(),
 | 
					        pageBody: Component.FolderContent(),
 | 
				
			||||||
      }),
 | 
					      }),
 | 
				
			||||||
 | 
					      Plugin.TagPage({
 | 
				
			||||||
 | 
					        ...sharedPageComponents,
 | 
				
			||||||
 | 
					        ...listPageLayout,
 | 
				
			||||||
 | 
					        pageBody: Component.TagContent(),
 | 
				
			||||||
 | 
					      }),
 | 
				
			||||||
      Plugin.ContentIndex({
 | 
					      Plugin.ContentIndex({
 | 
				
			||||||
        enableSiteMap: true,
 | 
					        enableSiteMap: true,
 | 
				
			||||||
        enableRSS: true,
 | 
					        enableRSS: true,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,7 +64,7 @@ yargs(hideBin(process.argv))
 | 
				
			||||||
      packages: "external",
 | 
					      packages: "external",
 | 
				
			||||||
      plugins: [
 | 
					      plugins: [
 | 
				
			||||||
        sassPlugin({
 | 
					        sassPlugin({
 | 
				
			||||||
          type: 'css-text'
 | 
					          type: 'css-text',
 | 
				
			||||||
        }),
 | 
					        }),
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          name: 'inline-script-loader',
 | 
					          name: 'inline-script-loader',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,12 +2,23 @@ import { QuartzComponent } from "./components/types"
 | 
				
			||||||
import { PluginTypes } from "./plugins/types"
 | 
					import { PluginTypes } from "./plugins/types"
 | 
				
			||||||
import { Theme } from "./theme"
 | 
					import { Theme } from "./theme"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type Analytics = null
 | 
				
			||||||
 | 
					  | {
 | 
				
			||||||
 | 
					    provider: 'plausible'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  | {
 | 
				
			||||||
 | 
					    provider: 'google',
 | 
				
			||||||
 | 
					    tagId: string
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface GlobalConfiguration {
 | 
					export interface GlobalConfiguration {
 | 
				
			||||||
  pageTitle: string,
 | 
					  pageTitle: string,
 | 
				
			||||||
  /** Whether to enable single-page-app style rendering. this prevents flashes of unstyled content and improves smoothness of Quartz */
 | 
					  /** Whether to enable single-page-app style rendering. this prevents flashes of unstyled content and improves smoothness of Quartz */
 | 
				
			||||||
  enableSPA: boolean,
 | 
					  enableSPA: boolean,
 | 
				
			||||||
  /** Whether to display Wikipedia-style popovers when hovering over links */
 | 
					  /** Whether to display Wikipedia-style popovers when hovering over links */
 | 
				
			||||||
  enablePopovers: boolean,
 | 
					  enablePopovers: boolean,
 | 
				
			||||||
 | 
					  /** Analytics mode */
 | 
				
			||||||
 | 
					  analytics: Analytics 
 | 
				
			||||||
  /** Glob patterns to not search */
 | 
					  /** Glob patterns to not search */
 | 
				
			||||||
  ignorePatterns: string[],
 | 
					  ignorePatterns: string[],
 | 
				
			||||||
  /** Base URL to use for CNAME files, sitemaps, and RSS feeds that require an absolute URL.
 | 
					  /** Base URL to use for CNAME files, sitemaps, and RSS feeds that require an absolute URL.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,9 +2,8 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function ArticleTitle({ fileData }: QuartzComponentProps) {
 | 
					function ArticleTitle({ fileData }: QuartzComponentProps) {
 | 
				
			||||||
  const title = fileData.frontmatter?.title
 | 
					  const title = fileData.frontmatter?.title
 | 
				
			||||||
  const displayTitle = fileData.slug === "index" ? undefined : title
 | 
					  if (title) {
 | 
				
			||||||
  if (displayTitle) {
 | 
					    return <h1 class="article-title">{title}</h1>
 | 
				
			||||||
    return <h1 class="article-title">{displayTitle}</h1>
 | 
					 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ export default ((opts?: Options) => {
 | 
				
			||||||
    return <>
 | 
					    return <>
 | 
				
			||||||
      <hr />
 | 
					      <hr />
 | 
				
			||||||
      <footer>
 | 
					      <footer>
 | 
				
			||||||
        <p>Made by {name} using <a>Quartz</a>, © {year}</p>
 | 
					        <p>Made by {name} using <a href="https://quartz.jzhao.xyz/">Quartz</a>, © {year}</p>
 | 
				
			||||||
        <ul>{Object.entries(links).map(([text, link]) => <li>
 | 
					        <ul>{Object.entries(links).map(([text, link]) => <li>
 | 
				
			||||||
          <a href={link}>{text}</a>
 | 
					          <a href={link}>{text}</a>
 | 
				
			||||||
        </li>)}</ul>
 | 
					        </li>)}</ul>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,15 +2,7 @@ import { resolveToRoot } from "../path"
 | 
				
			||||||
import { JSResourceToScriptElement } from "../resources"
 | 
					import { JSResourceToScriptElement } from "../resources"
 | 
				
			||||||
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
					import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Options {
 | 
					export default (() => {
 | 
				
			||||||
  prefetchContentIndex: boolean
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const defaultOptions: Options = {
 | 
					 | 
				
			||||||
  prefetchContentIndex: true
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default ((opts?: Options) => {
 | 
					 | 
				
			||||||
  function Head({ fileData, externalResources }: QuartzComponentProps) {
 | 
					  function Head({ fileData, externalResources }: QuartzComponentProps) {
 | 
				
			||||||
    const slug = fileData.slug!
 | 
					    const slug = fileData.slug!
 | 
				
			||||||
    const title = fileData.frontmatter?.title ?? "Untitled"
 | 
					    const title = fileData.frontmatter?.title ?? "Untitled"
 | 
				
			||||||
| 
						 | 
					@ -20,10 +12,6 @@ export default ((opts?: Options) => {
 | 
				
			||||||
    const iconPath = baseDir + "/static/icon.png"
 | 
					    const iconPath = baseDir + "/static/icon.png"
 | 
				
			||||||
    const ogImagePath = baseDir + "/static/og-image.png"
 | 
					    const ogImagePath = baseDir + "/static/og-image.png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const prefetchContentIndex = opts?.prefetchContentIndex ?? defaultOptions.prefetchContentIndex
 | 
					 | 
				
			||||||
    const contentIndexPath = baseDir + "/static/contentIndex.json"
 | 
					 | 
				
			||||||
    const contentIndexScript = `const fetchData = fetch(\`${contentIndexPath}\`).then(data => data.json())`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return <head>
 | 
					    return <head>
 | 
				
			||||||
      <title>{title}</title>
 | 
					      <title>{title}</title>
 | 
				
			||||||
      <meta charSet="utf-8" />
 | 
					      <meta charSet="utf-8" />
 | 
				
			||||||
| 
						 | 
					@ -38,7 +26,6 @@ export default ((opts?: Options) => {
 | 
				
			||||||
      <meta name="generator" content="Quartz" />
 | 
					      <meta name="generator" content="Quartz" />
 | 
				
			||||||
      <link rel="preconnect" href="https://fonts.googleapis.com"/>
 | 
					      <link rel="preconnect" href="https://fonts.googleapis.com"/>
 | 
				
			||||||
      <link rel="preconnect" href="https://fonts.gstatic.com"/>
 | 
					      <link rel="preconnect" href="https://fonts.gstatic.com"/>
 | 
				
			||||||
      {prefetchContentIndex && <script spa-preserve>{contentIndexScript}</script>}
 | 
					 | 
				
			||||||
      {css.map(href => <link key={href} href={href} rel="stylesheet" type="text/css" spa-preserve />)}
 | 
					      {css.map(href => <link key={href} href={href} rel="stylesheet" type="text/css" spa-preserve />)}
 | 
				
			||||||
      {js.filter(resource => resource.loadTime === "beforeDOMReady").map(res => JSResourceToScriptElement(res, true))}
 | 
					      {js.filter(resource => resource.loadTime === "beforeDOMReady").map(res => JSResourceToScriptElement(res, true))}
 | 
				
			||||||
    </head>
 | 
					    </head>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,7 @@ header {
 | 
				
			||||||
  flex-direction: row;
 | 
					  flex-direction: row;
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
  margin: 2em 0;
 | 
					  margin: 2em 0;
 | 
				
			||||||
 | 
					  gap: 1.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
header h1 {
 | 
					header h1 {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,7 @@ function byDateAndAlphabetical(f1: QuartzPluginData, f2: QuartzPluginData): numb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function PageList({ fileData, allFiles }: QuartzComponentProps) {
 | 
					export function PageList({ fileData, allFiles }: QuartzComponentProps) {
 | 
				
			||||||
  const slug = fileData.slug!
 | 
					  const slug = fileData.slug!
 | 
				
			||||||
  return <ul class="section-ul">
 | 
					  return <ul class="section-ul popover-hint">
 | 
				
			||||||
    {allFiles.sort(byDateAndAlphabetical).map(page => {
 | 
					    {allFiles.sort(byDateAndAlphabetical).map(page => {
 | 
				
			||||||
      const title = page.frontmatter?.title
 | 
					      const title = page.frontmatter?.title
 | 
				
			||||||
      const pageSlug = page.slug!
 | 
					      const pageSlug = page.slug!
 | 
				
			||||||
| 
						 | 
					@ -36,9 +36,8 @@ export function PageList({ fileData, allFiles }: QuartzComponentProps) {
 | 
				
			||||||
          <div class="desc">
 | 
					          <div class="desc">
 | 
				
			||||||
            <h3><a href={stripIndex(relativeToRoot(slug, pageSlug))} class="internal">{title}</a></h3>
 | 
					            <h3><a href={stripIndex(relativeToRoot(slug, pageSlug))} class="internal">{title}</a></h3>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div class="spacer"></div>
 | 
					 | 
				
			||||||
          <ul class="tags">
 | 
					          <ul class="tags">
 | 
				
			||||||
            {tags.map(tag => <li><a href={relativeToRoot(slug, `tags/${tag}`)}>#{tag}</a></li>)}
 | 
					            {tags.map(tag => <li><a class="internal" href={relativeToRoot(slug, `tags/${tag}`)}>#{tag}</a></li>)}
 | 
				
			||||||
          </ul>
 | 
					          </ul>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </li>
 | 
					      </li>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,7 @@ function TagList({ fileData }: QuartzComponentProps) {
 | 
				
			||||||
      const display = `#${tag}`
 | 
					      const display = `#${tag}`
 | 
				
			||||||
      const linkDest = baseDir + `/tags/${slugAnchor(tag)}`
 | 
					      const linkDest = baseDir + `/tags/${slugAnchor(tag)}`
 | 
				
			||||||
      return <li>
 | 
					      return <li>
 | 
				
			||||||
        <a href={linkDest}>{display}</a>
 | 
					        <a href={linkDest} class="internal">{display}</a>
 | 
				
			||||||
      </li>
 | 
					      </li>
 | 
				
			||||||
    })}</ul>
 | 
					    })}</ul>
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
| 
						 | 
					@ -25,18 +25,19 @@ TagList.css = `
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  padding-left: 0;
 | 
					  padding-left: 0;
 | 
				
			||||||
  gap: 0.4rem;
 | 
					  gap: 0.4rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  & > li {
 | 
					.tags > li {
 | 
				
			||||||
  display: inline-block;
 | 
					  display: inline-block;
 | 
				
			||||||
  margin: 0;
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  overflow-wrap: normal;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    & > a {
 | 
					.tags > li > a {
 | 
				
			||||||
  border-radius: 8px;
 | 
					  border-radius: 8px;
 | 
				
			||||||
      border: var(--lightgray) 1px solid;
 | 
					  background-color: var(--highlight);
 | 
				
			||||||
  padding: 0.2rem 0.5rem;
 | 
					  padding: 0.2rem 0.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default (() => TagList) satisfies QuartzComponentConstructor
 | 
					export default (() => TagList) satisfies QuartzComponentConstructor
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@ import { toJsxRuntime } from "hast-util-to-jsx-runtime"
 | 
				
			||||||
function Content({ tree }: QuartzComponentProps) {
 | 
					function Content({ tree }: QuartzComponentProps) {
 | 
				
			||||||
  // @ts-ignore (preact makes it angry)
 | 
					  // @ts-ignore (preact makes it angry)
 | 
				
			||||||
  const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
 | 
					  const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
 | 
				
			||||||
  return <article>{content}</article>
 | 
					  return <article class="popover-hint">{content}</article>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default (() => Content) satisfies QuartzComponentConstructor
 | 
					export default (() => Content) satisfies QuartzComponentConstructor
 | 
				
			||||||
| 
						 | 
					@ -17,10 +17,15 @@ interface RenderComponents {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function pageResources(slug: string, staticResources: StaticResources): StaticResources {
 | 
					export function pageResources(slug: string, staticResources: StaticResources): StaticResources {
 | 
				
			||||||
  const baseDir = resolveToRoot(slug)
 | 
					  const baseDir = resolveToRoot(slug)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const contentIndexPath = baseDir + "/static/contentIndex.json"
 | 
				
			||||||
 | 
					  const contentIndexScript = `const fetchData = fetch(\`${contentIndexPath}\`).then(data => data.json())`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    css: [baseDir + "/index.css", ...staticResources.css],
 | 
					    css: [baseDir + "/index.css", ...staticResources.css],
 | 
				
			||||||
    js: [
 | 
					    js: [
 | 
				
			||||||
      { src: baseDir + "/prescript.js", loadTime: "beforeDOMReady", contentType: "external" },
 | 
					      { src: baseDir + "/prescript.js", loadTime: "beforeDOMReady", contentType: "external" },
 | 
				
			||||||
 | 
					      { loadTime: "afterDOMReady", contentType: "inline", spaPreserve: true, script: contentIndexScript },
 | 
				
			||||||
      ...staticResources.js,
 | 
					      ...staticResources.js,
 | 
				
			||||||
      { src: baseDir + "/postscript.js", loadTime: "afterDOMReady", moduleType: 'module', contentType: "external" }
 | 
					      { src: baseDir + "/postscript.js", loadTime: "afterDOMReady", moduleType: 'module', contentType: "external" }
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
| 
						 | 
					@ -32,29 +37,41 @@ export function renderPage(slug: string, componentData: QuartzComponentProps, co
 | 
				
			||||||
  const Header = HeaderConstructor()
 | 
					  const Header = HeaderConstructor()
 | 
				
			||||||
  const Body = BodyConstructor()
 | 
					  const Body = BodyConstructor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const LeftComponent =
 | 
				
			||||||
 | 
					    <div class="left">
 | 
				
			||||||
 | 
					      <div class="left-inner">
 | 
				
			||||||
 | 
					        {left.map(BodyComponent => <BodyComponent {...componentData} />)}
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const RightComponent =
 | 
				
			||||||
 | 
					    <div class="right">
 | 
				
			||||||
 | 
					      <div class="right-inner">
 | 
				
			||||||
 | 
					        {right.map(BodyComponent => <BodyComponent {...componentData} />)}
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const doc = <html>
 | 
					  const doc = <html>
 | 
				
			||||||
    <Head {...componentData} />
 | 
					    <Head {...componentData} />
 | 
				
			||||||
    <body data-slug={slug}>
 | 
					    <body data-slug={slug}>
 | 
				
			||||||
      <div id="quartz-root" class="page">
 | 
					      <div id="quartz-root" class="page">
 | 
				
			||||||
 | 
					        <div class="page-header">
 | 
				
			||||||
          <Header {...componentData} >
 | 
					          <Header {...componentData} >
 | 
				
			||||||
            {header.map(HeaderComponent => <HeaderComponent {...componentData} />)}
 | 
					            {header.map(HeaderComponent => <HeaderComponent {...componentData} />)}
 | 
				
			||||||
          </Header>
 | 
					          </Header>
 | 
				
			||||||
          <div class="popover-hint">
 | 
					          <div class="popover-hint">
 | 
				
			||||||
            {beforeBody.map(BodyComponent => <BodyComponent {...componentData} />)}
 | 
					            {beforeBody.map(BodyComponent => <BodyComponent {...componentData} />)}
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
        <Body {...componentData}>
 | 
					        <Body {...componentData}>
 | 
				
			||||||
          <div class="left">
 | 
					          {LeftComponent}
 | 
				
			||||||
            {left.map(BodyComponent => <BodyComponent {...componentData} />)}
 | 
					          <div class="center">
 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <div class="center popover-hint">
 | 
					 | 
				
			||||||
            <Content {...componentData} />
 | 
					            <Content {...componentData} />
 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <div class="right">
 | 
					 | 
				
			||||||
            {right.map(BodyComponent => <BodyComponent {...componentData} />)}
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </Body>
 | 
					 | 
				
			||||||
            <Footer {...componentData} />
 | 
					            <Footer {...componentData} />
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
 | 
					          {RightComponent}
 | 
				
			||||||
 | 
					        </Body>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
    </body>
 | 
					    </body>
 | 
				
			||||||
    {pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(res => JSResourceToScriptElement(res))}
 | 
					    {pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(res => JSResourceToScriptElement(res))}
 | 
				
			||||||
  </html>
 | 
					  </html>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ const userPref = window.matchMedia('(prefers-color-scheme: light)').matches ? 'l
 | 
				
			||||||
const currentTheme = localStorage.getItem('theme') ?? userPref
 | 
					const currentTheme = localStorage.getItem('theme') ?? userPref
 | 
				
			||||||
document.documentElement.setAttribute('saved-theme', currentTheme)
 | 
					document.documentElement.setAttribute('saved-theme', currentTheme)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.addEventListener('DOMContentLoaded', () => {
 | 
					document.addEventListener("nav", () => {
 | 
				
			||||||
  const switchTheme = (e: any) => {
 | 
					  const switchTheme = (e: any) => {
 | 
				
			||||||
    if (e.target.checked) {
 | 
					    if (e.target.checked) {
 | 
				
			||||||
      document.documentElement.setAttribute('saved-theme', 'dark')
 | 
					      document.documentElement.setAttribute('saved-theme', 'dark')
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,8 @@ window.addEventListener('DOMContentLoaded', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Darkmode toggle
 | 
					  // Darkmode toggle
 | 
				
			||||||
  const toggleSwitch = document.querySelector('#darkmode-toggle') as HTMLInputElement
 | 
					  const toggleSwitch = document.querySelector('#darkmode-toggle') as HTMLInputElement
 | 
				
			||||||
  toggleSwitch.addEventListener('change', switchTheme, false)
 | 
					  toggleSwitch.removeEventListener('change', switchTheme)
 | 
				
			||||||
 | 
					  toggleSwitch.addEventListener('change', switchTheme)
 | 
				
			||||||
  if (currentTheme === 'dark') {
 | 
					  if (currentTheme === 'dark') {
 | 
				
			||||||
    toggleSwitch.checked = true
 | 
					    toggleSwitch.checked = true
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -266,9 +266,9 @@ async function renderGraph(container: string, slug: string) {
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function renderGlobalGraph() {
 | 
					async function renderGlobalGraph() {
 | 
				
			||||||
  const slug = document.body.dataset["slug"]!
 | 
					  const slug = document.body.dataset["slug"]!
 | 
				
			||||||
  renderGraph("global-graph-container", slug)
 | 
					  await renderGraph("global-graph-container", slug)
 | 
				
			||||||
  const container = document.getElementById("global-graph-outer")
 | 
					  const container = document.getElementById("global-graph-outer")
 | 
				
			||||||
  container?.classList.add("active")
 | 
					  container?.classList.add("active")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -293,7 +293,14 @@ document.addEventListener("nav", async (e: unknown) => {
 | 
				
			||||||
  containerIcon?.addEventListener("click", renderGlobalGraph)
 | 
					  containerIcon?.addEventListener("click", renderGlobalGraph)
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.addEventListener('resize', async () => {
 | 
					let resizeEventDebounce: number | undefined = undefined
 | 
				
			||||||
 | 
					window.addEventListener('resize', () => {
 | 
				
			||||||
 | 
					  if (resizeEventDebounce) {
 | 
				
			||||||
 | 
					    clearTimeout(resizeEventDebounce)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  resizeEventDebounce = window.setTimeout(async () => {
 | 
				
			||||||
    const slug = document.body.dataset["slug"]!
 | 
					    const slug = document.body.dataset["slug"]!
 | 
				
			||||||
    await renderGraph("graph-container", slug)
 | 
					    await renderGraph("graph-container", slug)
 | 
				
			||||||
 | 
					  }, 50)
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								quartz/components/scripts/plausible.inline.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								quartz/components/scripts/plausible.inline.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					import Plausible from 'plausible-tracker'
 | 
				
			||||||
 | 
					const { trackPageview } = Plausible()
 | 
				
			||||||
 | 
					document.addEventListener("nav", () => trackPageview())
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,24 @@
 | 
				
			||||||
import { computePosition, flip, inline, shift } from "@floating-ui/dom"
 | 
					import { computePosition, flip, inline, shift } from "@floating-ui/dom"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// from micromorph/src/utils.ts
 | 
				
			||||||
 | 
					// https://github.com/natemoo-re/micromorph/blob/main/src/utils.ts#L5
 | 
				
			||||||
 | 
					export function normalizeRelativeURLs(
 | 
				
			||||||
 | 
					  el: Element | Document,
 | 
				
			||||||
 | 
					  base: string | URL
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  const update = (el: Element, attr: string, base: string | URL) => {
 | 
				
			||||||
 | 
					    el.setAttribute(attr, new URL(el.getAttribute(attr)!, base).pathname)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  el.querySelectorAll('[href^="./"], [href^="../"]').forEach((item) =>
 | 
				
			||||||
 | 
					    update(item, 'href', base)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  el.querySelectorAll('[src^="./"], [src^="../"]').forEach((item) =>
 | 
				
			||||||
 | 
					    update(item, 'src', base)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
document.addEventListener("nav", () => {
 | 
					document.addEventListener("nav", () => {
 | 
				
			||||||
  const links = [...document.getElementsByClassName("internal")] as HTMLLinkElement[]
 | 
					  const links = [...document.getElementsByClassName("internal")] as HTMLLinkElement[]
 | 
				
			||||||
  const p = new DOMParser()
 | 
					  const p = new DOMParser()
 | 
				
			||||||
| 
						 | 
					@ -41,6 +60,7 @@ document.addEventListener("nav", () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (!contents) return
 | 
					      if (!contents) return
 | 
				
			||||||
      const html = p.parseFromString(contents, "text/html")
 | 
					      const html = p.parseFromString(contents, "text/html")
 | 
				
			||||||
 | 
					      normalizeRelativeURLs(html, targetUrl)
 | 
				
			||||||
      const elts = [...html.getElementsByClassName("popover-hint")]
 | 
					      const elts = [...html.getElementsByClassName("popover-hint")]
 | 
				
			||||||
      if (elts.length === 0) return
 | 
					      if (elts.length === 0) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,11 +75,13 @@ document.addEventListener("nav", () => {
 | 
				
			||||||
      link.appendChild(popoverElement)
 | 
					      link.appendChild(popoverElement)
 | 
				
			||||||
      link.dataset.fetchedPopover = "true"
 | 
					      link.dataset.fetchedPopover = "true"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (hash !== "") {
 | 
				
			||||||
        const heading = popoverInner.querySelector(hash) as HTMLElement | null
 | 
					        const heading = popoverInner.querySelector(hash) as HTMLElement | null
 | 
				
			||||||
        if (heading) {
 | 
					        if (heading) {
 | 
				
			||||||
          // leave ~12px of buffer when scrolling to a heading
 | 
					          // leave ~12px of buffer when scrolling to a heading
 | 
				
			||||||
          popoverInner.scroll({ top: heading.offsetTop - 12, behavior: 'instant' })
 | 
					          popoverInner.scroll({ top: heading.offsetTop - 12, behavior: 'instant' })
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,13 +7,9 @@
 | 
				
			||||||
  & > ul {
 | 
					  & > ul {
 | 
				
			||||||
    list-style: none;
 | 
					    list-style: none;
 | 
				
			||||||
    padding: 0;
 | 
					    padding: 0;
 | 
				
			||||||
    margin: 0;
 | 
					    margin: 0.5rem 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    & > li {
 | 
					    & > li {
 | 
				
			||||||
      margin: 0.5rem 0;
 | 
					 | 
				
			||||||
      padding: 0.25rem 1rem;
 | 
					 | 
				
			||||||
      border: var(--lightgray) 1px solid;
 | 
					 | 
				
			||||||
      border-radius: 5px;
 | 
					 | 
				
			||||||
      & > a {
 | 
					      & > a {
 | 
				
			||||||
        background-color: transparent;
 | 
					        background-color: transparent;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,8 @@
 | 
				
			||||||
footer {
 | 
					footer {
 | 
				
			||||||
  text-align: left;
 | 
					  text-align: left;
 | 
				
			||||||
  opacity: 0.8;
 | 
					  opacity: 0.8;
 | 
				
			||||||
 | 
					  margin-bottom: 4rem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  & ul {
 | 
					  & ul {
 | 
				
			||||||
    list-style: none;
 | 
					    list-style: none;
 | 
				
			||||||
    margin: 0;
 | 
					    margin: 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@
 | 
				
			||||||
    height: 250px;
 | 
					    height: 250px;
 | 
				
			||||||
    margin: 0.5em 0;
 | 
					    margin: 0.5em 0;
 | 
				
			||||||
    position: relative;
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    overflow: hidden;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    & > #global-graph-icon {
 | 
					    & > #global-graph-icon {
 | 
				
			||||||
      color: var(--dark);
 | 
					      color: var(--dark);
 | 
				
			||||||
| 
						 | 
					@ -30,10 +31,6 @@
 | 
				
			||||||
        background-color: var(--lightgray);
 | 
					        background-color: var(--lightgray);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    & > #graph-container > svg {
 | 
					 | 
				
			||||||
      margin-bottom: -5px;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  & > #global-graph-outer {
 | 
					  & > #global-graph-outer {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,29 +8,36 @@ li.section-li {
 | 
				
			||||||
  margin-bottom: 1em;
 | 
					  margin-bottom: 1em;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  & > .section {
 | 
					  & > .section {
 | 
				
			||||||
    display: flex;
 | 
					    display: grid;
 | 
				
			||||||
    align-items: center;
 | 
					    grid-template-columns: 6em 3fr 1fr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @media all and (max-width: 600px) {
 | 
					    @media all and (max-width: 600px) {
 | 
				
			||||||
      & .tags {
 | 
					      & > .tags {
 | 
				
			||||||
        display: none;
 | 
					        display: none;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    & h3 > a {
 | 
					    & > .tags {
 | 
				
			||||||
      font-weight: 700;
 | 
					      justify-self: end;
 | 
				
			||||||
      margin: 0;
 | 
					      margin-left: 1rem;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    & > .desc a {
 | 
				
			||||||
      background-color: transparent; 
 | 
					      background-color: transparent; 
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    & p {
 | 
					    & > .meta {
 | 
				
			||||||
      margin: 0;
 | 
					      margin: 0;
 | 
				
			||||||
      padding-right: 1em;
 | 
					 | 
				
			||||||
      flex-basis: 6em;
 | 
					      flex-basis: 6em;
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  & .meta {
 | 
					 | 
				
			||||||
      opacity: 0.6;
 | 
					      opacity: 0.6;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// modifications in popover context
 | 
				
			||||||
 | 
					.popover .section {
 | 
				
			||||||
 | 
					  grid-template-columns: 6em 1fr !important;
 | 
				
			||||||
 | 
					  & > .tags {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,7 +24,7 @@
 | 
				
			||||||
    height: 20rem;
 | 
					    height: 20rem;
 | 
				
			||||||
    padding: 0 1rem 1rem 1rem;
 | 
					    padding: 0 1rem 1rem 1rem;
 | 
				
			||||||
    font-weight: initial;
 | 
					    font-weight: initial;
 | 
				
			||||||
    line-height: initial;
 | 
					    line-height: normal;
 | 
				
			||||||
    font-size: initial;
 | 
					    font-size: initial;
 | 
				
			||||||
    font-family: var(--bodyFont);
 | 
					    font-family: var(--bodyFont);
 | 
				
			||||||
    border: 1px solid var(--gray);
 | 
					    border: 1px solid var(--gray);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,7 @@
 | 
				
			||||||
.search {
 | 
					.search {
 | 
				
			||||||
  min-width: 5rem;
 | 
					  min-width: 5rem;
 | 
				
			||||||
  max-width: 12rem;
 | 
					  max-width: 14rem;
 | 
				
			||||||
  flex-grow: 0.3;
 | 
					  flex-grow: 0.3;
 | 
				
			||||||
  margin: 0 1.5rem;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  & > #search-icon {
 | 
					  & > #search-icon {
 | 
				
			||||||
    background-color: var(--lightgray);
 | 
					    background-color: var(--lightgray);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ 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>
 | 
					  tree: Node<QuartzPluginData>
 | 
				
			||||||
  allFiles: QuartzPluginData[]
 | 
					  allFiles: QuartzPluginData[]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,17 @@ function slugSegment(s: string): string {
 | 
				
			||||||
  return s.replace(/\s/g, '-')
 | 
					  return s.replace(/\s/g, '-')
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// on the client, 'index' isn't ever rendered so we should clean it up
 | 
				
			||||||
 | 
					export function clientSideSlug(fp: string): string {
 | 
				
			||||||
 | 
					  if (fp.endsWith("index")) {
 | 
				
			||||||
 | 
					    fp = fp.slice(0, -"index".length)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return fp
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function trimPathSuffix(fp: string): string {
 | 
					export function trimPathSuffix(fp: string): string {
 | 
				
			||||||
 | 
					  fp = clientSideSlug(fp)
 | 
				
			||||||
  let [cleanPath, anchor] = fp.split("#", 2)
 | 
					  let [cleanPath, anchor] = fp.split("#", 2)
 | 
				
			||||||
  anchor = anchor === undefined ? "" : "#" + anchor
 | 
					  anchor = anchor === undefined ? "" : "#" + anchor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,9 +37,6 @@ export function slugify(s: string): string {
 | 
				
			||||||
// resolve /a/b/c to ../../
 | 
					// resolve /a/b/c to ../../
 | 
				
			||||||
export function resolveToRoot(slug: string): string {
 | 
					export function resolveToRoot(slug: string): string {
 | 
				
			||||||
  let fp = trimPathSuffix(slug)
 | 
					  let fp = trimPathSuffix(slug)
 | 
				
			||||||
  if (fp.endsWith("index")) {
 | 
					 | 
				
			||||||
    fp = fp.slice(0, -"index".length)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (fp === "") {
 | 
					  if (fp === "") {
 | 
				
			||||||
    return "."
 | 
					    return "."
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,6 @@ function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndex): string {
 | 
				
			||||||
  const base = cfg.canonicalUrl ?? ""
 | 
					  const base = cfg.canonicalUrl ?? ""
 | 
				
			||||||
  const root = `https://${base}`
 | 
					  const root = `https://${base}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // TODO: ogimage
 | 
					 | 
				
			||||||
  const createURLEntry = (slug: string, content: ContentDetails): string => `<items>
 | 
					  const createURLEntry = (slug: string, content: ContentDetails): string => `<items>
 | 
				
			||||||
    <title>${content.title}</title>
 | 
					    <title>${content.title}</title>
 | 
				
			||||||
    <link>${root}/${slug}</link>
 | 
					    <link>${root}/${slug}</link>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,29 +1,17 @@
 | 
				
			||||||
import { GlobalConfiguration } from '../cfg'
 | 
					import { GlobalConfiguration } from '../cfg'
 | 
				
			||||||
import { QuartzComponent } from '../components/types'
 | 
					import { QuartzComponent } from '../components/types'
 | 
				
			||||||
import { StaticResources } from '../resources'
 | 
					import { StaticResources } from '../resources'
 | 
				
			||||||
import { googleFontHref, joinStyles } from '../theme'
 | 
					import { joinStyles } from '../theme'
 | 
				
			||||||
import { EmitCallback, PluginTypes } from './types'
 | 
					import { EmitCallback, PluginTypes } from './types'
 | 
				
			||||||
import styles from '../styles/base.scss'
 | 
					import styles from '../styles/base.scss'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// @ts-ignore
 | 
					 | 
				
			||||||
import spaRouterScript from '../components/scripts/spa.inline'
 | 
					 | 
				
			||||||
// @ts-ignore
 | 
					 | 
				
			||||||
import popoverScript from '../components/scripts/popover.inline'
 | 
					 | 
				
			||||||
import popoverStyle from '../components/styles/popover.scss'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export type ComponentResources = {
 | 
					export type ComponentResources = {
 | 
				
			||||||
  css: string[],
 | 
					  css: string[],
 | 
				
			||||||
  beforeDOMLoaded: string[],
 | 
					  beforeDOMLoaded: string[],
 | 
				
			||||||
  afterDOMLoaded: string[]
 | 
					  afterDOMLoaded: string[]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function joinScripts(scripts: string[]): string {
 | 
					export function getComponentResources(plugins: PluginTypes): ComponentResources {
 | 
				
			||||||
  // wrap with iife to prevent scope collision
 | 
					 | 
				
			||||||
  return scripts.map(script => `(function () {${script}})();`).join("\n")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function emitComponentResources(cfg: GlobalConfiguration, resources: StaticResources, plugins: PluginTypes, emit: EmitCallback) {
 | 
					 | 
				
			||||||
  const fps: string[] = []
 | 
					 | 
				
			||||||
  const allComponents: Set<QuartzComponent> = new Set()
 | 
					  const allComponents: Set<QuartzComponent> = new Set()
 | 
				
			||||||
  for (const emitter of plugins.emitters) {
 | 
					  for (const emitter of plugins.emitters) {
 | 
				
			||||||
    const components = emitter.getQuartzComponents()
 | 
					    const components = emitter.getQuartzComponents()
 | 
				
			||||||
| 
						 | 
					@ -51,40 +39,34 @@ export function emitComponentResources(cfg: GlobalConfiguration, resources: Stat
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (cfg.enablePopovers) {
 | 
					  return componentResources
 | 
				
			||||||
    componentResources.afterDOMLoaded.push(popoverScript)
 | 
					 | 
				
			||||||
    componentResources.css.push(popoverStyle)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (cfg.enableSPA) {
 | 
					function joinScripts(scripts: string[]): string {
 | 
				
			||||||
    componentResources.afterDOMLoaded.push(spaRouterScript)
 | 
					  // wrap with iife to prevent scope collision
 | 
				
			||||||
  } else {
 | 
					  return scripts.map(script => `(function () {${script}})();`).join("\n")
 | 
				
			||||||
    componentResources.afterDOMLoaded.push(`
 | 
					 | 
				
			||||||
      window.spaNavigate = (url, _) => window.location.assign(url)
 | 
					 | 
				
			||||||
      const event = new CustomEvent("nav", { detail: { slug: document.body.dataset.slug } })
 | 
					 | 
				
			||||||
      document.dispatchEvent(event)`
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function emitComponentResources(cfg: GlobalConfiguration, res: ComponentResources, emit: EmitCallback): Promise<string[]> {
 | 
				
			||||||
 | 
					  const fps = await Promise.all([
 | 
				
			||||||
    emit({
 | 
					    emit({
 | 
				
			||||||
      slug: "index",
 | 
					      slug: "index",
 | 
				
			||||||
      ext: ".css",
 | 
					      ext: ".css",
 | 
				
			||||||
    content: joinStyles(cfg.theme, styles, ...componentResources.css)
 | 
					      content: joinStyles(cfg.theme, styles, ...res.css)
 | 
				
			||||||
  })
 | 
					    }),
 | 
				
			||||||
    emit({
 | 
					    emit({
 | 
				
			||||||
      slug: "prescript",
 | 
					      slug: "prescript",
 | 
				
			||||||
      ext: ".js",
 | 
					      ext: ".js",
 | 
				
			||||||
    content: joinScripts(componentResources.beforeDOMLoaded)
 | 
					      content: joinScripts(res.beforeDOMLoaded)
 | 
				
			||||||
  })
 | 
					    }),
 | 
				
			||||||
    emit({
 | 
					    emit({
 | 
				
			||||||
      slug: "postscript",
 | 
					      slug: "postscript",
 | 
				
			||||||
      ext: ".js",
 | 
					      ext: ".js",
 | 
				
			||||||
    content: joinScripts(componentResources.afterDOMLoaded)
 | 
					      content: joinScripts(res.afterDOMLoaded)
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					  ])
 | 
				
			||||||
  fps.push("index.css", "prescript.js", "postscript.js")
 | 
					 | 
				
			||||||
  resources.css.push(googleFontHref(cfg.theme))
 | 
					 | 
				
			||||||
  return fps
 | 
					  return fps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function getStaticResourcesFromPlugins(plugins: PluginTypes) {
 | 
					export function getStaticResourcesFromPlugins(plugins: PluginTypes) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
import { PluggableList } from "unified"
 | 
					 | 
				
			||||||
import remarkGfm from "remark-gfm"
 | 
					import remarkGfm from "remark-gfm"
 | 
				
			||||||
import smartypants from 'remark-smartypants'
 | 
					import smartypants from 'remark-smartypants'
 | 
				
			||||||
import { QuartzTransformerPlugin } from "../types"
 | 
					import { QuartzTransformerPlugin } from "../types"
 | 
				
			||||||
| 
						 | 
					@ -20,14 +19,14 @@ export const GitHubFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> |
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    name: "GitHubFlavoredMarkdown",
 | 
					    name: "GitHubFlavoredMarkdown",
 | 
				
			||||||
    markdownPlugins() {
 | 
					    markdownPlugins() {
 | 
				
			||||||
      return opts.enableSmartyPants ? [remarkGfm] : [remarkGfm, smartypants]
 | 
					      return opts.enableSmartyPants ? [remarkGfm, smartypants] : [remarkGfm]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    htmlPlugins() {
 | 
					    htmlPlugins() {
 | 
				
			||||||
      if (opts.linkHeadings) {
 | 
					      if (opts.linkHeadings) {
 | 
				
			||||||
        return [rehypeSlug, [rehypeAutolinkHeadings, {
 | 
					        return [rehypeSlug, [rehypeAutolinkHeadings, {
 | 
				
			||||||
          behavior: 'append', content: {
 | 
					          behavior: 'append', content: {
 | 
				
			||||||
            type: 'text',
 | 
					            type: 'text',
 | 
				
			||||||
            value: ' §'
 | 
					            value: ' §',
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }]]
 | 
					        }]]
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
import { QuartzTransformerPlugin } from "../types"
 | 
					import { QuartzTransformerPlugin } from "../types"
 | 
				
			||||||
import { relativeToRoot, slugify, trimPathSuffix } from "../../path"
 | 
					import { clientSideSlug, relativeToRoot, slugify, trimPathSuffix } from "../../path"
 | 
				
			||||||
import path from "path"
 | 
					import path from "path"
 | 
				
			||||||
import { visit } from 'unist-util-visit'
 | 
					import { visit } from 'unist-util-visit'
 | 
				
			||||||
import isAbsoluteUrl from "is-absolute-url"
 | 
					import isAbsoluteUrl from "is-absolute-url"
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,7 @@ export const CrawlLinks: QuartzTransformerPlugin<Partial<Options> | undefined> =
 | 
				
			||||||
    htmlPlugins() {
 | 
					    htmlPlugins() {
 | 
				
			||||||
      return [() => {
 | 
					      return [() => {
 | 
				
			||||||
        return (tree, file) => {
 | 
					        return (tree, file) => {
 | 
				
			||||||
          const curSlug = file.data.slug!
 | 
					          const curSlug = clientSideSlug(file.data.slug!)
 | 
				
			||||||
          const transformLink = (target: string) => {
 | 
					          const transformLink = (target: string) => {
 | 
				
			||||||
            const targetSlug = slugify(decodeURI(target).trim())
 | 
					            const targetSlug = slugify(decodeURI(target).trim())
 | 
				
			||||||
            if (opts.markdownLinkResolution === 'relative' && !path.isAbsolute(targetSlug)) {
 | 
					            if (opts.markdownLinkResolution === 'relative' && !path.isAbsolute(targetSlug)) {
 | 
				
			||||||
| 
						 | 
					@ -49,7 +49,6 @@ export const CrawlLinks: QuartzTransformerPlugin<Partial<Options> | undefined> =
 | 
				
			||||||
              let dest = node.properties.href
 | 
					              let dest = node.properties.href
 | 
				
			||||||
              node.properties.className = isAbsoluteUrl(dest) ? "external" : "internal"
 | 
					              node.properties.className = isAbsoluteUrl(dest) ? "external" : "internal"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
              // don't process external links or intra-document anchors
 | 
					              // don't process external links or intra-document anchors
 | 
				
			||||||
              if (!(isAbsoluteUrl(dest) || dest.startsWith("#"))) {
 | 
					              if (!(isAbsoluteUrl(dest) || dest.startsWith("#"))) {
 | 
				
			||||||
                node.properties.href = transformLink(dest)
 | 
					                node.properties.href = transformLink(dest)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,69 @@
 | 
				
			||||||
import path from "path"
 | 
					import path from "path"
 | 
				
			||||||
import fs from "fs"
 | 
					import fs from "fs"
 | 
				
			||||||
import { QuartzConfig } from "../cfg"
 | 
					import { GlobalConfiguration, QuartzConfig } from "../cfg"
 | 
				
			||||||
import { PerfTimer } from "../perf"
 | 
					import { PerfTimer } from "../perf"
 | 
				
			||||||
import { emitComponentResources, getStaticResourcesFromPlugins } from "../plugins"
 | 
					import { ComponentResources, emitComponentResources, getComponentResources, getStaticResourcesFromPlugins } from "../plugins"
 | 
				
			||||||
import { EmitCallback } from "../plugins/types"
 | 
					import { EmitCallback } from "../plugins/types"
 | 
				
			||||||
import { ProcessedContent } from "../plugins/vfile"
 | 
					import { ProcessedContent } from "../plugins/vfile"
 | 
				
			||||||
import { QUARTZ, slugify } from "../path"
 | 
					import { QUARTZ, slugify } from "../path"
 | 
				
			||||||
import { globbyStream } from "globby"
 | 
					import { globbyStream } from "globby"
 | 
				
			||||||
import chalk from "chalk"
 | 
					import chalk from "chalk"
 | 
				
			||||||
 | 
					import { googleFontHref } from '../theme'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// @ts-ignore
 | 
				
			||||||
 | 
					import spaRouterScript from '../components/scripts/spa.inline'
 | 
				
			||||||
 | 
					// @ts-ignore
 | 
				
			||||||
 | 
					import plausibleScript from '../components/scripts/plausible.inline'
 | 
				
			||||||
 | 
					// @ts-ignore
 | 
				
			||||||
 | 
					import popoverScript from '../components/scripts/popover.inline'
 | 
				
			||||||
 | 
					import popoverStyle from '../components/styles/popover.scss'
 | 
				
			||||||
 | 
					import { StaticResources } from "../resources"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function addGlobalPageResources(cfg: GlobalConfiguration, staticResources: StaticResources, componentResources: ComponentResources) {
 | 
				
			||||||
 | 
					  // font and other resources
 | 
				
			||||||
 | 
					  staticResources.css.push(googleFontHref(cfg.theme))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // popovers
 | 
				
			||||||
 | 
					  if (cfg.enablePopovers) {
 | 
				
			||||||
 | 
					    componentResources.afterDOMLoaded.push(popoverScript)
 | 
				
			||||||
 | 
					    componentResources.css.push(popoverStyle)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (cfg.analytics?.provider === "google") {
 | 
				
			||||||
 | 
					    const tagId = cfg.analytics.tagId
 | 
				
			||||||
 | 
					    staticResources.js.push({
 | 
				
			||||||
 | 
					      src: `https://www.googletagmanager.com/gtag/js?id=${tagId}`,
 | 
				
			||||||
 | 
					      contentType: 'external',
 | 
				
			||||||
 | 
					      loadTime: 'afterDOMReady',
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    componentResources.afterDOMLoaded.push(`
 | 
				
			||||||
 | 
					    window.dataLayer = window.dataLayer || [];
 | 
				
			||||||
 | 
					    function gtag() { dataLayer.push(arguments); }
 | 
				
			||||||
 | 
					    gtag(\`js\`, new Date());
 | 
				
			||||||
 | 
					    gtag(\`config\`, \`${tagId}\`, { send_page_view: false });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    document.addEventListener(\`nav\`, () => {
 | 
				
			||||||
 | 
					      gtag(\`event\`, \`page_view\`, {
 | 
				
			||||||
 | 
					        page_title: document.title,
 | 
				
			||||||
 | 
					        page_location: location.href,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });`
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  } else if (cfg.analytics?.provider === "plausible") {
 | 
				
			||||||
 | 
					    componentResources.afterDOMLoaded.push(plausibleScript)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // spa
 | 
				
			||||||
 | 
					  if (cfg.enableSPA) {
 | 
				
			||||||
 | 
					    componentResources.afterDOMLoaded.push(spaRouterScript)
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    componentResources.afterDOMLoaded.push(`
 | 
				
			||||||
 | 
					      window.spaNavigate = (url, _) => window.location.assign(url)
 | 
				
			||||||
 | 
					      const event = new CustomEvent("nav", { detail: { slug: document.body.dataset.slug } })
 | 
				
			||||||
 | 
					      document.dispatchEvent(event)`
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function emitContent(contentFolder: string, output: string, cfg: QuartzConfig, content: ProcessedContent[], verbose: boolean) {
 | 
					export async function emitContent(contentFolder: string, output: string, cfg: QuartzConfig, content: ProcessedContent[], verbose: boolean) {
 | 
				
			||||||
  const perf = new PerfTimer()
 | 
					  const perf = new PerfTimer()
 | 
				
			||||||
| 
						 | 
					@ -19,9 +75,25 @@ export async function emitContent(contentFolder: string, output: string, cfg: Qu
 | 
				
			||||||
    return pathToPage
 | 
					    return pathToPage
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // initialize from plugins
 | 
				
			||||||
  const staticResources = getStaticResourcesFromPlugins(cfg.plugins)
 | 
					  const staticResources = getStaticResourcesFromPlugins(cfg.plugins)
 | 
				
			||||||
  emitComponentResources(cfg.configuration, staticResources, cfg.plugins, emit)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // component specific scripts and styles
 | 
				
			||||||
 | 
					  const componentResources = getComponentResources(cfg.plugins)
 | 
				
			||||||
 | 
					  // important that this goes *after* component scripts 
 | 
				
			||||||
 | 
					  // as the "nav" event gets triggered here and we should make sure 
 | 
				
			||||||
 | 
					  // that everyone else had the chance to register a listener for it
 | 
				
			||||||
 | 
					  addGlobalPageResources(cfg.configuration, staticResources, componentResources)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // emit in one go
 | 
				
			||||||
 | 
					  const emittedResources = await emitComponentResources(cfg.configuration, componentResources, emit)
 | 
				
			||||||
 | 
					  if (verbose) {
 | 
				
			||||||
 | 
					    for (const file of emittedResources) {
 | 
				
			||||||
 | 
					      console.log(`[emit:Resources] ${file}`)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // emitter plugins
 | 
				
			||||||
  let emittedFiles = 0
 | 
					  let emittedFiles = 0
 | 
				
			||||||
  for (const emitter of cfg.plugins.emitters) {
 | 
					  for (const emitter of cfg.plugins.emitters) {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,8 @@ import { JSX } from "preact/jsx-runtime"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type JSResource = {
 | 
					export type JSResource = {
 | 
				
			||||||
  loadTime: 'beforeDOMReady' | 'afterDOMReady'
 | 
					  loadTime: 'beforeDOMReady' | 'afterDOMReady'
 | 
				
			||||||
  moduleType?: 'module'
 | 
					  moduleType?: 'module',
 | 
				
			||||||
 | 
					  spaPreserve?: boolean
 | 
				
			||||||
} & ({
 | 
					} & ({
 | 
				
			||||||
  src: string
 | 
					  src: string
 | 
				
			||||||
  contentType: 'external'
 | 
					  contentType: 'external'
 | 
				
			||||||
| 
						 | 
					@ -14,11 +15,12 @@ export type JSResource = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function JSResourceToScriptElement(resource: JSResource, preserve?: boolean): JSX.Element {
 | 
					export function JSResourceToScriptElement(resource: JSResource, preserve?: boolean): JSX.Element {
 | 
				
			||||||
  const scriptType = resource.moduleType ?? 'application/javascript'
 | 
					  const scriptType = resource.moduleType ?? 'application/javascript'
 | 
				
			||||||
 | 
					  const spaPreserve = preserve ?? resource.spaPreserve
 | 
				
			||||||
  if (resource.contentType === 'external') {
 | 
					  if (resource.contentType === 'external') {
 | 
				
			||||||
    return <script key={resource.src} src={resource.src} type={scriptType} spa-preserve={preserve} />
 | 
					    return <script key={resource.src} src={resource.src} type={scriptType} spa-preserve={spaPreserve} />
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    const content = resource.script
 | 
					    const content = resource.script
 | 
				
			||||||
    return <script key={randomUUID()} type={scriptType} spa-preserve={preserve}>{content}</script>
 | 
					    return <script key={randomUUID()} type={scriptType} spa-preserve={spaPreserve}>{content}</script>
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,9 @@ body {
 | 
				
			||||||
  box-sizing: border-box;
 | 
					  box-sizing: border-box;
 | 
				
			||||||
  background-color: var(--light);
 | 
					  background-color: var(--light);
 | 
				
			||||||
  font-family: var(--bodyFont);
 | 
					  font-family: var(--bodyFont);
 | 
				
			||||||
 | 
					  --pageWidth: 800px;
 | 
				
			||||||
 | 
					  --sidePanelWidth: 400px;
 | 
				
			||||||
 | 
					  --topSpacing: 6rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.text-highlight {
 | 
					.text-highlight {
 | 
				
			||||||
| 
						 | 
					@ -27,7 +30,7 @@ p, ul, text, a, tr, td, li, ol, ul, .katex {
 | 
				
			||||||
a {
 | 
					a {
 | 
				
			||||||
  font-weight: 600;
 | 
					  font-weight: 600;
 | 
				
			||||||
  text-decoration: none;
 | 
					  text-decoration: none;
 | 
				
			||||||
  transition: all 0.2s ease;
 | 
					  transition: color 0.2s ease;
 | 
				
			||||||
  color: var(--secondary);
 | 
					  color: var(--secondary);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &:hover {
 | 
					  &:hover {
 | 
				
			||||||
| 
						 | 
					@ -43,34 +46,48 @@ a {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.page {
 | 
					.page {
 | 
				
			||||||
  margin: 6rem 35vw 6rem 20vw;
 | 
					  & > .page-header {
 | 
				
			||||||
  max-width: 1000px;
 | 
					    max-width: var(--pageWidth);
 | 
				
			||||||
  position: relative;
 | 
					    margin: var(--topSpacing) auto 0 auto;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  & > #quartz-body {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    & .left, & .right {
 | 
					    & .left, & .right {
 | 
				
			||||||
    position: fixed;
 | 
					      flex: 1;
 | 
				
			||||||
    height: 100vh;
 | 
					      width: calc(calc(100vw - var(--pageWidth)) / 2);
 | 
				
			||||||
    overflow-y: scroll;
 | 
					    }
 | 
				
			||||||
    box-sizing: border-box;
 | 
					
 | 
				
			||||||
 | 
					    & .left-inner, & .right-inner {
 | 
				
			||||||
      display: flex;
 | 
					      display: flex;
 | 
				
			||||||
      flex-direction: column;
 | 
					      flex-direction: column;
 | 
				
			||||||
    top: 0;
 | 
					 | 
				
			||||||
      gap: 2rem;
 | 
					      gap: 2rem;
 | 
				
			||||||
    padding: 6rem;
 | 
					      top: 0;
 | 
				
			||||||
 | 
					      width: var(--sidePanelWidth);
 | 
				
			||||||
 | 
					      margin-top: calc(var(--topSpacing));
 | 
				
			||||||
 | 
					      box-sizing: border-box;
 | 
				
			||||||
 | 
					      padding: 0 4rem;
 | 
				
			||||||
 | 
					      position: fixed;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  & .left {
 | 
					    & .left-inner {
 | 
				
			||||||
    left: 0;
 | 
					      left: calc(calc(100vw - var(--pageWidth)) / 2 - var(--sidePanelWidth));
 | 
				
			||||||
    padding-left: 10vw;
 | 
					 | 
				
			||||||
    width: 20vw;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  & .right {
 | 
					    & .right-inner {
 | 
				
			||||||
    right: 0;
 | 
					      right: calc(calc(100vw - var(--pageWidth)) / 2 - var(--sidePanelWidth));
 | 
				
			||||||
    padding-right: 10vw;
 | 
					 | 
				
			||||||
    width: 35vw;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    & .center {
 | 
				
			||||||
 | 
					      width: var(--pageWidth);
 | 
				
			||||||
 | 
					      margin: 0 auto;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.page {
 | 
				
			||||||
  @media all and (max-width: 1200px) {
 | 
					  @media all and (max-width: 1200px) {
 | 
				
			||||||
    margin: 25px 5vw;
 | 
					    margin: 25px 5vw;
 | 
				
			||||||
    & .left, & .right {
 | 
					    & .left, & .right {
 | 
				
			||||||
| 
						 | 
					@ -89,7 +106,24 @@ a {
 | 
				
			||||||
    & > h1 {
 | 
					    & > h1 {
 | 
				
			||||||
      font-size: 2rem;
 | 
					      font-size: 2rem;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // darkmode diagrams
 | 
				
			||||||
 | 
					    & svg {
 | 
				
			||||||
 | 
					      stroke: var(--dark);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    & ul:has(input[type='checkbox']) {
 | 
				
			||||||
 | 
					      list-style-type: none;
 | 
				
			||||||
 | 
					      padding-left: 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					input[type="checkbox"] {
 | 
				
			||||||
 | 
					  transform: translateY(2px);
 | 
				
			||||||
 | 
					  color: var(--secondary);
 | 
				
			||||||
 | 
					  border-color: var(--lightgray);
 | 
				
			||||||
 | 
					  background-color: var(--light);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
blockquote {
 | 
					blockquote {
 | 
				
			||||||
| 
						 | 
					@ -120,7 +154,7 @@ thead {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
h1, h2, h3, h4, h5, h6 {
 | 
					h1, h2, h3, h4, h5, h6 {
 | 
				
			||||||
  &[id] > a {
 | 
					  &[id] > a[href^="#"] {
 | 
				
			||||||
    margin: 0 0.5rem;
 | 
					    margin: 0 0.5rem;
 | 
				
			||||||
    opacity: 0;
 | 
					    opacity: 0;
 | 
				
			||||||
    transition: opacity 0.2s ease;
 | 
					    transition: opacity 0.2s ease;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue