diff --git a/quartz/components/pages/CanvasContent.tsx b/quartz/components/pages/CanvasContent.tsx
index 35b0ace6f..dea91b39d 100644
--- a/quartz/components/pages/CanvasContent.tsx
+++ b/quartz/components/pages/CanvasContent.tsx
@@ -1,12 +1,19 @@
import { ComponentChildren } from "preact"
import { htmlToJsx } from "../../util/jsx"
+import d3 from "d3"
import {
QuartzComponent,
QuartzComponentConstructor,
QuartzComponentProps,
QuartzCanvasComponent,
+ CanvasNode,
+ CanvasEdge,
+ CanvasTextNode,
+ CanvasFileNode,
+ CanvasLinkNode,
+ CanvasGroupNode,
} from "../types"
-import { type FilePath } from "../../util/path"
+import { type FilePath, slugifyFilePath } from "../../util/path"
import fs from "fs"
function loadCanvas(file: FilePath): QuartzCanvasComponent {
@@ -15,13 +22,84 @@ function loadCanvas(file: FilePath): QuartzCanvasComponent {
return JSON.parse(data) as QuartzCanvasComponent
}
+function renderTextNodes(nodes: CanvasTextNode[]): ComponentChildren {
+ return nodes.map((node) => (
+
+ {node["text"]}
+
+ ))
+}
+
+function renderFileNodes(nodes: CanvasFileNode[]): ComponentChildren {
+ return nodes.map((node) => (
+
+ ))
+}
+
+function renderLinkNodes(nodes: CanvasLinkNode[]): ComponentChildren {
+ return nodes.map((node) => (
+
+ ))
+}
+
const CanvasContent: QuartzComponent = ({ fileData, tree }: QuartzComponentProps) => {
//const content = htmlToJsx(fileData.filePath!, tree) as ComponentChildren
- const content = loadCanvas(fileData.filePath!)
+ const canvas = loadCanvas(fileData.filePath!)
+ const canvasNodes = (canvas["nodes"] ?? []) as CanvasNode[]
+ const canvasEdges = (canvas["edges"] ?? []) as CanvasEdge[]
const classes: string[] = fileData.frontmatter?.cssclasses ?? []
const classString = ["popover-hint", ...classes].join(" ")
+
+ // Canvas parts
+ const textNodes = (canvasNodes.filter((node) => node["type"] === "text") ??
+ []) as CanvasTextNode[]
+ const fileNodes = (canvasNodes.filter((node) => node["type"] === "file") ??
+ []) as CanvasFileNode[]
+ const linkNodes = (canvasNodes.filter((node) => node["type"] === "link") ??
+ []) as CanvasLinkNode[]
+ const groupNodes = (canvasNodes.filter((node) => node["type"] === "group") ??
+ []) as CanvasGroupNode[]
+
+ const result = (
+
+ {renderTextNodes(textNodes)}
+ {renderFileNodes(fileNodes)}
+ {renderLinkNodes(linkNodes)}
+
+ )
+
//TODO: Implement canvas rendering
- return {content["nodes"]}
+ return {result}
}
export default (() => CanvasContent) satisfies QuartzComponentConstructor
diff --git a/quartz/components/renderCanvas.tsx b/quartz/components/renderCanvas.tsx
index 3a04ba5df..0eb077705 100644
--- a/quartz/components/renderCanvas.tsx
+++ b/quartz/components/renderCanvas.tsx
@@ -91,7 +91,7 @@ export function renderCanvas(
console.log(root)
// process transcludes in componentData
- /*visit(root, "element", (node, _index, _parent) => {
+ visit(root, "element", (node, _index, _parent) => {
if (node.tagName === "blockquote") {
const classNames = (node.properties?.className ?? []) as string[]
if (classNames.includes("transclude")) {
@@ -204,7 +204,7 @@ export function renderCanvas(
}
}
}
- })*/
+ })
// set componentData.tree to the edited html that has transclusions rendered
componentData.tree = root
diff --git a/quartz/components/scripts/popover.inline.ts b/quartz/components/scripts/popover.inline.ts
index b01af0e85..f586931ff 100644
--- a/quartz/components/scripts/popover.inline.ts
+++ b/quartz/components/scripts/popover.inline.ts
@@ -100,10 +100,121 @@ async function mouseEnterHandler(
}
}
+async function navigationHandler(this: HTMLAnchorElement) {
+ const link = this
+ if (link.dataset.noPopover === "true") {
+ return
+ }
+
+ const canvasX = Number(link.getAttribute("data-x") ?? "0")
+ const canvasY = Number(link.getAttribute("data-y") ?? "0")
+
+ async function setPosition(popoverElement: HTMLElement) {
+ const { x, y } = await computePosition(link, popoverElement, {
+ middleware: [inline({ x: canvasX, y: canvasY }), shift(), flip()],
+ })
+ Object.assign(popoverElement.style, {
+ left: `${x}px`,
+ top: `${y}px`,
+ width: "100%",
+ height: "100%",
+ })
+ }
+
+ const hasAlreadyBeenFetched = () =>
+ [...link.children].some((child) => child.classList.contains("popover"))
+
+ // dont refetch if there's already a popover
+ if (hasAlreadyBeenFetched()) {
+ return setPosition(link.lastChild as HTMLElement)
+ }
+
+ const thisUrl = new URL(document.location.href)
+ thisUrl.hash = ""
+ thisUrl.search = ""
+ const targetUrl = new URL(link.href)
+ const hash = decodeURIComponent(targetUrl.hash)
+ targetUrl.hash = ""
+ targetUrl.search = ""
+
+ const response = await fetchCanonical(targetUrl).catch((err) => {
+ console.error(err)
+ })
+
+ // bailout if another popover exists
+ if (hasAlreadyBeenFetched()) {
+ return
+ }
+
+ if (!response) return
+ const [contentType] = response.headers.get("Content-Type")!.split(";")
+ const [contentTypeCategory, typeInfo] = contentType.split("/")
+
+ const popoverElement = document.createElement("div")
+ popoverElement.classList.add("popover")
+ const popoverInner = document.createElement("div")
+ popoverInner.classList.add("popover-inner")
+ popoverElement.appendChild(popoverInner)
+
+ popoverInner.dataset.contentType = contentType ?? undefined
+
+ switch (contentTypeCategory) {
+ case "image":
+ const img = document.createElement("img")
+ img.src = targetUrl.toString()
+ img.alt = targetUrl.pathname
+
+ popoverInner.appendChild(img)
+ break
+ case "application":
+ switch (typeInfo) {
+ case "pdf":
+ const pdf = document.createElement("iframe")
+ pdf.src = targetUrl.toString()
+ popoverInner.appendChild(pdf)
+ break
+ default:
+ break
+ }
+ break
+ default:
+ const contents = await response.text()
+ const html = p.parseFromString(contents, "text/html")
+ normalizeRelativeURLs(html, targetUrl)
+ const elts = [...html.getElementsByClassName("popover-hint")]
+ if (elts.length === 0) return
+
+ elts.forEach((elt) => popoverInner.appendChild(elt))
+ }
+
+ setPosition(popoverElement)
+ link.appendChild(popoverElement)
+
+ if (hash !== "") {
+ const heading = popoverInner.querySelector(hash) as HTMLElement | null
+ if (heading) {
+ // leave ~12px of buffer when scrolling to a heading
+ popoverInner.scroll({ top: heading.offsetTop - 12, behavior: "instant" })
+ }
+ }
+}
+
document.addEventListener("nav", () => {
const links = [...document.getElementsByClassName("internal")] as HTMLAnchorElement[]
- for (const link of links) {
+ console.log(links)
+ const pageLinks = links.filter((link) =>
+ link.classList.contains("internal-canvas"),
+ ) as HTMLAnchorElement[]
+ const canvasLinks = links.filter(
+ (link) => !link.classList.contains("internal-canvas"),
+ ) as HTMLAnchorElement[]
+ for (const link of pageLinks) {
link.addEventListener("mouseenter", mouseEnterHandler)
window.addCleanup(() => link.removeEventListener("mouseenter", mouseEnterHandler))
}
+ //TODO: Fix canvas loading of popovers
+ for (const link of canvasLinks) {
+ link.addEventListener("load", navigationHandler)
+ window.addCleanup(() => link.removeEventListener("load", navigationHandler))
+ }
})