diff --git a/quartz.config.ts b/quartz.config.ts
index 25518d5f..01412f91 100644
--- a/quartz.config.ts
+++ b/quartz.config.ts
@@ -1,4 +1,5 @@
import { QuartzConfig } from "./quartz/cfg"
+import Body from "./quartz/components/Body"
import Head from "./quartz/components/Head"
import Header from "./quartz/components/Header"
import {
@@ -68,7 +69,8 @@ const config: QuartzConfig = {
emitters: [
new ContentPage({
Head: Head,
- Header: Header
+ Header: Header,
+ Body: Body
})
]
},
diff --git a/quartz/components/Body.tsx b/quartz/components/Body.tsx
new file mode 100644
index 00000000..1d9296b5
--- /dev/null
+++ b/quartz/components/Body.tsx
@@ -0,0 +1,18 @@
+import { ComponentChildren } from "preact"
+import clipboardScript from './scripts/clipboard.inline'
+import clipboardStyle from './styles/clipboard.scss'
+
+export interface BodyProps {
+ title?: string
+ children: ComponentChildren
+}
+
+export default function Body({ title, children }: BodyProps) {
+ return
+ {title && {title}
}
+ {children}
+
+}
+
+Body.afterDOMLoaded = clipboardScript
+Body.css = clipboardStyle
diff --git a/quartz/components/Darkmode.tsx b/quartz/components/Darkmode.tsx
index ae6788cb..2170253c 100644
--- a/quartz/components/Darkmode.tsx
+++ b/quartz/components/Darkmode.tsx
@@ -2,7 +2,7 @@
// modules are automatically deferred and we don't want that to happen for critical beforeDOMLoads
// see: https://v8.dev/features/modules#defer
import darkmodeScript from "./scripts/darkmode.inline"
-import styles from '../styles/darkmode.scss'
+import styles from './styles/darkmode.scss'
export default function Darkmode() {
return
diff --git a/quartz/components/Header.tsx b/quartz/components/Header.tsx
index cb2fae8d..7005e64d 100644
--- a/quartz/components/Header.tsx
+++ b/quartz/components/Header.tsx
@@ -1,6 +1,6 @@
import { resolveToRoot } from "../path"
import Darkmode from "./Darkmode"
-import style from '../styles/header.scss'
+import style from './styles/header.scss'
export interface HeaderProps {
title: string
diff --git a/quartz/components/scripts/clipboard.inline.ts b/quartz/components/scripts/clipboard.inline.ts
new file mode 100644
index 00000000..efddfa6f
--- /dev/null
+++ b/quartz/components/scripts/clipboard.inline.ts
@@ -0,0 +1,32 @@
+const description = "Initialize copy for codeblocks"
+export default description
+
+const svgCopy =
+ '
'
+const svgCheck =
+ '
'
+
+const els = document.getElementsByTagName("pre")
+for (let i = 0; i < els.length; i++) {
+ const codeBlock = els[i].getElementsByTagName("code")[0]
+ const source = codeBlock.innerText.replace(/\n\n/g, "\n")
+ const button = document.createElement("button")
+ button.className = "clipboard-button"
+ button.type = "button"
+ button.innerHTML = svgCopy
+ button.ariaLabel = "Copy source"
+ button.addEventListener("click", () => {
+ navigator.clipboard.writeText(source).then(
+ () => {
+ button.blur()
+ button.innerHTML = svgCheck
+ setTimeout(() => {
+ button.innerHTML = svgCopy
+ button.style.borderColor = ""
+ }, 2000)
+ },
+ (error) => console.error(error),
+ )
+ })
+ els[i].prepend(button)
+}
diff --git a/quartz/components/styles/clipboard.scss b/quartz/components/styles/clipboard.scss
new file mode 100644
index 00000000..1702a7bb
--- /dev/null
+++ b/quartz/components/styles/clipboard.scss
@@ -0,0 +1,37 @@
+.clipboard-button {
+ position: absolute;
+ display: flex;
+ float: right;
+ right: 0;
+ padding: 0.4rem;
+ margin: -0.2rem 0.3rem;
+ color: var(--gray);
+ border-color: var(--dark);
+ background-color: var(--light);
+ border: 1px solid;
+ border-radius: 5px;
+ z-index: 1;
+ opacity: 0;
+ transition: 0.2s;
+
+ & > svg {
+ fill: var(--light);
+ filter: contrast(0.3);
+ }
+
+ &:hover {
+ cursor: pointer;
+ border-color: var(--secondary);
+ }
+
+ &:focus {
+ outline: 0;
+ }
+}
+
+pre {
+ &:hover > .clipboard-button {
+ opacity: 1;
+ transition: 0.2s;
+ }
+}
diff --git a/quartz/styles/darkmode.scss b/quartz/components/styles/darkmode.scss
similarity index 100%
rename from quartz/styles/darkmode.scss
rename to quartz/components/styles/darkmode.scss
diff --git a/quartz/styles/header.scss b/quartz/components/styles/header.scss
similarity index 100%
rename from quartz/styles/header.scss
rename to quartz/components/styles/header.scss
diff --git a/quartz/plugins/emitters/contentPage.tsx b/quartz/plugins/emitters/contentPage.tsx
index 61ddcfbd..c4b357c1 100644
--- a/quartz/plugins/emitters/contentPage.tsx
+++ b/quartz/plugins/emitters/contentPage.tsx
@@ -9,10 +9,12 @@ import { GlobalConfiguration } from "../../cfg"
import { HeaderProps } from "../../components/Header"
import { QuartzComponent } from "../../components/types"
import { resolveToRoot } from "../../path"
+import { BodyProps } from "../../components/Body"
interface Options {
Head: QuartzComponent
Header: QuartzComponent
+ Body: QuartzComponent
}
export class ContentPage extends QuartzEmitterPlugin {
@@ -31,7 +33,7 @@ export class ContentPage extends QuartzEmitterPlugin {
async emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emit: EmitCallback): Promise {
const fps: string[] = []
- const { Head, Header } = this.opts
+ const { Head, Header, Body } = this.opts
for (const [tree, file] of content) {
// @ts-ignore (preact makes it angry)
const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
@@ -42,7 +44,7 @@ export class ContentPage extends QuartzEmitterPlugin {
js: [
{ src: baseDir + "/prescript.js", loadTime: "beforeDOMReady" },
...resources.js,
- { src: baseDir + "/postscript.js", loadTime: "afterDOMReady" }
+ { src: baseDir + "/postscript.js", loadTime: "afterDOMReady", type: 'module' }
]
}
@@ -56,10 +58,7 @@ export class ContentPage extends QuartzEmitterPlugin {
-
- {file.data.slug !== "index" && {title}
}
- {content}
-
+ {content}
{pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(resource => )}
diff --git a/quartz/plugins/index.ts b/quartz/plugins/index.ts
index 30f77e8e..e904a00e 100644
--- a/quartz/plugins/index.ts
+++ b/quartz/plugins/index.ts
@@ -40,7 +40,7 @@ export function emitComponentResources(cfg: GlobalConfiguration, resources: Stat
componentResources.beforeDOMLoaded.push(beforeDOMLoaded)
}
if (afterDOMLoaded) {
- componentResources.beforeDOMLoaded.push(afterDOMLoaded)
+ componentResources.afterDOMLoaded.push(afterDOMLoaded)
}
}