diff --git a/docs/advanced/making plugins.md b/docs/advanced/making plugins.md
index 1f1616f42..d0934ad81 100644
--- a/docs/advanced/making plugins.md
+++ b/docs/advanced/making plugins.md
@@ -247,7 +247,7 @@ If you are creating an emitter plugin that needs to render components, there are
- Your component should use `getQuartzComponents` to declare a list of `QuartzComponents` that it uses to construct the page. See the page on [[creating components]] for more information.
- You can use the `renderPage` function defined in `quartz/components/renderPage.tsx` to render Quartz components into HTML.
-- If you need to render an HTML AST to JSX, you can use the `toJsxRuntime` function from `hast-util-to-jsx-runtime` library. An example of this can be found in `quartz/components/pages/Content.tsx`.
+- If you need to render an HTML AST to JSX, you can use the `htmlToJsx` function from `quartz/util/jsx.ts`. An example of this can be found in `quartz/components/pages/Content.tsx`.
For example, the following is a simplified version of the content page plugin that renders every single page.
diff --git a/quartz/components/TagList.tsx b/quartz/components/TagList.tsx
index c763b14ba..b39b19947 100644
--- a/quartz/components/TagList.tsx
+++ b/quartz/components/TagList.tsx
@@ -28,11 +28,16 @@ function TagList({ fileData, displayClass }: QuartzComponentProps) {
TagList.css = `
.tags {
list-style: none;
- display:flex;
- flex-wrap: wrap;
+ display: flex;
padding-left: 0;
gap: 0.4rem;
margin: 1rem 0;
+ flex-wrap: wrap;
+ justify-self: end;
+}
+
+.section-li > .section > .tags {
+ justify-content: flex-end;
}
.tags > li {
@@ -42,7 +47,7 @@ TagList.css = `
overflow-wrap: normal;
}
-a.tag-link {
+a.internal.tag-link {
border-radius: 8px;
background-color: var(--highlight);
padding: 0.2rem 0.4rem;
diff --git a/quartz/components/pages/Content.tsx b/quartz/components/pages/Content.tsx
index 7490a7ea7..76cecc38c 100644
--- a/quartz/components/pages/Content.tsx
+++ b/quartz/components/pages/Content.tsx
@@ -1,10 +1,8 @@
+import { htmlToJsx } from "../../util/jsx"
import { QuartzComponentConstructor, QuartzComponentProps } from "../types"
-import { Fragment, jsx, jsxs } from "preact/jsx-runtime"
-import { toJsxRuntime } from "hast-util-to-jsx-runtime"
-function Content({ tree }: QuartzComponentProps) {
- // @ts-ignore (preact makes it angry)
- const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" })
+function Content({ fileData, tree }: QuartzComponentProps) {
+ const content = htmlToJsx(fileData.filePath!, tree)
return {content}
}
diff --git a/quartz/components/pages/FolderContent.tsx b/quartz/components/pages/FolderContent.tsx
index a766d4b0b..765f84657 100644
--- a/quartz/components/pages/FolderContent.tsx
+++ b/quartz/components/pages/FolderContent.tsx
@@ -1,6 +1,4 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "../types"
-import { Fragment, jsx, jsxs } from "preact/jsx-runtime"
-import { toJsxRuntime } from "hast-util-to-jsx-runtime"
import path from "path"
import style from "../styles/listPage.scss"
@@ -8,6 +6,7 @@ import { PageList } from "../PageList"
import { _stripSlashes, simplifySlug } from "../../util/path"
import { Root } from "hast"
import { pluralize } from "../../util/lang"
+import { htmlToJsx } from "../../util/jsx"
function FolderContent(props: QuartzComponentProps) {
const { tree, fileData, allFiles } = props
@@ -29,8 +28,7 @@ function FolderContent(props: QuartzComponentProps) {
const content =
(tree as Root).children.length === 0
? fileData.description
- : // @ts-ignore
- toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" })
+ : htmlToJsx(fileData.filePath!, tree)
return (
diff --git a/quartz/components/pages/TagContent.tsx b/quartz/components/pages/TagContent.tsx
index 9907e3fc3..205ba8958 100644
--- a/quartz/components/pages/TagContent.tsx
+++ b/quartz/components/pages/TagContent.tsx
@@ -1,12 +1,11 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "../types"
-import { Fragment, jsx, jsxs } from "preact/jsx-runtime"
-import { toJsxRuntime } from "hast-util-to-jsx-runtime"
import style from "../styles/listPage.scss"
import { PageList } from "../PageList"
import { FullSlug, getAllSegmentPrefixes, simplifySlug } from "../../util/path"
import { QuartzPluginData } from "../../plugins/vfile"
import { Root } from "hast"
import { pluralize } from "../../util/lang"
+import { htmlToJsx } from "../../util/jsx"
const numPages = 10
function TagContent(props: QuartzComponentProps) {
@@ -26,8 +25,7 @@ function TagContent(props: QuartzComponentProps) {
const content =
(tree as Root).children.length === 0
? fileData.description
- : // @ts-ignore
- toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" })
+ : htmlToJsx(fileData.filePath!, tree)
if (tag === "") {
const tags = [...new Set(allFiles.flatMap((data) => data.frontmatter?.tags ?? []))]
diff --git a/quartz/components/scripts/search.inline.ts b/quartz/components/scripts/search.inline.ts
index a1c3e6ca2..eff4eb1b9 100644
--- a/quartz/components/scripts/search.inline.ts
+++ b/quartz/components/scripts/search.inline.ts
@@ -303,7 +303,6 @@ document.addEventListener("nav", async (e: unknown) => {
// setup index if it hasn't been already
if (!index) {
index = new Document({
- cache: true,
charset: "latin:extra",
optimize: true,
encode: encoder,
diff --git a/quartz/components/scripts/spa.inline.ts b/quartz/components/scripts/spa.inline.ts
index fc5aa7400..31ae14fcb 100644
--- a/quartz/components/scripts/spa.inline.ts
+++ b/quartz/components/scripts/spa.inline.ts
@@ -20,6 +20,7 @@ const isLocalUrl = (href: string) => {
const getOpts = ({ target }: Event): { url: URL; scroll?: boolean } | undefined => {
if (!isElement(target)) return
+ if (target.attributes.getNamedItem("target")?.value === "_blank") return
const a = target.closest("a")
if (!a) return
if ("routerIgnore" in a.dataset) return
diff --git a/quartz/components/styles/listPage.scss b/quartz/components/styles/listPage.scss
index 7105a1e86..c8fc9e957 100644
--- a/quartz/components/styles/listPage.scss
+++ b/quartz/components/styles/listPage.scss
@@ -19,11 +19,6 @@ li.section-li {
}
}
- & > .tags {
- justify-self: end;
- margin-left: 1rem;
- }
-
& > .desc > h3 > a {
background-color: transparent;
}
diff --git a/quartz/plugins/emitters/componentResources.ts b/quartz/plugins/emitters/componentResources.ts
index 5cb34804f..116e4e3e0 100644
--- a/quartz/plugins/emitters/componentResources.ts
+++ b/quartz/plugins/emitters/componentResources.ts
@@ -164,7 +164,7 @@ export const ComponentResources: QuartzEmitterPlugin
= (opts?: Partial<
addGlobalPageResources(ctx, resources, componentResources)
- const stylesheet = joinStyles(ctx.cfg.configuration.theme, styles, ...componentResources.css)
+ const stylesheet = joinStyles(ctx.cfg.configuration.theme, ...componentResources.css, styles)
const prescript = joinScripts(componentResources.beforeDOMLoaded)
const postscript = joinScripts(componentResources.afterDOMLoaded)
const fps = await Promise.all([
diff --git a/quartz/plugins/transformers/links.ts b/quartz/plugins/transformers/links.ts
index e050e00ad..8d16136f3 100644
--- a/quartz/plugins/transformers/links.ts
+++ b/quartz/plugins/transformers/links.ts
@@ -18,11 +18,13 @@ interface Options {
markdownLinkResolution: TransformOptions["strategy"]
/** Strips folders from a link so that it looks nice */
prettyLinks: boolean
+ openLinksInNewTab: boolean
}
const defaultOptions: Options = {
markdownLinkResolution: "absolute",
prettyLinks: true,
+ openLinksInNewTab: false,
}
export const CrawlLinks: QuartzTransformerPlugin | undefined> = (userOpts) => {
@@ -52,6 +54,10 @@ export const CrawlLinks: QuartzTransformerPlugin | undefined> =
node.properties.className ??= []
node.properties.className.push(isAbsoluteUrl(dest) ? "external" : "internal")
+ if (opts.openLinksInNewTab) {
+ node.properties.target = "_blank"
+ }
+
// don't process external links or intra-document anchors
const isInternal = !(isAbsoluteUrl(dest) || dest.startsWith("#"))
if (isInternal) {
diff --git a/quartz/util/jsx.ts b/quartz/util/jsx.ts
new file mode 100644
index 000000000..8cba485ab
--- /dev/null
+++ b/quartz/util/jsx.ts
@@ -0,0 +1,15 @@
+import { toJsxRuntime } from "hast-util-to-jsx-runtime"
+import { QuartzPluginData } from "../plugins/vfile"
+import { Node, Root } from "hast"
+import { Fragment, jsx, jsxs } from "preact/jsx-runtime"
+import { trace } from "./trace"
+import { type FilePath } from "./path"
+
+export function htmlToJsx(fp: FilePath, tree: Node) {
+ try {
+ // @ts-ignore (preact makes it angry)
+ return toJsxRuntime(tree as Root, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" })
+ } catch (e) {
+ trace(`Failed to parse Markdown in \`${fp}\` into JSX`, e as Error)
+ }
+}
diff --git a/quartz/util/trace.ts b/quartz/util/trace.ts
index c7f3cc339..a33135d64 100644
--- a/quartz/util/trace.ts
+++ b/quartz/util/trace.ts
@@ -4,7 +4,7 @@ import { isMainThread } from "workerpool"
const rootFile = /.*at file:/
export function trace(msg: string, err: Error) {
- const stack = err.stack
+ let stack = err.stack ?? ""
const lines: string[] = []
@@ -12,15 +12,11 @@ export function trace(msg: string, err: Error) {
lines.push(
"\n" +
chalk.bgRed.black.bold(" ERROR ") +
- "\n" +
+ "\n\n" +
chalk.red(` ${msg}`) +
(err.message.length > 0 ? `: ${err.message}` : ""),
)
- if (!stack) {
- return
- }
-
let reachedEndOfLegibleTrace = false
for (const line of stack.split("\n").slice(1)) {
if (reachedEndOfLegibleTrace) {