Jacky Zhao
2023-07-26 cc7950267089648e4329531105fe5f8ba011b1b4
make layouts simpler to think about
1 files added
7 files modified
239 ■■■■ changed files
content/advanced/making plugins.md 12 ●●●● patch | view | raw | blame | history
content/configuration.md 4 ●●● patch | view | raw | blame | history
content/features/upcoming features.md 2 ●●●●● patch | view | raw | blame | history
quartz.config.ts 135 ●●●●● patch | view | raw | blame | history
quartz.layout.ts 39 ●●●●● patch | view | raw | blame | history
quartz/plugins/emitters/contentPage.tsx 17 ●●●●● patch | view | raw | blame | history
quartz/plugins/emitters/folderPage.tsx 15 ●●●●● patch | view | raw | blame | history
quartz/plugins/emitters/tagPage.tsx 15 ●●●●● patch | view | raw | blame | history
content/advanced/making plugins.md
@@ -4,17 +4,9 @@
This part of the documentation will assume you have some basic coding knowledge and will include code snippets that describe the interface of what Quartz plugins should look like.
## Transformers
![[quartz-transform-pipeline.png]]
```ts
export type QuartzTransformerPluginInstance = {
  name: string
  textTransform?: (src: string | Buffer) => string | Buffer
  markdownPlugins?: () => PluggableList
  htmlPlugins?: () => PluggableList
  externalResources?: () => Partial<StaticResources>
}
```
## Transformers
## Filters
content/configuration.md
@@ -75,8 +75,10 @@
]
```
If you'd like to make your own plugins, read the guide on [[making plugins]] for more information.
### Layout
Certain emitters may also output [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML) files. To make sure that
Certain emitters may also output [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML) files. To enable easy customization, these emitters allow you to fully rearrange the layout of the page.
### Components
content/features/upcoming features.md
@@ -6,8 +6,6 @@
- images in same folder are broken on shortest path mode
- watch mode for config/source code
- publish metadata https://help.obsidian.md/Editing+and+formatting/Metadata#Metadata+for+Obsidian+Publish
- metadata aliases: https://help.obsidian.md/Editing+and+formatting/Metadata#Predefined+metadata
- block links: https://help.obsidian.md/Linking+notes+and+files/Internal+links#Link+to+a+block+in+a+note
- note/header/block transcludes: https://help.obsidian.md/Linking+notes+and+files/Embedding+files
quartz.config.ts
@@ -1,83 +1,46 @@
import { GlobalConfiguration, PageLayout, QuartzConfig } from "./quartz/cfg"
import * as Component from "./quartz/components"
import { QuartzConfig } from "./quartz/cfg"
import * as Plugin from "./quartz/plugins"
const generalConfiguration: GlobalConfiguration = {
  pageTitle: "🪴 Quartz 4.0",
  enableSPA: true,
  enablePopovers: true,
  analytics: {
    provider: "plausible",
  },
  baseUrl: "quartz.jzhao.xyz",
  ignorePatterns: ["private", "templates"],
  theme: {
    typography: {
      header: "Schibsted Grotesk",
      body: "Source Sans Pro",
      code: "IBM Plex Mono",
    },
    colors: {
      lightMode: {
        light: "#faf8f8",
        lightgray: "#e5e5e5",
        gray: "#b8b8b8",
        darkgray: "#4e4e4e",
        dark: "#2b2b2b",
        secondary: "#284b63",
        tertiary: "#84a59d",
        highlight: "rgba(143, 159, 169, 0.15)",
      },
      darkMode: {
        light: "#161618",
        lightgray: "#393639",
        gray: "#646464",
        darkgray: "#d4d4d4",
        dark: "#ebebec",
        secondary: "#7b97aa",
        tertiary: "#84a59d",
        highlight: "rgba(143, 159, 169, 0.15)",
      },
    },
  },
}
const sharedPageComponents = {
  head: Component.Head(),
  header: [],
  footer: Component.Footer({
    links: {
      GitHub: "https://github.com/jackyzha0/quartz",
      "Discord Community": "https://discord.gg/cRFFHYye7t",
    },
  }),
}
const contentPageLayout: PageLayout = {
  beforeBody: [Component.ArticleTitle(), Component.ReadingTime(), Component.TagList()],
  left: [
    Component.PageTitle(),
    Component.MobileOnly(Component.Spacer()),
    Component.Search(),
    Component.Darkmode(),
    Component.DesktopOnly(Component.TableOfContents()),
  ],
  right: [Component.Graph(), Component.Backlinks()],
}
const listPageLayout: PageLayout = {
  beforeBody: [Component.ArticleTitle()],
  left: [
    Component.PageTitle(),
    Component.MobileOnly(Component.Spacer()),
    Component.Search(),
    Component.Darkmode(),
  ],
  right: [],
}
const config: QuartzConfig = {
  configuration: generalConfiguration,
  configuration: {
    pageTitle: "🪴 Quartz 4.0",
    enableSPA: true,
    enablePopovers: true,
    analytics: {
      provider: "plausible",
    },
    baseUrl: "quartz.jzhao.xyz",
    ignorePatterns: ["private", "templates"],
    theme: {
      typography: {
        header: "Schibsted Grotesk",
        body: "Source Sans Pro",
        code: "IBM Plex Mono",
      },
      colors: {
        lightMode: {
          light: "#faf8f8",
          lightgray: "#e5e5e5",
          gray: "#b8b8b8",
          darkgray: "#4e4e4e",
          dark: "#2b2b2b",
          secondary: "#284b63",
          tertiary: "#84a59d",
          highlight: "rgba(143, 159, 169, 0.15)",
        },
        darkMode: {
          light: "#161618",
          lightgray: "#393639",
          gray: "#646464",
          darkgray: "#d4d4d4",
          dark: "#ebebec",
          secondary: "#7b97aa",
          tertiary: "#84a59d",
          highlight: "rgba(143, 159, 169, 0.15)",
        },
      },
    },
  },
  plugins: {
    transformers: [
      Plugin.FrontMatter(),
@@ -96,21 +59,9 @@
    emitters: [
      Plugin.AliasRedirects(),
      Plugin.ComponentResources({ fontOrigin: "googleFonts" }),
      Plugin.ContentPage({
        ...sharedPageComponents,
        ...contentPageLayout,
        pageBody: Component.Content(),
      }),
      Plugin.FolderPage({
        ...sharedPageComponents,
        ...listPageLayout,
        pageBody: Component.FolderContent(),
      }),
      Plugin.TagPage({
        ...sharedPageComponents,
        ...listPageLayout,
        pageBody: Component.TagContent(),
      }),
      Plugin.ContentPage(),
      Plugin.FolderPage(),
      Plugin.TagPage(),
      Plugin.ContentIndex({
        enableSiteMap: true,
        enableRSS: true,
quartz.layout.ts
New file
@@ -0,0 +1,39 @@
import { PageLayout } from "./quartz/cfg"
import * as Component from "./quartz/components"
// components shared across all pages
export const sharedPageComponents = {
  head: Component.Head(),
  header: [],
  footer: Component.Footer({
    links: {
      GitHub: "https://github.com/jackyzha0/quartz",
      "Discord Community": "https://discord.gg/cRFFHYye7t",
    },
  }),
}
// components for pages that display a single page (e.g. a single note)
export const defaultContentPageLayout: PageLayout = {
  beforeBody: [Component.ArticleTitle(), Component.ReadingTime(), Component.TagList()],
  left: [
    Component.PageTitle(),
    Component.MobileOnly(Component.Spacer()),
    Component.Search(),
    Component.Darkmode(),
    Component.DesktopOnly(Component.TableOfContents()),
  ],
  right: [Component.Graph(), Component.Backlinks()],
}
// components for pages that display lists of pages  (e.g. tags or folders)
export const defaultListPageLayout: PageLayout = {
  beforeBody: [Component.ArticleTitle()],
  left: [
    Component.PageTitle(),
    Component.MobileOnly(Component.Spacer()),
    Component.Search(),
    Component.Darkmode(),
  ],
  right: [],
}
quartz/plugins/emitters/contentPage.tsx
@@ -5,22 +5,25 @@
import { pageResources, renderPage } from "../../components/renderPage"
import { FullPageLayout } from "../../cfg"
import { FilePath, canonicalizeServer } from "../../path"
import { defaultContentPageLayout, sharedPageComponents } from "../../../quartz.layout"
import { Content } from "../../components"
export const ContentPage: QuartzEmitterPlugin<FullPageLayout> = (opts) => {
  if (!opts) {
    throw new Error(
      "ContentPage must be initialized with options specifiying the components to use",
    )
export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts) => {
  const opts: FullPageLayout = {
    ...sharedPageComponents,
    ...defaultContentPageLayout,
    pageBody: Content(),
    ...userOpts,
  }
  const { head: Head, header, beforeBody, pageBody: Content, left, right, footer: Footer } = opts
  const { head: Head, header, beforeBody, pageBody, left, right, footer: Footer } = opts
  const Header = HeaderConstructor()
  const Body = BodyConstructor()
  return {
    name: "ContentPage",
    getQuartzComponents() {
      return [Head, Header, Body, ...header, ...beforeBody, Content, ...left, ...right, Footer]
      return [Head, Header, Body, ...header, ...beforeBody, pageBody, ...left, ...right, Footer]
    },
    async emit(ctx, content, resources, emit): Promise<FilePath[]> {
      const cfg = ctx.cfg.configuration
quartz/plugins/emitters/folderPage.tsx
@@ -7,20 +7,25 @@
import { FullPageLayout } from "../../cfg"
import path from "path"
import { CanonicalSlug, FilePath, ServerSlug, canonicalizeServer, joinSegments } from "../../path"
import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout"
import { FolderContent } from "../../components"
export const FolderPage: QuartzEmitterPlugin<FullPageLayout> = (opts) => {
  if (!opts) {
    throw new Error("ErrorPage must be initialized with options specifiying the components to use")
export const FolderPage: QuartzEmitterPlugin<FullPageLayout> = (userOpts) => {
  const opts: FullPageLayout = {
    ...sharedPageComponents,
    ...defaultListPageLayout,
    pageBody: FolderContent(),
    ...userOpts,
  }
  const { head: Head, header, beforeBody, pageBody: Content, left, right, footer: Footer } = opts
  const { head: Head, header, beforeBody, pageBody, left, right, footer: Footer } = opts
  const Header = HeaderConstructor()
  const Body = BodyConstructor()
  return {
    name: "FolderPage",
    getQuartzComponents() {
      return [Head, Header, Body, ...header, ...beforeBody, Content, ...left, ...right, Footer]
      return [Head, Header, Body, ...header, ...beforeBody, pageBody, ...left, ...right, Footer]
    },
    async emit(ctx, content, resources, emit): Promise<FilePath[]> {
      const fps: FilePath[] = []
quartz/plugins/emitters/tagPage.tsx
@@ -12,20 +12,25 @@
  getAllSegmentPrefixes,
  joinSegments,
} from "../../path"
import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout"
import { TagContent } from "../../components"
export const TagPage: QuartzEmitterPlugin<FullPageLayout> = (opts) => {
  if (!opts) {
    throw new Error("TagPage must be initialized with options specifiying the components to use")
export const TagPage: QuartzEmitterPlugin<FullPageLayout> = (userOpts) => {
  const opts: FullPageLayout = {
    ...sharedPageComponents,
    ...defaultListPageLayout,
    pageBody: TagContent(),
    ...userOpts,
  }
  const { head: Head, header, beforeBody, pageBody: Content, left, right, footer: Footer } = opts
  const { head: Head, header, beforeBody, pageBody, left, right, footer: Footer } = opts
  const Header = HeaderConstructor()
  const Body = BodyConstructor()
  return {
    name: "TagPage",
    getQuartzComponents() {
      return [Head, Header, Body, ...header, ...beforeBody, Content, ...left, ...right, Footer]
      return [Head, Header, Body, ...header, ...beforeBody, pageBody, ...left, ...right, Footer]
    },
    async emit(ctx, content, resources, emit): Promise<FilePath[]> {
      const fps: FilePath[] = []