Aaron Pham
2024-02-05 e58c217de11680cb09fa29ccd5f1493bcb77a60a
quartz/plugins/transformers/ofm.ts
@@ -9,6 +9,8 @@
import { JSResource } from "../../util/resources"
// @ts-ignore
import calloutScript from "../../components/scripts/callout.inline.ts"
// @ts-ignore
import checkboxScript from "../../components/scripts/checkbox.inline.ts"
import { FilePath, pathToRoot, slugTag, slugifyFilePath } from "../../util/path"
import { toHast } from "mdast-util-to-hast"
import { toHtml } from "hast-util-to-html"
@@ -28,6 +30,7 @@
  enableInHtmlEmbed: boolean
  enableYouTubeEmbed: boolean
  enableVideoEmbed: boolean
  enableCheckbox: boolean
}
const defaultOptions: Options = {
@@ -42,6 +45,7 @@
  enableInHtmlEmbed: false,
  enableYouTubeEmbed: true,
  enableVideoEmbed: true,
  enableCheckbox: false,
}
const calloutMapping = {
@@ -74,6 +78,17 @@
  cite: "quote",
} as const
const arrowMapping: Record<string, string> = {
  "->": "&rarr;",
  "-->": "&rArr;",
  "=>": "&rArr;",
  "==>": "&rArr;",
  "<-": "&larr;",
  "<--": "&lArr;",
  "<=": "&lArr;",
  "<==": "&lArr;",
}
function canonicalizeCallout(calloutName: string): keyof typeof calloutMapping {
  const normalizedCallout = calloutName.toLowerCase() as keyof typeof calloutMapping
  // if callout is not recognized, make it a custom one
@@ -82,7 +97,7 @@
export const externalLinkRegex = /^https?:\/\//i
export const arrowRegex = new RegExp(/-{1,2}>/, "g")
export const arrowRegex = new RegExp(/(-{1,2}>|={1,2}>|<-{1,2}|<={1,2})/, "g")
// !?                -> optional embedding
// \[\[              -> open brace
@@ -271,10 +286,12 @@
          if (opts.parseArrows) {
            replacements.push([
              arrowRegex,
              (_value: string, ..._capture: string[]) => {
              (value: string, ..._capture: string[]) => {
                const maybeArrow = arrowMapping[value]
                if (maybeArrow === undefined) return SKIP
                return {
                  type: "html",
                  value: `<span>&rarr;</span>`,
                  value: `<span>${maybeArrow}</span>`,
                }
              },
            ])
@@ -383,14 +400,17 @@
                const calloutType = canonicalizeCallout(typeString.toLowerCase())
                const collapse = collapseChar === "+" || collapseChar === "-"
                const defaultState = collapseChar === "-" ? "collapsed" : "expanded"
                const titleContent =
                  match.input.slice(calloutDirective.length).trim() || capitalize(calloutType)
                const titleContent = match.input.slice(calloutDirective.length).trim()
                const useDefaultTitle = titleContent === "" && restOfTitle.length === 0
                const titleNode: Paragraph = {
                  type: "paragraph",
                  children:
                    restOfTitle.length === 0
                      ? [{ type: "text", value: titleContent + " " }]
                      : restOfTitle,
                  children: [
                    {
                      type: "text",
                      value: useDefaultTitle ? capitalize(calloutType) : titleContent + " ",
                    },
                    ...restOfTitle,
                  ],
                }
                const title = mdastToHtml(titleNode)
@@ -538,11 +558,37 @@
        })
      }
      if (opts.enableCheckbox) {
        plugins.push(() => {
          return (tree: HtmlRoot, _file) => {
            visit(tree, "element", (node) => {
              if (node.tagName === "input" && node.properties.type === "checkbox") {
                const isChecked = node.properties?.checked ?? false
                node.properties = {
                  type: "checkbox",
                  disabled: false,
                  checked: isChecked,
                  class: "checkbox-toggle",
                }
              }
            })
          }
        })
      }
      return plugins
    },
    externalResources() {
      const js: JSResource[] = []
      if (opts.enableCheckbox) {
        js.push({
          script: checkboxScript,
          loadTime: "afterDOMReady",
          contentType: "inline",
        })
      }
      if (opts.callouts) {
        js.push({
          script: calloutScript,