various polish
This commit is contained in:
parent
4c904d88ab
commit
e0ebee5aa9
@ -4,12 +4,7 @@ import * as Plugin from "./quartz/plugins"
|
|||||||
|
|
||||||
const sharedPageComponents = {
|
const sharedPageComponents = {
|
||||||
head: Component.Head(),
|
head: Component.Head(),
|
||||||
header: [
|
header: [],
|
||||||
Component.PageTitle(),
|
|
||||||
Component.Spacer(),
|
|
||||||
Component.Search(),
|
|
||||||
Component.Darkmode()
|
|
||||||
],
|
|
||||||
footer: Component.Footer({
|
footer: Component.Footer({
|
||||||
authorName: "Jacky",
|
authorName: "Jacky",
|
||||||
links: {
|
links: {
|
||||||
@ -25,11 +20,15 @@ const contentPageLayout: PageLayout = {
|
|||||||
Component.ReadingTime(),
|
Component.ReadingTime(),
|
||||||
Component.TagList(),
|
Component.TagList(),
|
||||||
],
|
],
|
||||||
left: [],
|
left: [
|
||||||
|
Component.PageTitle(),
|
||||||
|
Component.Search(),
|
||||||
|
Component.TableOfContents(),
|
||||||
|
Component.Darkmode()
|
||||||
|
],
|
||||||
right: [
|
right: [
|
||||||
Component.Graph(),
|
Component.Graph(),
|
||||||
Component.TableOfContents(),
|
Component.Backlinks(),
|
||||||
Component.Backlinks()
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +36,11 @@ const listPageLayout: PageLayout = {
|
|||||||
beforeBody: [
|
beforeBody: [
|
||||||
Component.ArticleTitle()
|
Component.ArticleTitle()
|
||||||
],
|
],
|
||||||
left: [],
|
left: [
|
||||||
|
Component.PageTitle(),
|
||||||
|
Component.Search(),
|
||||||
|
Component.Darkmode()
|
||||||
|
],
|
||||||
right: [],
|
right: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,6 +49,9 @@ const config: QuartzConfig = {
|
|||||||
pageTitle: "🪴 Quartz 4.0",
|
pageTitle: "🪴 Quartz 4.0",
|
||||||
enableSPA: true,
|
enableSPA: true,
|
||||||
enablePopovers: true,
|
enablePopovers: true,
|
||||||
|
analytics: {
|
||||||
|
provider: 'plausible',
|
||||||
|
},
|
||||||
canonicalUrl: "quartz.jzhao.xyz",
|
canonicalUrl: "quartz.jzhao.xyz",
|
||||||
ignorePatterns: ["private", "templates"],
|
ignorePatterns: ["private", "templates"],
|
||||||
theme: {
|
theme: {
|
||||||
@ -102,16 +108,16 @@ const config: QuartzConfig = {
|
|||||||
...contentPageLayout,
|
...contentPageLayout,
|
||||||
pageBody: Component.Content(),
|
pageBody: Component.Content(),
|
||||||
}),
|
}),
|
||||||
Plugin.TagPage({
|
|
||||||
...sharedPageComponents,
|
|
||||||
...listPageLayout,
|
|
||||||
pageBody: Component.TagContent(),
|
|
||||||
}),
|
|
||||||
Plugin.FolderPage({
|
Plugin.FolderPage({
|
||||||
...sharedPageComponents,
|
...sharedPageComponents,
|
||||||
...listPageLayout,
|
...listPageLayout,
|
||||||
pageBody: Component.FolderContent(),
|
pageBody: Component.FolderContent(),
|
||||||
}),
|
}),
|
||||||
|
Plugin.TagPage({
|
||||||
|
...sharedPageComponents,
|
||||||
|
...listPageLayout,
|
||||||
|
pageBody: Component.TagContent(),
|
||||||
|
}),
|
||||||
Plugin.ContentIndex({
|
Plugin.ContentIndex({
|
||||||
enableSiteMap: true,
|
enableSiteMap: true,
|
||||||
enableRSS: true,
|
enableRSS: true,
|
||||||
|
@ -64,7 +64,7 @@ yargs(hideBin(process.argv))
|
|||||||
packages: "external",
|
packages: "external",
|
||||||
plugins: [
|
plugins: [
|
||||||
sassPlugin({
|
sassPlugin({
|
||||||
type: 'css-text'
|
type: 'css-text',
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: 'inline-script-loader',
|
name: 'inline-script-loader',
|
||||||
|
@ -2,12 +2,23 @@ import { QuartzComponent } from "./components/types"
|
|||||||
import { PluginTypes } from "./plugins/types"
|
import { PluginTypes } from "./plugins/types"
|
||||||
import { Theme } from "./theme"
|
import { Theme } from "./theme"
|
||||||
|
|
||||||
|
export type Analytics = null
|
||||||
|
| {
|
||||||
|
provider: 'plausible'
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
provider: 'google',
|
||||||
|
tagId: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface GlobalConfiguration {
|
export interface GlobalConfiguration {
|
||||||
pageTitle: string,
|
pageTitle: string,
|
||||||
/** Whether to enable single-page-app style rendering. this prevents flashes of unstyled content and improves smoothness of Quartz */
|
/** Whether to enable single-page-app style rendering. this prevents flashes of unstyled content and improves smoothness of Quartz */
|
||||||
enableSPA: boolean,
|
enableSPA: boolean,
|
||||||
/** Whether to display Wikipedia-style popovers when hovering over links */
|
/** Whether to display Wikipedia-style popovers when hovering over links */
|
||||||
enablePopovers: boolean,
|
enablePopovers: boolean,
|
||||||
|
/** Analytics mode */
|
||||||
|
analytics: Analytics
|
||||||
/** Glob patterns to not search */
|
/** Glob patterns to not search */
|
||||||
ignorePatterns: string[],
|
ignorePatterns: string[],
|
||||||
/** Base URL to use for CNAME files, sitemaps, and RSS feeds that require an absolute URL.
|
/** Base URL to use for CNAME files, sitemaps, and RSS feeds that require an absolute URL.
|
||||||
|
@ -2,9 +2,8 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
|||||||
|
|
||||||
function ArticleTitle({ fileData }: QuartzComponentProps) {
|
function ArticleTitle({ fileData }: QuartzComponentProps) {
|
||||||
const title = fileData.frontmatter?.title
|
const title = fileData.frontmatter?.title
|
||||||
const displayTitle = fileData.slug === "index" ? undefined : title
|
if (title) {
|
||||||
if (displayTitle) {
|
return <h1 class="article-title">{title}</h1>
|
||||||
return <h1 class="article-title">{displayTitle}</h1>
|
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ export default ((opts?: Options) => {
|
|||||||
return <>
|
return <>
|
||||||
<hr />
|
<hr />
|
||||||
<footer>
|
<footer>
|
||||||
<p>Made by {name} using <a>Quartz</a>, © {year}</p>
|
<p>Made by {name} using <a href="https://quartz.jzhao.xyz/">Quartz</a>, © {year}</p>
|
||||||
<ul>{Object.entries(links).map(([text, link]) => <li>
|
<ul>{Object.entries(links).map(([text, link]) => <li>
|
||||||
<a href={link}>{text}</a>
|
<a href={link}>{text}</a>
|
||||||
</li>)}</ul>
|
</li>)}</ul>
|
||||||
|
@ -2,15 +2,7 @@ import { resolveToRoot } from "../path"
|
|||||||
import { JSResourceToScriptElement } from "../resources"
|
import { JSResourceToScriptElement } from "../resources"
|
||||||
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
|
|
||||||
interface Options {
|
export default (() => {
|
||||||
prefetchContentIndex: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultOptions: Options = {
|
|
||||||
prefetchContentIndex: true
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ((opts?: Options) => {
|
|
||||||
function Head({ fileData, externalResources }: QuartzComponentProps) {
|
function Head({ fileData, externalResources }: QuartzComponentProps) {
|
||||||
const slug = fileData.slug!
|
const slug = fileData.slug!
|
||||||
const title = fileData.frontmatter?.title ?? "Untitled"
|
const title = fileData.frontmatter?.title ?? "Untitled"
|
||||||
@ -20,10 +12,6 @@ export default ((opts?: Options) => {
|
|||||||
const iconPath = baseDir + "/static/icon.png"
|
const iconPath = baseDir + "/static/icon.png"
|
||||||
const ogImagePath = baseDir + "/static/og-image.png"
|
const ogImagePath = baseDir + "/static/og-image.png"
|
||||||
|
|
||||||
const prefetchContentIndex = opts?.prefetchContentIndex ?? defaultOptions.prefetchContentIndex
|
|
||||||
const contentIndexPath = baseDir + "/static/contentIndex.json"
|
|
||||||
const contentIndexScript = `const fetchData = fetch(\`${contentIndexPath}\`).then(data => data.json())`
|
|
||||||
|
|
||||||
return <head>
|
return <head>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<meta charSet="utf-8" />
|
<meta charSet="utf-8" />
|
||||||
@ -38,7 +26,6 @@ export default ((opts?: Options) => {
|
|||||||
<meta name="generator" content="Quartz" />
|
<meta name="generator" content="Quartz" />
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com"/>
|
<link rel="preconnect" href="https://fonts.googleapis.com"/>
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com"/>
|
<link rel="preconnect" href="https://fonts.gstatic.com"/>
|
||||||
{prefetchContentIndex && <script spa-preserve>{contentIndexScript}</script>}
|
|
||||||
{css.map(href => <link key={href} href={href} rel="stylesheet" type="text/css" spa-preserve />)}
|
{css.map(href => <link key={href} href={href} rel="stylesheet" type="text/css" spa-preserve />)}
|
||||||
{js.filter(resource => resource.loadTime === "beforeDOMReady").map(res => JSResourceToScriptElement(res, true))}
|
{js.filter(resource => resource.loadTime === "beforeDOMReady").map(res => JSResourceToScriptElement(res, true))}
|
||||||
</head>
|
</head>
|
||||||
|
@ -12,6 +12,7 @@ header {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 2em 0;
|
margin: 2em 0;
|
||||||
|
gap: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
header h1 {
|
header h1 {
|
||||||
|
@ -23,7 +23,7 @@ function byDateAndAlphabetical(f1: QuartzPluginData, f2: QuartzPluginData): numb
|
|||||||
|
|
||||||
export function PageList({ fileData, allFiles }: QuartzComponentProps) {
|
export function PageList({ fileData, allFiles }: QuartzComponentProps) {
|
||||||
const slug = fileData.slug!
|
const slug = fileData.slug!
|
||||||
return <ul class="section-ul">
|
return <ul class="section-ul popover-hint">
|
||||||
{allFiles.sort(byDateAndAlphabetical).map(page => {
|
{allFiles.sort(byDateAndAlphabetical).map(page => {
|
||||||
const title = page.frontmatter?.title
|
const title = page.frontmatter?.title
|
||||||
const pageSlug = page.slug!
|
const pageSlug = page.slug!
|
||||||
@ -36,9 +36,8 @@ export function PageList({ fileData, allFiles }: QuartzComponentProps) {
|
|||||||
<div class="desc">
|
<div class="desc">
|
||||||
<h3><a href={stripIndex(relativeToRoot(slug, pageSlug))} class="internal">{title}</a></h3>
|
<h3><a href={stripIndex(relativeToRoot(slug, pageSlug))} class="internal">{title}</a></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="spacer"></div>
|
|
||||||
<ul class="tags">
|
<ul class="tags">
|
||||||
{tags.map(tag => <li><a href={relativeToRoot(slug, `tags/${tag}`)}>#{tag}</a></li>)}
|
{tags.map(tag => <li><a class="internal" href={relativeToRoot(slug, `tags/${tag}`)}>#{tag}</a></li>)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -11,7 +11,7 @@ function TagList({ fileData }: QuartzComponentProps) {
|
|||||||
const display = `#${tag}`
|
const display = `#${tag}`
|
||||||
const linkDest = baseDir + `/tags/${slugAnchor(tag)}`
|
const linkDest = baseDir + `/tags/${slugAnchor(tag)}`
|
||||||
return <li>
|
return <li>
|
||||||
<a href={linkDest}>{display}</a>
|
<a href={linkDest} class="internal">{display}</a>
|
||||||
</li>
|
</li>
|
||||||
})}</ul>
|
})}</ul>
|
||||||
} else {
|
} else {
|
||||||
@ -25,18 +25,19 @@ TagList.css = `
|
|||||||
display: flex;
|
display: flex;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
gap: 0.4rem;
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
& > li {
|
.tags > li {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
overflow-wrap: normal;
|
||||||
|
}
|
||||||
|
|
||||||
& > a {
|
.tags > li > a {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: var(--lightgray) 1px solid;
|
background-color: var(--highlight);
|
||||||
padding: 0.2rem 0.5rem;
|
padding: 0.2rem 0.5rem;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
`
|
||||||
|
|
||||||
export default (() => TagList) satisfies QuartzComponentConstructor
|
export default (() => TagList) satisfies QuartzComponentConstructor
|
||||||
|
@ -5,7 +5,7 @@ import { toJsxRuntime } from "hast-util-to-jsx-runtime"
|
|||||||
function Content({ tree }: QuartzComponentProps) {
|
function Content({ tree }: QuartzComponentProps) {
|
||||||
// @ts-ignore (preact makes it angry)
|
// @ts-ignore (preact makes it angry)
|
||||||
const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
|
const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
|
||||||
return <article>{content}</article>
|
return <article class="popover-hint">{content}</article>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (() => Content) satisfies QuartzComponentConstructor
|
export default (() => Content) satisfies QuartzComponentConstructor
|
@ -17,10 +17,15 @@ interface RenderComponents {
|
|||||||
|
|
||||||
export function pageResources(slug: string, staticResources: StaticResources): StaticResources {
|
export function pageResources(slug: string, staticResources: StaticResources): StaticResources {
|
||||||
const baseDir = resolveToRoot(slug)
|
const baseDir = resolveToRoot(slug)
|
||||||
|
|
||||||
|
const contentIndexPath = baseDir + "/static/contentIndex.json"
|
||||||
|
const contentIndexScript = `const fetchData = fetch(\`${contentIndexPath}\`).then(data => data.json())`
|
||||||
|
|
||||||
return {
|
return {
|
||||||
css: [baseDir + "/index.css", ...staticResources.css],
|
css: [baseDir + "/index.css", ...staticResources.css],
|
||||||
js: [
|
js: [
|
||||||
{ src: baseDir + "/prescript.js", loadTime: "beforeDOMReady", contentType: "external" },
|
{ src: baseDir + "/prescript.js", loadTime: "beforeDOMReady", contentType: "external" },
|
||||||
|
{ loadTime: "afterDOMReady", contentType: "inline", spaPreserve: true, script: contentIndexScript },
|
||||||
...staticResources.js,
|
...staticResources.js,
|
||||||
{ src: baseDir + "/postscript.js", loadTime: "afterDOMReady", moduleType: 'module', contentType: "external" }
|
{ src: baseDir + "/postscript.js", loadTime: "afterDOMReady", moduleType: 'module', contentType: "external" }
|
||||||
]
|
]
|
||||||
@ -32,29 +37,41 @@ export function renderPage(slug: string, componentData: QuartzComponentProps, co
|
|||||||
const Header = HeaderConstructor()
|
const Header = HeaderConstructor()
|
||||||
const Body = BodyConstructor()
|
const Body = BodyConstructor()
|
||||||
|
|
||||||
|
const LeftComponent =
|
||||||
|
<div class="left">
|
||||||
|
<div class="left-inner">
|
||||||
|
{left.map(BodyComponent => <BodyComponent {...componentData} />)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
const RightComponent =
|
||||||
|
<div class="right">
|
||||||
|
<div class="right-inner">
|
||||||
|
{right.map(BodyComponent => <BodyComponent {...componentData} />)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
const doc = <html>
|
const doc = <html>
|
||||||
<Head {...componentData} />
|
<Head {...componentData} />
|
||||||
<body data-slug={slug}>
|
<body data-slug={slug}>
|
||||||
<div id="quartz-root" class="page">
|
<div id="quartz-root" class="page">
|
||||||
|
<div class="page-header">
|
||||||
<Header {...componentData} >
|
<Header {...componentData} >
|
||||||
{header.map(HeaderComponent => <HeaderComponent {...componentData} />)}
|
{header.map(HeaderComponent => <HeaderComponent {...componentData} />)}
|
||||||
</Header>
|
</Header>
|
||||||
<div class="popover-hint">
|
<div class="popover-hint">
|
||||||
{beforeBody.map(BodyComponent => <BodyComponent {...componentData} />)}
|
{beforeBody.map(BodyComponent => <BodyComponent {...componentData} />)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<Body {...componentData}>
|
<Body {...componentData}>
|
||||||
<div class="left">
|
{LeftComponent}
|
||||||
{left.map(BodyComponent => <BodyComponent {...componentData} />)}
|
<div class="center">
|
||||||
</div>
|
|
||||||
<div class="center popover-hint">
|
|
||||||
<Content {...componentData} />
|
<Content {...componentData} />
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
{right.map(BodyComponent => <BodyComponent {...componentData} />)}
|
|
||||||
</div>
|
|
||||||
</Body>
|
|
||||||
<Footer {...componentData} />
|
<Footer {...componentData} />
|
||||||
</div>
|
</div>
|
||||||
|
{RightComponent}
|
||||||
|
</Body>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
{pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(res => JSResourceToScriptElement(res))}
|
{pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(res => JSResourceToScriptElement(res))}
|
||||||
</html>
|
</html>
|
||||||
|
@ -2,7 +2,7 @@ const userPref = window.matchMedia('(prefers-color-scheme: light)').matches ? 'l
|
|||||||
const currentTheme = localStorage.getItem('theme') ?? userPref
|
const currentTheme = localStorage.getItem('theme') ?? userPref
|
||||||
document.documentElement.setAttribute('saved-theme', currentTheme)
|
document.documentElement.setAttribute('saved-theme', currentTheme)
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener("nav", () => {
|
||||||
const switchTheme = (e: any) => {
|
const switchTheme = (e: any) => {
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
document.documentElement.setAttribute('saved-theme', 'dark')
|
document.documentElement.setAttribute('saved-theme', 'dark')
|
||||||
@ -16,7 +16,8 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
// Darkmode toggle
|
// Darkmode toggle
|
||||||
const toggleSwitch = document.querySelector('#darkmode-toggle') as HTMLInputElement
|
const toggleSwitch = document.querySelector('#darkmode-toggle') as HTMLInputElement
|
||||||
toggleSwitch.addEventListener('change', switchTheme, false)
|
toggleSwitch.removeEventListener('change', switchTheme)
|
||||||
|
toggleSwitch.addEventListener('change', switchTheme)
|
||||||
if (currentTheme === 'dark') {
|
if (currentTheme === 'dark') {
|
||||||
toggleSwitch.checked = true
|
toggleSwitch.checked = true
|
||||||
}
|
}
|
||||||
|
@ -266,9 +266,9 @@ async function renderGraph(container: string, slug: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderGlobalGraph() {
|
async function renderGlobalGraph() {
|
||||||
const slug = document.body.dataset["slug"]!
|
const slug = document.body.dataset["slug"]!
|
||||||
renderGraph("global-graph-container", slug)
|
await renderGraph("global-graph-container", slug)
|
||||||
const container = document.getElementById("global-graph-outer")
|
const container = document.getElementById("global-graph-outer")
|
||||||
container?.classList.add("active")
|
container?.classList.add("active")
|
||||||
|
|
||||||
@ -293,7 +293,14 @@ document.addEventListener("nav", async (e: unknown) => {
|
|||||||
containerIcon?.addEventListener("click", renderGlobalGraph)
|
containerIcon?.addEventListener("click", renderGlobalGraph)
|
||||||
})
|
})
|
||||||
|
|
||||||
window.addEventListener('resize', async () => {
|
let resizeEventDebounce: number | undefined = undefined
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
if (resizeEventDebounce) {
|
||||||
|
clearTimeout(resizeEventDebounce)
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeEventDebounce = window.setTimeout(async () => {
|
||||||
const slug = document.body.dataset["slug"]!
|
const slug = document.body.dataset["slug"]!
|
||||||
await renderGraph("graph-container", slug)
|
await renderGraph("graph-container", slug)
|
||||||
|
}, 50)
|
||||||
})
|
})
|
||||||
|
3
quartz/components/scripts/plausible.inline.ts
Normal file
3
quartz/components/scripts/plausible.inline.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import Plausible from 'plausible-tracker'
|
||||||
|
const { trackPageview } = Plausible()
|
||||||
|
document.addEventListener("nav", () => trackPageview())
|
@ -1,5 +1,24 @@
|
|||||||
import { computePosition, flip, inline, shift } from "@floating-ui/dom"
|
import { computePosition, flip, inline, shift } from "@floating-ui/dom"
|
||||||
|
|
||||||
|
// from micromorph/src/utils.ts
|
||||||
|
// https://github.com/natemoo-re/micromorph/blob/main/src/utils.ts#L5
|
||||||
|
export function normalizeRelativeURLs(
|
||||||
|
el: Element | Document,
|
||||||
|
base: string | URL
|
||||||
|
) {
|
||||||
|
const update = (el: Element, attr: string, base: string | URL) => {
|
||||||
|
el.setAttribute(attr, new URL(el.getAttribute(attr)!, base).pathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
el.querySelectorAll('[href^="./"], [href^="../"]').forEach((item) =>
|
||||||
|
update(item, 'href', base)
|
||||||
|
)
|
||||||
|
|
||||||
|
el.querySelectorAll('[src^="./"], [src^="../"]').forEach((item) =>
|
||||||
|
update(item, 'src', base)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener("nav", () => {
|
document.addEventListener("nav", () => {
|
||||||
const links = [...document.getElementsByClassName("internal")] as HTMLLinkElement[]
|
const links = [...document.getElementsByClassName("internal")] as HTMLLinkElement[]
|
||||||
const p = new DOMParser()
|
const p = new DOMParser()
|
||||||
@ -41,6 +60,7 @@ document.addEventListener("nav", () => {
|
|||||||
|
|
||||||
if (!contents) return
|
if (!contents) return
|
||||||
const html = p.parseFromString(contents, "text/html")
|
const html = p.parseFromString(contents, "text/html")
|
||||||
|
normalizeRelativeURLs(html, targetUrl)
|
||||||
const elts = [...html.getElementsByClassName("popover-hint")]
|
const elts = [...html.getElementsByClassName("popover-hint")]
|
||||||
if (elts.length === 0) return
|
if (elts.length === 0) return
|
||||||
|
|
||||||
@ -55,11 +75,13 @@ document.addEventListener("nav", () => {
|
|||||||
link.appendChild(popoverElement)
|
link.appendChild(popoverElement)
|
||||||
link.dataset.fetchedPopover = "true"
|
link.dataset.fetchedPopover = "true"
|
||||||
|
|
||||||
|
if (hash !== "") {
|
||||||
const heading = popoverInner.querySelector(hash) as HTMLElement | null
|
const heading = popoverInner.querySelector(hash) as HTMLElement | null
|
||||||
if (heading) {
|
if (heading) {
|
||||||
// leave ~12px of buffer when scrolling to a heading
|
// leave ~12px of buffer when scrolling to a heading
|
||||||
popoverInner.scroll({ top: heading.offsetTop - 12, behavior: 'instant' })
|
popoverInner.scroll({ top: heading.offsetTop - 12, behavior: 'instant' })
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -7,13 +7,9 @@
|
|||||||
& > ul {
|
& > ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0.5rem 0;
|
||||||
|
|
||||||
& > li {
|
& > li {
|
||||||
margin: 0.5rem 0;
|
|
||||||
padding: 0.25rem 1rem;
|
|
||||||
border: var(--lightgray) 1px solid;
|
|
||||||
border-radius: 5px;
|
|
||||||
& > a {
|
& > a {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
footer {
|
footer {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
|
margin-bottom: 4rem;
|
||||||
|
|
||||||
& ul {
|
& ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
height: 250px;
|
height: 250px;
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
& > #global-graph-icon {
|
& > #global-graph-icon {
|
||||||
color: var(--dark);
|
color: var(--dark);
|
||||||
@ -30,10 +31,6 @@
|
|||||||
background-color: var(--lightgray);
|
background-color: var(--lightgray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& > #graph-container > svg {
|
|
||||||
margin-bottom: -5px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
& > #global-graph-outer {
|
& > #global-graph-outer {
|
||||||
|
@ -8,29 +8,36 @@ li.section-li {
|
|||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
|
||||||
& > .section {
|
& > .section {
|
||||||
display: flex;
|
display: grid;
|
||||||
align-items: center;
|
grid-template-columns: 6em 3fr 1fr;
|
||||||
|
|
||||||
@media all and (max-width: 600px) {
|
@media all and (max-width: 600px) {
|
||||||
& .tags {
|
& > .tags {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& h3 > a {
|
& > .tags {
|
||||||
font-weight: 700;
|
justify-self: end;
|
||||||
margin: 0;
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .desc a {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
& p {
|
& > .meta {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-right: 1em;
|
|
||||||
flex-basis: 6em;
|
flex-basis: 6em;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& .meta {
|
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// modifications in popover context
|
||||||
|
.popover .section {
|
||||||
|
grid-template-columns: 6em 1fr !important;
|
||||||
|
& > .tags {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
height: 20rem;
|
height: 20rem;
|
||||||
padding: 0 1rem 1rem 1rem;
|
padding: 0 1rem 1rem 1rem;
|
||||||
font-weight: initial;
|
font-weight: initial;
|
||||||
line-height: initial;
|
line-height: normal;
|
||||||
font-size: initial;
|
font-size: initial;
|
||||||
font-family: var(--bodyFont);
|
font-family: var(--bodyFont);
|
||||||
border: 1px solid var(--gray);
|
border: 1px solid var(--gray);
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
.search {
|
.search {
|
||||||
min-width: 5rem;
|
min-width: 5rem;
|
||||||
max-width: 12rem;
|
max-width: 14rem;
|
||||||
flex-grow: 0.3;
|
flex-grow: 0.3;
|
||||||
margin: 0 1.5rem;
|
|
||||||
|
|
||||||
& > #search-icon {
|
& > #search-icon {
|
||||||
background-color: var(--lightgray);
|
background-color: var(--lightgray);
|
||||||
|
@ -8,7 +8,7 @@ export type QuartzComponentProps = {
|
|||||||
externalResources: StaticResources
|
externalResources: StaticResources
|
||||||
fileData: QuartzPluginData
|
fileData: QuartzPluginData
|
||||||
cfg: GlobalConfiguration
|
cfg: GlobalConfiguration
|
||||||
children: QuartzComponent[] | JSX.Element[]
|
children: (QuartzComponent | JSX.Element)[]
|
||||||
tree: Node<QuartzPluginData>
|
tree: Node<QuartzPluginData>
|
||||||
allFiles: QuartzPluginData[]
|
allFiles: QuartzPluginData[]
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,17 @@ function slugSegment(s: string): string {
|
|||||||
return s.replace(/\s/g, '-')
|
return s.replace(/\s/g, '-')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// on the client, 'index' isn't ever rendered so we should clean it up
|
||||||
|
export function clientSideSlug(fp: string): string {
|
||||||
|
if (fp.endsWith("index")) {
|
||||||
|
fp = fp.slice(0, -"index".length)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fp
|
||||||
|
}
|
||||||
|
|
||||||
export function trimPathSuffix(fp: string): string {
|
export function trimPathSuffix(fp: string): string {
|
||||||
|
fp = clientSideSlug(fp)
|
||||||
let [cleanPath, anchor] = fp.split("#", 2)
|
let [cleanPath, anchor] = fp.split("#", 2)
|
||||||
anchor = anchor === undefined ? "" : "#" + anchor
|
anchor = anchor === undefined ? "" : "#" + anchor
|
||||||
|
|
||||||
@ -27,9 +37,6 @@ export function slugify(s: string): string {
|
|||||||
// resolve /a/b/c to ../../
|
// resolve /a/b/c to ../../
|
||||||
export function resolveToRoot(slug: string): string {
|
export function resolveToRoot(slug: string): string {
|
||||||
let fp = trimPathSuffix(slug)
|
let fp = trimPathSuffix(slug)
|
||||||
if (fp.endsWith("index")) {
|
|
||||||
fp = fp.slice(0, -"index".length)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fp === "") {
|
if (fp === "") {
|
||||||
return "."
|
return "."
|
||||||
|
@ -36,7 +36,6 @@ function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndex): string {
|
|||||||
const base = cfg.canonicalUrl ?? ""
|
const base = cfg.canonicalUrl ?? ""
|
||||||
const root = `https://${base}`
|
const root = `https://${base}`
|
||||||
|
|
||||||
// TODO: ogimage
|
|
||||||
const createURLEntry = (slug: string, content: ContentDetails): string => `<items>
|
const createURLEntry = (slug: string, content: ContentDetails): string => `<items>
|
||||||
<title>${content.title}</title>
|
<title>${content.title}</title>
|
||||||
<link>${root}/${slug}</link>
|
<link>${root}/${slug}</link>
|
||||||
|
@ -1,29 +1,17 @@
|
|||||||
import { GlobalConfiguration } from '../cfg'
|
import { GlobalConfiguration } from '../cfg'
|
||||||
import { QuartzComponent } from '../components/types'
|
import { QuartzComponent } from '../components/types'
|
||||||
import { StaticResources } from '../resources'
|
import { StaticResources } from '../resources'
|
||||||
import { googleFontHref, joinStyles } from '../theme'
|
import { joinStyles } from '../theme'
|
||||||
import { EmitCallback, PluginTypes } from './types'
|
import { EmitCallback, PluginTypes } from './types'
|
||||||
import styles from '../styles/base.scss'
|
import styles from '../styles/base.scss'
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import spaRouterScript from '../components/scripts/spa.inline'
|
|
||||||
// @ts-ignore
|
|
||||||
import popoverScript from '../components/scripts/popover.inline'
|
|
||||||
import popoverStyle from '../components/styles/popover.scss'
|
|
||||||
|
|
||||||
export type ComponentResources = {
|
export type ComponentResources = {
|
||||||
css: string[],
|
css: string[],
|
||||||
beforeDOMLoaded: string[],
|
beforeDOMLoaded: string[],
|
||||||
afterDOMLoaded: string[]
|
afterDOMLoaded: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
function joinScripts(scripts: string[]): string {
|
export function getComponentResources(plugins: PluginTypes): ComponentResources {
|
||||||
// wrap with iife to prevent scope collision
|
|
||||||
return scripts.map(script => `(function () {${script}})();`).join("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
export function emitComponentResources(cfg: GlobalConfiguration, resources: StaticResources, plugins: PluginTypes, emit: EmitCallback) {
|
|
||||||
const fps: string[] = []
|
|
||||||
const allComponents: Set<QuartzComponent> = new Set()
|
const allComponents: Set<QuartzComponent> = new Set()
|
||||||
for (const emitter of plugins.emitters) {
|
for (const emitter of plugins.emitters) {
|
||||||
const components = emitter.getQuartzComponents()
|
const components = emitter.getQuartzComponents()
|
||||||
@ -51,40 +39,34 @@ export function emitComponentResources(cfg: GlobalConfiguration, resources: Stat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg.enablePopovers) {
|
return componentResources
|
||||||
componentResources.afterDOMLoaded.push(popoverScript)
|
|
||||||
componentResources.css.push(popoverStyle)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg.enableSPA) {
|
function joinScripts(scripts: string[]): string {
|
||||||
componentResources.afterDOMLoaded.push(spaRouterScript)
|
// wrap with iife to prevent scope collision
|
||||||
} else {
|
return scripts.map(script => `(function () {${script}})();`).join("\n")
|
||||||
componentResources.afterDOMLoaded.push(`
|
|
||||||
window.spaNavigate = (url, _) => window.location.assign(url)
|
|
||||||
const event = new CustomEvent("nav", { detail: { slug: document.body.dataset.slug } })
|
|
||||||
document.dispatchEvent(event)`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function emitComponentResources(cfg: GlobalConfiguration, res: ComponentResources, emit: EmitCallback): Promise<string[]> {
|
||||||
|
const fps = await Promise.all([
|
||||||
emit({
|
emit({
|
||||||
slug: "index",
|
slug: "index",
|
||||||
ext: ".css",
|
ext: ".css",
|
||||||
content: joinStyles(cfg.theme, styles, ...componentResources.css)
|
content: joinStyles(cfg.theme, styles, ...res.css)
|
||||||
})
|
}),
|
||||||
emit({
|
emit({
|
||||||
slug: "prescript",
|
slug: "prescript",
|
||||||
ext: ".js",
|
ext: ".js",
|
||||||
content: joinScripts(componentResources.beforeDOMLoaded)
|
content: joinScripts(res.beforeDOMLoaded)
|
||||||
})
|
}),
|
||||||
emit({
|
emit({
|
||||||
slug: "postscript",
|
slug: "postscript",
|
||||||
ext: ".js",
|
ext: ".js",
|
||||||
content: joinScripts(componentResources.afterDOMLoaded)
|
content: joinScripts(res.afterDOMLoaded)
|
||||||
})
|
})
|
||||||
|
])
|
||||||
fps.push("index.css", "prescript.js", "postscript.js")
|
|
||||||
resources.css.push(googleFontHref(cfg.theme))
|
|
||||||
return fps
|
return fps
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getStaticResourcesFromPlugins(plugins: PluginTypes) {
|
export function getStaticResourcesFromPlugins(plugins: PluginTypes) {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { PluggableList } from "unified"
|
|
||||||
import remarkGfm from "remark-gfm"
|
import remarkGfm from "remark-gfm"
|
||||||
import smartypants from 'remark-smartypants'
|
import smartypants from 'remark-smartypants'
|
||||||
import { QuartzTransformerPlugin } from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
@ -20,14 +19,14 @@ export const GitHubFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> |
|
|||||||
return {
|
return {
|
||||||
name: "GitHubFlavoredMarkdown",
|
name: "GitHubFlavoredMarkdown",
|
||||||
markdownPlugins() {
|
markdownPlugins() {
|
||||||
return opts.enableSmartyPants ? [remarkGfm] : [remarkGfm, smartypants]
|
return opts.enableSmartyPants ? [remarkGfm, smartypants] : [remarkGfm]
|
||||||
},
|
},
|
||||||
htmlPlugins() {
|
htmlPlugins() {
|
||||||
if (opts.linkHeadings) {
|
if (opts.linkHeadings) {
|
||||||
return [rehypeSlug, [rehypeAutolinkHeadings, {
|
return [rehypeSlug, [rehypeAutolinkHeadings, {
|
||||||
behavior: 'append', content: {
|
behavior: 'append', content: {
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: ' §'
|
value: ' §',
|
||||||
}
|
}
|
||||||
}]]
|
}]]
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { QuartzTransformerPlugin } from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
import { relativeToRoot, slugify, trimPathSuffix } from "../../path"
|
import { clientSideSlug, relativeToRoot, slugify, trimPathSuffix } from "../../path"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { visit } from 'unist-util-visit'
|
import { visit } from 'unist-util-visit'
|
||||||
import isAbsoluteUrl from "is-absolute-url"
|
import isAbsoluteUrl from "is-absolute-url"
|
||||||
@ -27,7 +27,7 @@ export const CrawlLinks: QuartzTransformerPlugin<Partial<Options> | undefined> =
|
|||||||
htmlPlugins() {
|
htmlPlugins() {
|
||||||
return [() => {
|
return [() => {
|
||||||
return (tree, file) => {
|
return (tree, file) => {
|
||||||
const curSlug = file.data.slug!
|
const curSlug = clientSideSlug(file.data.slug!)
|
||||||
const transformLink = (target: string) => {
|
const transformLink = (target: string) => {
|
||||||
const targetSlug = slugify(decodeURI(target).trim())
|
const targetSlug = slugify(decodeURI(target).trim())
|
||||||
if (opts.markdownLinkResolution === 'relative' && !path.isAbsolute(targetSlug)) {
|
if (opts.markdownLinkResolution === 'relative' && !path.isAbsolute(targetSlug)) {
|
||||||
@ -49,7 +49,6 @@ export const CrawlLinks: QuartzTransformerPlugin<Partial<Options> | undefined> =
|
|||||||
let dest = node.properties.href
|
let dest = node.properties.href
|
||||||
node.properties.className = isAbsoluteUrl(dest) ? "external" : "internal"
|
node.properties.className = isAbsoluteUrl(dest) ? "external" : "internal"
|
||||||
|
|
||||||
|
|
||||||
// don't process external links or intra-document anchors
|
// don't process external links or intra-document anchors
|
||||||
if (!(isAbsoluteUrl(dest) || dest.startsWith("#"))) {
|
if (!(isAbsoluteUrl(dest) || dest.startsWith("#"))) {
|
||||||
node.properties.href = transformLink(dest)
|
node.properties.href = transformLink(dest)
|
||||||
|
@ -1,13 +1,69 @@
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import { QuartzConfig } from "../cfg"
|
import { GlobalConfiguration, QuartzConfig } from "../cfg"
|
||||||
import { PerfTimer } from "../perf"
|
import { PerfTimer } from "../perf"
|
||||||
import { emitComponentResources, getStaticResourcesFromPlugins } from "../plugins"
|
import { ComponentResources, emitComponentResources, getComponentResources, getStaticResourcesFromPlugins } from "../plugins"
|
||||||
import { EmitCallback } from "../plugins/types"
|
import { EmitCallback } from "../plugins/types"
|
||||||
import { ProcessedContent } from "../plugins/vfile"
|
import { ProcessedContent } from "../plugins/vfile"
|
||||||
import { QUARTZ, slugify } from "../path"
|
import { QUARTZ, slugify } from "../path"
|
||||||
import { globbyStream } from "globby"
|
import { globbyStream } from "globby"
|
||||||
import chalk from "chalk"
|
import chalk from "chalk"
|
||||||
|
import { googleFontHref } from '../theme'
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import spaRouterScript from '../components/scripts/spa.inline'
|
||||||
|
// @ts-ignore
|
||||||
|
import plausibleScript from '../components/scripts/plausible.inline'
|
||||||
|
// @ts-ignore
|
||||||
|
import popoverScript from '../components/scripts/popover.inline'
|
||||||
|
import popoverStyle from '../components/styles/popover.scss'
|
||||||
|
import { StaticResources } from "../resources"
|
||||||
|
|
||||||
|
function addGlobalPageResources(cfg: GlobalConfiguration, staticResources: StaticResources, componentResources: ComponentResources) {
|
||||||
|
// font and other resources
|
||||||
|
staticResources.css.push(googleFontHref(cfg.theme))
|
||||||
|
|
||||||
|
// popovers
|
||||||
|
if (cfg.enablePopovers) {
|
||||||
|
componentResources.afterDOMLoaded.push(popoverScript)
|
||||||
|
componentResources.css.push(popoverStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg.analytics?.provider === "google") {
|
||||||
|
const tagId = cfg.analytics.tagId
|
||||||
|
staticResources.js.push({
|
||||||
|
src: `https://www.googletagmanager.com/gtag/js?id=${tagId}`,
|
||||||
|
contentType: 'external',
|
||||||
|
loadTime: 'afterDOMReady',
|
||||||
|
})
|
||||||
|
componentResources.afterDOMLoaded.push(`
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag() { dataLayer.push(arguments); }
|
||||||
|
gtag(\`js\`, new Date());
|
||||||
|
gtag(\`config\`, \`${tagId}\`, { send_page_view: false });
|
||||||
|
|
||||||
|
document.addEventListener(\`nav\`, () => {
|
||||||
|
gtag(\`event\`, \`page_view\`, {
|
||||||
|
page_title: document.title,
|
||||||
|
page_location: location.href,
|
||||||
|
});
|
||||||
|
});`
|
||||||
|
)
|
||||||
|
} else if (cfg.analytics?.provider === "plausible") {
|
||||||
|
componentResources.afterDOMLoaded.push(plausibleScript)
|
||||||
|
}
|
||||||
|
|
||||||
|
// spa
|
||||||
|
if (cfg.enableSPA) {
|
||||||
|
componentResources.afterDOMLoaded.push(spaRouterScript)
|
||||||
|
} else {
|
||||||
|
componentResources.afterDOMLoaded.push(`
|
||||||
|
window.spaNavigate = (url, _) => window.location.assign(url)
|
||||||
|
const event = new CustomEvent("nav", { detail: { slug: document.body.dataset.slug } })
|
||||||
|
document.dispatchEvent(event)`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function emitContent(contentFolder: string, output: string, cfg: QuartzConfig, content: ProcessedContent[], verbose: boolean) {
|
export async function emitContent(contentFolder: string, output: string, cfg: QuartzConfig, content: ProcessedContent[], verbose: boolean) {
|
||||||
const perf = new PerfTimer()
|
const perf = new PerfTimer()
|
||||||
@ -19,9 +75,25 @@ export async function emitContent(contentFolder: string, output: string, cfg: Qu
|
|||||||
return pathToPage
|
return pathToPage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initialize from plugins
|
||||||
const staticResources = getStaticResourcesFromPlugins(cfg.plugins)
|
const staticResources = getStaticResourcesFromPlugins(cfg.plugins)
|
||||||
emitComponentResources(cfg.configuration, staticResources, cfg.plugins, emit)
|
|
||||||
|
|
||||||
|
// component specific scripts and styles
|
||||||
|
const componentResources = getComponentResources(cfg.plugins)
|
||||||
|
// important that this goes *after* component scripts
|
||||||
|
// as the "nav" event gets triggered here and we should make sure
|
||||||
|
// that everyone else had the chance to register a listener for it
|
||||||
|
addGlobalPageResources(cfg.configuration, staticResources, componentResources)
|
||||||
|
|
||||||
|
// emit in one go
|
||||||
|
const emittedResources = await emitComponentResources(cfg.configuration, componentResources, emit)
|
||||||
|
if (verbose) {
|
||||||
|
for (const file of emittedResources) {
|
||||||
|
console.log(`[emit:Resources] ${file}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// emitter plugins
|
||||||
let emittedFiles = 0
|
let emittedFiles = 0
|
||||||
for (const emitter of cfg.plugins.emitters) {
|
for (const emitter of cfg.plugins.emitters) {
|
||||||
try {
|
try {
|
||||||
|
@ -3,7 +3,8 @@ import { JSX } from "preact/jsx-runtime"
|
|||||||
|
|
||||||
export type JSResource = {
|
export type JSResource = {
|
||||||
loadTime: 'beforeDOMReady' | 'afterDOMReady'
|
loadTime: 'beforeDOMReady' | 'afterDOMReady'
|
||||||
moduleType?: 'module'
|
moduleType?: 'module',
|
||||||
|
spaPreserve?: boolean
|
||||||
} & ({
|
} & ({
|
||||||
src: string
|
src: string
|
||||||
contentType: 'external'
|
contentType: 'external'
|
||||||
@ -14,11 +15,12 @@ export type JSResource = {
|
|||||||
|
|
||||||
export function JSResourceToScriptElement(resource: JSResource, preserve?: boolean): JSX.Element {
|
export function JSResourceToScriptElement(resource: JSResource, preserve?: boolean): JSX.Element {
|
||||||
const scriptType = resource.moduleType ?? 'application/javascript'
|
const scriptType = resource.moduleType ?? 'application/javascript'
|
||||||
|
const spaPreserve = preserve ?? resource.spaPreserve
|
||||||
if (resource.contentType === 'external') {
|
if (resource.contentType === 'external') {
|
||||||
return <script key={resource.src} src={resource.src} type={scriptType} spa-preserve={preserve} />
|
return <script key={resource.src} src={resource.src} type={scriptType} spa-preserve={spaPreserve} />
|
||||||
} else {
|
} else {
|
||||||
const content = resource.script
|
const content = resource.script
|
||||||
return <script key={randomUUID()} type={scriptType} spa-preserve={preserve}>{content}</script>
|
return <script key={randomUUID()} type={scriptType} spa-preserve={spaPreserve}>{content}</script>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,9 @@ body {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: var(--light);
|
background-color: var(--light);
|
||||||
font-family: var(--bodyFont);
|
font-family: var(--bodyFont);
|
||||||
|
--pageWidth: 800px;
|
||||||
|
--sidePanelWidth: 400px;
|
||||||
|
--topSpacing: 6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-highlight {
|
.text-highlight {
|
||||||
@ -27,7 +30,7 @@ p, ul, text, a, tr, td, li, ol, ul, .katex {
|
|||||||
a {
|
a {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: all 0.2s ease;
|
transition: color 0.2s ease;
|
||||||
color: var(--secondary);
|
color: var(--secondary);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -43,34 +46,48 @@ a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.page {
|
.page {
|
||||||
margin: 6rem 35vw 6rem 20vw;
|
& > .page-header {
|
||||||
max-width: 1000px;
|
max-width: var(--pageWidth);
|
||||||
position: relative;
|
margin: var(--topSpacing) auto 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > #quartz-body {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
& .left, & .right {
|
& .left, & .right {
|
||||||
position: fixed;
|
flex: 1;
|
||||||
height: 100vh;
|
width: calc(calc(100vw - var(--pageWidth)) / 2);
|
||||||
overflow-y: scroll;
|
}
|
||||||
box-sizing: border-box;
|
|
||||||
|
& .left-inner, & .right-inner {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
top: 0;
|
|
||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
padding: 6rem;
|
top: 0;
|
||||||
|
width: var(--sidePanelWidth);
|
||||||
|
margin-top: calc(var(--topSpacing));
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0 4rem;
|
||||||
|
position: fixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
& .left {
|
& .left-inner {
|
||||||
left: 0;
|
left: calc(calc(100vw - var(--pageWidth)) / 2 - var(--sidePanelWidth));
|
||||||
padding-left: 10vw;
|
|
||||||
width: 20vw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
& .right {
|
& .right-inner {
|
||||||
right: 0;
|
right: calc(calc(100vw - var(--pageWidth)) / 2 - var(--sidePanelWidth));
|
||||||
padding-right: 10vw;
|
|
||||||
width: 35vw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& .center {
|
||||||
|
width: var(--pageWidth);
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
@media all and (max-width: 1200px) {
|
@media all and (max-width: 1200px) {
|
||||||
margin: 25px 5vw;
|
margin: 25px 5vw;
|
||||||
& .left, & .right {
|
& .left, & .right {
|
||||||
@ -89,7 +106,24 @@ a {
|
|||||||
& > h1 {
|
& > h1 {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// darkmode diagrams
|
||||||
|
& svg {
|
||||||
|
stroke: var(--dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& ul:has(input[type='checkbox']) {
|
||||||
|
list-style-type: none;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
transform: translateY(2px);
|
||||||
|
color: var(--secondary);
|
||||||
|
border-color: var(--lightgray);
|
||||||
|
background-color: var(--light);
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
@ -120,7 +154,7 @@ thead {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
&[id] > a {
|
&[id] > a[href^="#"] {
|
||||||
margin: 0 0.5rem;
|
margin: 0 0.5rem;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.2s ease;
|
transition: opacity 0.2s ease;
|
||||||
|
Loading…
Reference in New Issue
Block a user