This commit is contained in:
semanticdata 2024-01-30 13:23:48 -06:00
commit 0f0653ef24
33 changed files with 284 additions and 282 deletions

2
globals.d.ts vendored
View File

@ -4,7 +4,7 @@ export declare global {
type: K,
listener: (this: Document, ev: CustomEventMap[K]) => void,
): void
dispatchEvent<K extends keyof CustomEventMap>(ev: CustomEventMap[K]): void
dispatchEvent<K extends keyof CustomEventMap>(ev: CustomEventMap[K] | UIEvent): void
}
interface Window {
spaNavigate(url: URL, isBack: boolean = false)

130
package-lock.json generated
View File

@ -11,7 +11,7 @@
"dependencies": {
"@clack/prompts": "^0.7.0",
"@floating-ui/dom": "^1.5.3",
"@napi-rs/simple-git": "0.1.11",
"@napi-rs/simple-git": "0.1.14",
"async-mutex": "^0.4.0",
"chalk": "^5.3.0",
"chokidar": "^3.5.3",
@ -49,9 +49,9 @@
"remark-gfm": "^4.0.0",
"remark-math": "^6.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.0.0",
"remark-rehype": "^11.1.0",
"remark-smartypants": "^2.0.0",
"rfdc": "^1.3.0",
"rfdc": "^1.3.1",
"rimraf": "^5.0.5",
"serve-handler": "^6.1.5",
"shikiji": "^0.10.2",
@ -61,7 +61,7 @@
"unified": "^11.0.4",
"unist-util-visit": "^5.0.0",
"vfile": "^6.0.1",
"workerpool": "^8.0.0",
"workerpool": "^9.1.0",
"ws": "^8.15.1",
"yargs": "^17.7.2"
},
@ -76,7 +76,6 @@
"@types/node": "^20.1.2",
"@types/pretty-time": "^1.1.5",
"@types/source-map-support": "^0.5.10",
"@types/workerpool": "^6.4.7",
"@types/ws": "^8.5.10",
"@types/yargs": "^17.0.32",
"esbuild": "^0.19.9",
@ -517,30 +516,30 @@
}
},
"node_modules/@napi-rs/simple-git": {
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git/-/simple-git-0.1.11.tgz",
"integrity": "sha512-z14cPCBrtDKKVJ3q4GS5gmXEithGUAt+U8sICgA9i3UFdxJKD4H5rCnO7BVC3htdE9g6OR2w2IcHAL56AjpFbg==",
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git/-/simple-git-0.1.14.tgz",
"integrity": "sha512-2cDnsT0nKpQ7yg5u/Zf8/ibp9YFIKhpcfMAGATYuqdJoHuBo6P6UArZ0RDOOtfFC5b9FXuYcGw2ApbM4eWdnbQ==",
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@napi-rs/simple-git-android-arm-eabi": "0.1.11",
"@napi-rs/simple-git-android-arm64": "0.1.11",
"@napi-rs/simple-git-darwin-arm64": "0.1.11",
"@napi-rs/simple-git-darwin-x64": "0.1.11",
"@napi-rs/simple-git-linux-arm-gnueabihf": "0.1.11",
"@napi-rs/simple-git-linux-arm64-gnu": "0.1.11",
"@napi-rs/simple-git-linux-arm64-musl": "0.1.11",
"@napi-rs/simple-git-linux-x64-gnu": "0.1.11",
"@napi-rs/simple-git-linux-x64-musl": "0.1.11",
"@napi-rs/simple-git-win32-arm64-msvc": "0.1.11",
"@napi-rs/simple-git-win32-x64-msvc": "0.1.11"
"@napi-rs/simple-git-android-arm-eabi": "0.1.14",
"@napi-rs/simple-git-android-arm64": "0.1.14",
"@napi-rs/simple-git-darwin-arm64": "0.1.14",
"@napi-rs/simple-git-darwin-x64": "0.1.14",
"@napi-rs/simple-git-linux-arm-gnueabihf": "0.1.14",
"@napi-rs/simple-git-linux-arm64-gnu": "0.1.14",
"@napi-rs/simple-git-linux-arm64-musl": "0.1.14",
"@napi-rs/simple-git-linux-x64-gnu": "0.1.14",
"@napi-rs/simple-git-linux-x64-musl": "0.1.14",
"@napi-rs/simple-git-win32-arm64-msvc": "0.1.14",
"@napi-rs/simple-git-win32-x64-msvc": "0.1.14"
}
},
"node_modules/@napi-rs/simple-git-android-arm-eabi": {
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-android-arm-eabi/-/simple-git-android-arm-eabi-0.1.11.tgz",
"integrity": "sha512-wt4Wu9MxvKzEqT4iwodFs7Nrc31K73gR5hM7VnlO6iLELmUQZ5JVJkYoFWgzLQWtzIC48W2+zFMbBgY6+F2rZg==",
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-android-arm-eabi/-/simple-git-android-arm-eabi-0.1.14.tgz",
"integrity": "sha512-fAJ/Hxc9DhtSHOcB3dPCRW1YcVsqAnbNoOOnHir4aDCtqTP64HrFa7A/675v3vQZpI0u3fXHRcYqW8NF0O/zcg==",
"cpu": [
"arm"
],
@ -553,9 +552,9 @@
}
},
"node_modules/@napi-rs/simple-git-android-arm64": {
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-android-arm64/-/simple-git-android-arm64-0.1.11.tgz",
"integrity": "sha512-5/Aj6N44CxwhV3TZWRZ4vGqFj4wb2/a2gwvUZJo9Dwik9Spls7As8LaLe7pOptiGPH0GRP3H5kTT7I6twHNgqw==",
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-android-arm64/-/simple-git-android-arm64-0.1.14.tgz",
"integrity": "sha512-dav730MRAR142DoyNDafuwKXcUCYwlbxxxxOarDph7bbN0mZZnKHOQohvRCD/Uz4aJLaj6khCavXSjLDWArEUg==",
"cpu": [
"arm64"
],
@ -568,9 +567,9 @@
}
},
"node_modules/@napi-rs/simple-git-darwin-arm64": {
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-darwin-arm64/-/simple-git-darwin-arm64-0.1.11.tgz",
"integrity": "sha512-vdVsJUNcRsGVu0hBmLZdxxgwIbJA/Ias8NKWze8MZkZ3VyBwhg0uAzFgESEL3/USAgeCCHjF3uwVki8E+iPq1w==",
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-darwin-arm64/-/simple-git-darwin-arm64-0.1.14.tgz",
"integrity": "sha512-f6+DqRnI+vFvnsAyw66mWhwl0vw1TOieHV07hvKbg4PU5j+RBI+lVqwY2L+IEAxDFlPirTWKKvGY1Lr7M/yi/A==",
"cpu": [
"arm64"
],
@ -583,9 +582,9 @@
}
},
"node_modules/@napi-rs/simple-git-darwin-x64": {
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-darwin-x64/-/simple-git-darwin-x64-0.1.11.tgz",
"integrity": "sha512-ufVuZxyJ3LpApk3V101X9qYNX91fnQ4isulz9lWjg90U7Xz0Cav4J3yyFZy6B/cJpYxuiy49R8wV1xDtTeGThA==",
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-darwin-x64/-/simple-git-darwin-x64-0.1.14.tgz",
"integrity": "sha512-x/EnwJdDWJAFay8TQt09byJoBlVZhPEaTAPmRR0fUPzWTjrr28bOy8UW1ysszd9ylBxlyIhuWjOHMHu9CBigTQ==",
"cpu": [
"x64"
],
@ -598,9 +597,9 @@
}
},
"node_modules/@napi-rs/simple-git-linux-arm-gnueabihf": {
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm-gnueabihf/-/simple-git-linux-arm-gnueabihf-0.1.11.tgz",
"integrity": "sha512-rFafW0Qc/j5we2ghUecB7mFzGcNDtJ5lTiB4I7kffNeL8pEi6Yi7kST8hylswcCowia65d45xsyeNp1mFlFwcg==",
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm-gnueabihf/-/simple-git-linux-arm-gnueabihf-0.1.14.tgz",
"integrity": "sha512-k0JZaXBl031gP5VOnoMa1I3lCHlBG7QvtunX5rxnRjx2kZ+JgUyT12s/qle/4xkJ0MnmfKTeiD7hs4Cc4Z3Tzw==",
"cpu": [
"arm"
],
@ -613,9 +612,9 @@
}
},
"node_modules/@napi-rs/simple-git-linux-arm64-gnu": {
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm64-gnu/-/simple-git-linux-arm64-gnu-0.1.11.tgz",
"integrity": "sha512-HZ4yaqpj/FQ3V9qNQrTGhtXb7pLAARXeRJrwoaGfz3eZ069y2bHReFcNR//5bsVhZ18JaS9EV47F8WjDxtpI5g==",
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm64-gnu/-/simple-git-linux-arm64-gnu-0.1.14.tgz",
"integrity": "sha512-CsmKP6tSIxau10ZKxV1Q1kem2QcJ/Qlov7pxp1Q7kMErcouW0H6vliVniewicaXRVDYV9wK18iD2t5GoJttwlA==",
"cpu": [
"arm64"
],
@ -628,9 +627,9 @@
}
},
"node_modules/@napi-rs/simple-git-linux-arm64-musl": {
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm64-musl/-/simple-git-linux-arm64-musl-0.1.11.tgz",
"integrity": "sha512-b39lJiC3n2+Y6Exjx6qwHoBF++D3k2hN4mZZkvQCFSdLXJ2xtalCatSRWW3pt+mHOHMOgbGektL5v5BYq52hxw==",
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm64-musl/-/simple-git-linux-arm64-musl-0.1.14.tgz",
"integrity": "sha512-krfEckZQ3myoHwmGmqY0aHBnqAzzV66+jFNLQEKaVMSGsXA2P+UcGo0coGzmB13rFRWC2eZpZRNB3MrfrStHkw==",
"cpu": [
"arm64"
],
@ -643,9 +642,9 @@
}
},
"node_modules/@napi-rs/simple-git-linux-x64-gnu": {
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-x64-gnu/-/simple-git-linux-x64-gnu-0.1.11.tgz",
"integrity": "sha512-9EPFvY7PZg+oqWi6Jft5WgSsQtvy9Ey1g4NG+LG8y1RbvaNKthxKbR5zgx196pnFVdcLtsuIdOv/OaQlbcTXkw==",
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-x64-gnu/-/simple-git-linux-x64-gnu-0.1.14.tgz",
"integrity": "sha512-4T2Q2QdO6t3OawkwdVmdqLz2EH8lfAw2cMT/zdjfTMfhNKjJgSg3kTgRnu/tf8TLCb+wu80fFvafwE0laB2VTQ==",
"cpu": [
"x64"
],
@ -658,9 +657,9 @@
}
},
"node_modules/@napi-rs/simple-git-linux-x64-musl": {
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-x64-musl/-/simple-git-linux-x64-musl-0.1.11.tgz",
"integrity": "sha512-doIt1lPYIGL3UthlEQjdM9s1Wv0v8bz8LVAgbzJMS+UpVZzArwLWkanAJCy1HjgMTUMiE3AVJqACKIF3EfW/TQ==",
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-x64-musl/-/simple-git-linux-x64-musl-0.1.14.tgz",
"integrity": "sha512-RaTGW8u+RXJbfRF4QN2Dcr5r5DrFh4wLjOvFeOy7sGX3Q9m3IKuw5AjRxTJqIw6xD/AAPKKNzOvPjrIF7728Lw==",
"cpu": [
"x64"
],
@ -673,9 +672,9 @@
}
},
"node_modules/@napi-rs/simple-git-win32-arm64-msvc": {
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-win32-arm64-msvc/-/simple-git-win32-arm64-msvc-0.1.11.tgz",
"integrity": "sha512-TK3Uvj3Q72ebxfxDT/eLFt8sxCNHo20QMvqJ5BHt4zP1Y9Fl1DXSPRUKLBIhJd0nPcI45ZOMRiZyoT8joxAC9g==",
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-win32-arm64-msvc/-/simple-git-win32-arm64-msvc-0.1.14.tgz",
"integrity": "sha512-kb9bKG9t79HJMuRMqbUJFLfWRf952O2Ea4VFwoRA2d/Uwtowm85Ol3JV9E6oeurguRLqdMLrUKyduCW6Hc9Jsg==",
"cpu": [
"arm64"
],
@ -688,9 +687,9 @@
}
},
"node_modules/@napi-rs/simple-git-win32-x64-msvc": {
"version": "0.1.11",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-win32-x64-msvc/-/simple-git-win32-x64-msvc-0.1.11.tgz",
"integrity": "sha512-XOgP6kFDXGmB2KCXFQEsCq70n/Do2h7W9o7qZu8APAD+Sc8JGKz4hKG7PKY2ot924v9nIoKSYbHnupnhXSoXkg==",
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@napi-rs/simple-git-win32-x64-msvc/-/simple-git-win32-x64-msvc-0.1.14.tgz",
"integrity": "sha512-3835xy0e2gOaZ3SPt1pINBFSBBL3dOx3cChyAzQU0TnMU4Ye/YOh1qa5pO7BOJlCSnOh7iWt782blxCT0HH61w==",
"cpu": [
"x64"
],
@ -1114,15 +1113,6 @@
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
"integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ=="
},
"node_modules/@types/workerpool": {
"version": "6.4.7",
"resolved": "https://registry.npmjs.org/@types/workerpool/-/workerpool-6.4.7.tgz",
"integrity": "sha512-DI2U4obcMzFViyNjLw0xXspim++qkAJ4BWRdYPVMMFtOpTvMr6PAk3UTZEoSqnZnvgUkJ3ck97Ybk+iIfuJHMg==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
@ -4854,9 +4844,9 @@
}
},
"node_modules/remark-rehype": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.0.0.tgz",
"integrity": "sha512-vx8x2MDMcxuE4lBmQ46zYUDfcFMmvg80WYX+UNLeG6ixjdCCLcw1lrgAukwBTuOFsS78eoAedHGn9sNM0w7TPw==",
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz",
"integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==",
"dependencies": {
"@types/hast": "^3.0.0",
"@types/mdast": "^4.0.0",
@ -5171,9 +5161,9 @@
}
},
"node_modules/rfdc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
"integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz",
"integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg=="
},
"node_modules/rimraf": {
"version": "5.0.5",
@ -6069,9 +6059,9 @@
"integrity": "sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw=="
},
"node_modules/workerpool": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-8.0.0.tgz",
"integrity": "sha512-aoLtwqMXoYVA1JV+t8uCLo7sXkF4Q1Ijrn7954X2IVyysk2bv2Il7C9sVJH8xk9xJAL0FNgR+hPOhmvnMk/P5Q=="
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.1.0.tgz",
"integrity": "sha512-+wRWfm9yyJghvXLSHMQj3WXDxHbibHAQmRrWbqKBfy0RjftZNeQaW+Std5bSYc83ydkrxoPTPOWVlXUR9RWJdQ=="
},
"node_modules/wrap-ansi": {
"version": "8.1.0",

View File

@ -36,7 +36,7 @@
"dependencies": {
"@clack/prompts": "^0.7.0",
"@floating-ui/dom": "^1.5.3",
"@napi-rs/simple-git": "0.1.11",
"@napi-rs/simple-git": "0.1.14",
"async-mutex": "^0.4.0",
"chalk": "^5.3.0",
"chokidar": "^3.5.3",
@ -74,9 +74,9 @@
"remark-gfm": "^4.0.0",
"remark-math": "^6.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.0.0",
"remark-rehype": "^11.1.0",
"remark-smartypants": "^2.0.0",
"rfdc": "^1.3.0",
"rfdc": "^1.3.1",
"rimraf": "^5.0.5",
"serve-handler": "^6.1.5",
"shikiji": "^0.10.2",
@ -86,7 +86,7 @@
"unified": "^11.0.4",
"unist-util-visit": "^5.0.0",
"vfile": "^6.0.1",
"workerpool": "^8.0.0",
"workerpool": "^9.1.0",
"ws": "^8.15.1",
"yargs": "^17.7.2"
},
@ -98,7 +98,6 @@
"@types/node": "^20.1.2",
"@types/pretty-time": "^1.1.5",
"@types/source-map-support": "^0.5.10",
"@types/workerpool": "^6.4.7",
"@types/ws": "^8.5.10",
"@types/yargs": "^17.0.32",
"esbuild": "^0.19.9",

View File

@ -16,6 +16,7 @@ export type Analytics =
| {
provider: "umami"
websiteId: string
host?: string
}
export interface GlobalConfiguration {
@ -35,6 +36,12 @@ export interface GlobalConfiguration {
*/
baseUrl?: string
theme: Theme
/**
* The locale to use for date formatting. Default to "en-US"
* Allow to translate the date in the language of your choice.
* Need to be formated following the IETF language tag format (https://en.wikipedia.org/wiki/IETF_language_tag)
*/
locale?: string
}
export interface QuartzConfig {

View File

@ -347,7 +347,7 @@ export async function handleBuild(argv) {
directoryListing: false,
headers: [
{
source: "**/*.html",
source: "**/*.*",
headers: [{ key: "Content-Disposition", value: "inline" }],
},
],

View File

@ -1,9 +1,10 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { classNames } from "../util/lang"
function ArticleTitle({ fileData, displayClass }: QuartzComponentProps) {
const title = fileData.frontmatter?.title
if (title) {
return <h1 class={`article-title ${displayClass ?? ""}`}>{title}</h1>
return <h1 class={classNames(displayClass, "article-title")}>{title}</h1>
} else {
return null
}

View File

@ -1,12 +1,13 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import style from "./styles/backlinks.scss"
import { resolveRelative, simplifySlug } from "../util/path"
import { classNames } from "../util/lang"
function Backlinks({ fileData, allFiles, displayClass }: QuartzComponentProps) {
const slug = simplifySlug(fileData.slug!)
const backlinkFiles = allFiles.filter((file) => file.links?.includes(slug))
return (
<div class={`backlinks ${displayClass ?? ""}`}>
<div class={classNames(displayClass, "backlinks")}>
<h3>Backlinks</h3>
<ul class="overflow">
{backlinkFiles.length > 0 ? (

View File

@ -2,6 +2,7 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import breadcrumbsStyle from "./styles/breadcrumbs.scss"
import { FullSlug, SimpleSlug, resolveRelative } from "../util/path"
import { QuartzPluginData } from "../plugins/vfile"
import { classNames } from "../util/lang"
type CrumbData = {
displayName: string
@ -113,7 +114,7 @@ export default ((opts?: Partial<BreadcrumbOptions>) => {
}
return (
<nav class={`breadcrumb-container ${displayClass ?? ""}`} aria-label="breadcrumbs">
<nav class={classNames(displayClass, "breadcrumb-container")} aria-label="breadcrumbs">
{crumbs.map((crumb, index) => (
<div class="breadcrumb-element">
<a href={crumb.path}>{crumb.displayName}</a>

View File

@ -1,6 +1,7 @@
import { formatDate, getDate } from "./Date"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import readingTime from "reading-time"
import { classNames } from "../util/lang"
interface ContentMetaOptions {
/**
@ -24,7 +25,7 @@ export default ((opts?: Partial<ContentMetaOptions>) => {
const segments: string[] = []
if (fileData.dates) {
segments.push(formatDate(getDate(cfg, fileData)!))
segments.push(formatDate(getDate(cfg, fileData)!, cfg.locale))
}
// Display reading time if enabled
@ -33,7 +34,7 @@ export default ((opts?: Partial<ContentMetaOptions>) => {
segments.push(timeTaken)
}
return <p class={`content-meta ${displayClass ?? ""}`}>{segments.join(", ")}</p>
return <p class={classNames(displayClass, "content-meta")}>{segments.join(", ")}</p>
} else {
return null
}

View File

@ -4,10 +4,11 @@
import darkmodeScript from "./scripts/darkmode.inline"
import styles from "./styles/darkmode.scss"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { classNames } from "../util/lang"
function Darkmode({ displayClass }: QuartzComponentProps) {
return (
<div class={`darkmode ${displayClass ?? ""}`}>
<div class={classNames(displayClass, "darkmode")}>
<input class="toggle" id="darkmode-toggle" type="checkbox" tabIndex={-1} />
<label id="toggle-label-light" for="darkmode-toggle" tabIndex={-1}>
<svg

View File

@ -3,6 +3,7 @@ import { QuartzPluginData } from "../plugins/vfile"
interface Props {
date: Date
locale?: string
}
export type ValidDateType = keyof Required<QuartzPluginData>["dates"]
@ -16,14 +17,14 @@ export function getDate(cfg: GlobalConfiguration, data: QuartzPluginData): Date
return data.dates?.[cfg.defaultDateType]
}
export function formatDate(d: Date): string {
return d.toLocaleDateString("en-US", {
export function formatDate(d: Date, locale = "en-US"): string {
return d.toLocaleDateString(locale, {
year: "numeric",
month: "short",
day: "2-digit",
})
}
export function Date({ date }: Props) {
return <>{formatDate(date)}</>
export function Date({ date, locale }: Props) {
return <>{formatDate(date, locale)}</>
}

View File

@ -5,6 +5,7 @@ import explorerStyle from "./styles/explorer.scss"
import script from "./scripts/explorer.inline"
import { ExplorerNode, FileNode, Options } from "./ExplorerNode"
import { QuartzPluginData } from "../plugins/vfile"
import { classNames } from "../util/lang"
// Options interface defined in `ExplorerNode` to avoid circular dependency
const defaultOptions = {
@ -69,16 +70,15 @@ export default ((userOpts?: Partial<Options>) => {
}
// Get all folders of tree. Initialize with collapsed state
const folders = fileTree.getFolderPaths(opts.folderDefaultState === "collapsed")
// Stringify to pass json tree as data attribute ([data-tree])
const folders = fileTree.getFolderPaths(opts.folderDefaultState === "collapsed")
jsonTree = JSON.stringify(folders)
}
function Explorer({ allFiles, displayClass, fileData }: QuartzComponentProps) {
constructFileTree(allFiles)
return (
<div class={`explorer ${displayClass ?? ""}`}>
<div class={classNames(displayClass, "explorer")}>
<button
type="button"
id="explorer"

View File

@ -2,6 +2,7 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
// @ts-ignore
import script from "./scripts/graph.inline"
import style from "./styles/graph.scss"
import { classNames } from "../util/lang"
export interface D3Config {
drag: boolean
@ -56,7 +57,7 @@ export default ((opts?: GraphOptions) => {
const localGraph = { ...defaultOptions.localGraph, ...opts?.localGraph }
const globalGraph = { ...defaultOptions.globalGraph, ...opts?.globalGraph }
return (
<div class={`graph ${displayClass ?? ""}`}>
<div class={classNames(displayClass, "graph")}>
<h3>Graph View</h3>
<div class="graph-outer">
<div id="graph-container" data-cfg={JSON.stringify(localGraph)}></div>

View File

@ -46,7 +46,7 @@ export function PageList({ cfg, fileData, allFiles, limit }: Props) {
<div class="section">
{page.dates && (
<p class="meta">
<Date date={getDate(cfg, page)!} />
<Date date={getDate(cfg, page)!} locale={cfg.locale} />
</p>
)}
<div class="desc">

View File

@ -1,11 +1,12 @@
import { pathToRoot } from "../util/path"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { classNames } from "../util/lang"
function PageTitle({ fileData, cfg, displayClass }: QuartzComponentProps) {
const title = cfg?.pageTitle ?? "Untitled Quartz"
const baseDir = pathToRoot(fileData.slug!)
return (
<h1 class={`page-title ${displayClass ?? ""}`}>
<h1 class={classNames(displayClass, "page-title")}>
<a href={baseDir}>{title}</a>
</h1>
)

View File

@ -5,6 +5,7 @@ import { byDateAndAlphabetical } from "./PageList"
import style from "./styles/recentNotes.scss"
import { Date, getDate } from "./Date"
import { GlobalConfiguration } from "../cfg"
import { classNames } from "../util/lang"
interface Options {
title: string
@ -28,7 +29,7 @@ export default ((userOpts?: Partial<Options>) => {
const pages = allFiles.filter(opts.filter).sort(opts.sort)
const remaining = Math.max(0, pages.length - opts.limit)
return (
<div class={`recent-notes ${displayClass ?? ""}`}>
<div class={classNames(displayClass, "recent-notes")}>
<h3>{opts.title}</h3>
<ul class="recent-ul">
{pages.slice(0, opts.limit).map((page) => {
@ -47,7 +48,7 @@ export default ((userOpts?: Partial<Options>) => {
</div>
{page.dates && (
<p class="meta">
<Date date={getDate(cfg, page)!} />
<Date date={getDate(cfg, page)!} locale={cfg.locale} />
</p>
)}
<ul class="tags">

View File

@ -2,11 +2,12 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import style from "./styles/search.scss"
// @ts-ignore
import script from "./scripts/search.inline"
import { classNames } from "../util/lang"
export default (() => {
function Search({ displayClass }: QuartzComponentProps) {
return (
<div class={`search ${displayClass ?? ""}`}>
<div class={classNames(displayClass, "search")}>
<div id="search-icon">
{/* <p>Search</p> */}
<div></div>

View File

@ -1,7 +1,8 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { classNames } from "../util/lang"
function Spacer({ displayClass }: QuartzComponentProps) {
return <div class={`spacer ${displayClass ?? ""}`}></div>
return <div class={classNames(displayClass, "spacer")}></div>
}
export default (() => Spacer) satisfies QuartzComponentConstructor

View File

@ -1,6 +1,7 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import legacyStyle from "./styles/legacyToc.scss"
import modernStyle from "./styles/toc.scss"
import { classNames } from "../util/lang"
// @ts-ignore
import script from "./scripts/toc.inline"
@ -19,7 +20,7 @@ function TableOfContents({ fileData, displayClass }: QuartzComponentProps) {
}
return (
<div class={`toc ${displayClass ?? ""}`}>
<div class={classNames(displayClass, "toc")}>
<button type="button" id="toc" class={fileData.collapseToc ? "collapsed" : ""}>
<h3>Table of Contents</h3>
<svg

View File

@ -1,12 +1,13 @@
import { pathToRoot, slugTag } from "../util/path"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import { classNames } from "../util/lang"
function TagList({ fileData, displayClass }: QuartzComponentProps) {
const tags = fileData.frontmatter?.tags
const baseDir = pathToRoot(fileData.slug!)
if (tags && tags.length > 0) {
return (
<ul class={`tags ${displayClass ?? ""}`}>
<ul class={classNames(displayClass, "tags")}>
{tags.map((tag) => {
const display = `#${tag}`
const linkDest = baseDir + `/tags/${slugTag(tag)}`

View File

@ -33,7 +33,8 @@ export default ((opts?: Partial<FolderContentOptions>) => {
const isDirectChild = fileParts.length === folderParts.length + 1
return prefixed && isDirectChild
})
const cssClasses: string[] = fileData.frontmatter?.cssclasses ?? []
const classes = ["popover-hint", ...cssClasses].join(" ")
const listProps = {
...props,
allFiles: allPagesInFolder,
@ -45,15 +46,17 @@ export default ((opts?: Partial<FolderContentOptions>) => {
: htmlToJsx(fileData.filePath!, tree)
return (
<div class="popover-hint">
<div class={classes}>
<article>
<p>{content}</p>
</article>
{options.showFolderCount && (
<p>{pluralize(allPagesInFolder.length, "item")} under this folder.</p>
)}
<div>
<PageList {...listProps} />
<div class="page-listing">
{options.showFolderCount && (
<p>{pluralize(allPagesInFolder.length, "item")} under this folder.</p>
)}
<div>
<PageList {...listProps} />
</div>
</div>
</div>
)

View File

@ -26,7 +26,8 @@ function TagContent(props: QuartzComponentProps) {
(tree as Root).children.length === 0
? fileData.description
: htmlToJsx(fileData.filePath!, tree)
const cssClasses: string[] = fileData.frontmatter?.cssclasses ?? []
const classes = ["popover-hint", ...cssClasses].join(" ")
if (tag === "/") {
const tags = [
...new Set(
@ -37,9 +38,8 @@ function TagContent(props: QuartzComponentProps) {
for (const tag of tags) {
tagItemMap.set(tag, allPagesWithTag(tag))
}
return (
<div class="popover-hint">
<div class={classes}>
<article>
<p>{content}</p>
</article>
@ -62,11 +62,13 @@ function TagContent(props: QuartzComponentProps) {
</a>
</h2>
{content && <p>{content}</p>}
<p>
{pluralize(pages.length, "item")} with this tag.{" "}
{pages.length > numPages && `Showing first ${numPages}.`}
</p>
<PageList limit={numPages} {...listProps} />
<div class="page-listing">
<p>
{pluralize(pages.length, "item")} with this tag.{" "}
{pages.length > numPages && `Showing first ${numPages}.`}
</p>
<PageList limit={numPages} {...listProps} />
</div>
</div>
)
})}
@ -81,11 +83,13 @@ function TagContent(props: QuartzComponentProps) {
}
return (
<div class="popover-hint">
<div class={classes}>
<article>{content}</article>
<p>{pluralize(pages.length, "item")} with this tag.</p>
<div>
<PageList {...listProps} />
<div class="page-listing">
<p>{pluralize(pages.length, "item")} with this tag.</p>
<div>
<PageList {...listProps} />
</div>
</div>
</div>
)

View File

@ -1,132 +1,106 @@
import { FolderState } from "../ExplorerNode"
// Current state of folders
let explorerState: FolderState[]
type MaybeHTMLElement = HTMLElement | undefined
let currentExplorerState: FolderState[]
const observer = new IntersectionObserver((entries) => {
// If last element is observed, remove gradient of "overflow" class so element is visible
const explorer = document.getElementById("explorer-ul")
const explorerUl = document.getElementById("explorer-ul")
if (!explorerUl) return
for (const entry of entries) {
if (entry.isIntersecting) {
explorer?.classList.add("no-background")
explorerUl.classList.add("no-background")
} else {
explorer?.classList.remove("no-background")
explorerUl.classList.remove("no-background")
}
}
})
function toggleExplorer(this: HTMLElement) {
// Toggle collapsed state of entire explorer
this.classList.toggle("collapsed")
const content = this.nextElementSibling as HTMLElement
const content = this.nextElementSibling as MaybeHTMLElement
if (!content) return
content.classList.toggle("collapsed")
content.style.maxHeight = content.style.maxHeight === "0px" ? content.scrollHeight + "px" : "0px"
}
function toggleFolder(evt: MouseEvent) {
evt.stopPropagation()
const target = evt.target as MaybeHTMLElement
if (!target) return
// Element that was clicked
const target = evt.target as HTMLElement
// Check if target was svg icon or button
const isSvg = target.nodeName === "svg"
const childFolderContainer = (
isSvg
? target.parentElement?.nextSibling
: target.parentElement?.parentElement?.nextElementSibling
) as MaybeHTMLElement
const currentFolderParent = (
isSvg ? target.nextElementSibling : target.parentElement
) as MaybeHTMLElement
if (!(childFolderContainer && currentFolderParent)) return
// corresponding <ul> element relative to clicked button/folder
let childFolderContainer: HTMLElement
// <li> element of folder (stores folder-path dataset)
let currentFolderParent: HTMLElement
// Get correct relative container and toggle collapsed class
if (isSvg) {
childFolderContainer = target.parentElement?.nextSibling as HTMLElement
currentFolderParent = target.nextElementSibling as HTMLElement
childFolderContainer.classList.toggle("open")
} else {
childFolderContainer = target.parentElement?.parentElement?.nextElementSibling as HTMLElement
currentFolderParent = target.parentElement as HTMLElement
childFolderContainer.classList.toggle("open")
}
if (!childFolderContainer) return
// Collapse folder container
childFolderContainer.classList.toggle("open")
const isCollapsed = childFolderContainer.classList.contains("open")
setFolderState(childFolderContainer, !isCollapsed)
// Save folder state to localStorage
const clickFolderPath = currentFolderParent.dataset.folderpath as string
const fullFolderPath = clickFolderPath
toggleCollapsedByPath(explorerState, fullFolderPath)
const stringifiedFileTree = JSON.stringify(explorerState)
const fullFolderPath = currentFolderParent.dataset.folderpath as string
toggleCollapsedByPath(currentExplorerState, fullFolderPath)
const stringifiedFileTree = JSON.stringify(currentExplorerState)
localStorage.setItem("fileTree", stringifiedFileTree)
}
function setupExplorer() {
// Set click handler for collapsing entire explorer
const explorer = document.getElementById("explorer")
if (!explorer) return
if (explorer.dataset.behavior === "collapse") {
for (const item of document.getElementsByClassName(
"folder-button",
) as HTMLCollectionOf<HTMLElement>) {
item.removeEventListener("click", toggleFolder)
item.addEventListener("click", toggleFolder)
}
}
explorer.removeEventListener("click", toggleExplorer)
explorer.addEventListener("click", toggleExplorer)
// Set up click handlers for each folder (click handler on folder "icon")
for (const item of document.getElementsByClassName(
"folder-icon",
) as HTMLCollectionOf<HTMLElement>) {
item.removeEventListener("click", toggleFolder)
item.addEventListener("click", toggleFolder)
}
// Get folder state from local storage
const storageTree = localStorage.getItem("fileTree")
// Convert to bool
const useSavedFolderState = explorer?.dataset.savestate === "true"
const oldExplorerState: FolderState[] =
storageTree && useSavedFolderState ? JSON.parse(storageTree) : []
const oldIndex = new Map(oldExplorerState.map((entry) => [entry.path, entry.collapsed]))
const newExplorerState: FolderState[] = explorer.dataset.tree
? JSON.parse(explorer.dataset.tree)
: []
currentExplorerState = []
for (const { path, collapsed } of newExplorerState) {
currentExplorerState.push({ path, collapsed: oldIndex.get(path) ?? collapsed })
}
if (explorer) {
// Get config
const collapseBehavior = explorer.dataset.behavior
// Add click handlers for all folders (click handler on folder "label")
if (collapseBehavior === "collapse") {
Array.prototype.forEach.call(
document.getElementsByClassName("folder-button"),
function (item) {
item.removeEventListener("click", toggleFolder)
item.addEventListener("click", toggleFolder)
},
)
currentExplorerState.map((folderState) => {
const folderLi = document.querySelector(
`[data-folderpath='${folderState.path}']`,
) as MaybeHTMLElement
const folderUl = folderLi?.parentElement?.nextElementSibling as MaybeHTMLElement
if (folderUl) {
setFolderState(folderUl, folderState.collapsed)
}
// Add click handler to main explorer
explorer.removeEventListener("click", toggleExplorer)
explorer.addEventListener("click", toggleExplorer)
}
// Set up click handlers for each folder (click handler on folder "icon")
Array.prototype.forEach.call(document.getElementsByClassName("folder-icon"), function (item) {
item.removeEventListener("click", toggleFolder)
item.addEventListener("click", toggleFolder)
})
if (storageTree && useSavedFolderState) {
// Get state from localStorage and set folder state
explorerState = JSON.parse(storageTree)
explorerState.map((folderUl) => {
// grab <li> element for matching folder path
const folderLi = document.querySelector(`[data-folderpath='${folderUl.path}']`) as HTMLElement
// Get corresponding content <ul> tag and set state
if (folderLi) {
const folderUL = folderLi.parentElement?.nextElementSibling
if (folderUL) {
setFolderState(folderUL as HTMLElement, folderUl.collapsed)
}
}
})
} else if (explorer?.dataset.tree) {
// If tree is not in localStorage or config is disabled, use tree passed from Explorer as dataset
explorerState = JSON.parse(explorer.dataset.tree)
}
}
window.addEventListener("resize", setupExplorer)
document.addEventListener("nav", () => {
setupExplorer()
observer.disconnect()
// select pseudo element at end of list
@ -142,11 +116,7 @@ document.addEventListener("nav", () => {
* @param collapsed if folder should be set to collapsed or not
*/
function setFolderState(folderElement: HTMLElement, collapsed: boolean) {
if (collapsed) {
folderElement?.classList.remove("open")
} else {
folderElement?.classList.add("open")
}
return collapsed ? folderElement.classList.remove("open") : folderElement.classList.add("open")
}
/**

View File

@ -319,8 +319,8 @@ function renderGlobalGraph() {
registerEscapeHandler(container, hideGlobalGraph)
}
document.addEventListener("nav", async (e: unknown) => {
const slug = (e as CustomEventMap["nav"]).detail.url
document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
const slug = e.detail.url
addToVisited(slug)
await renderGraph("graph-container", slug)

View File

@ -16,7 +16,8 @@ const observer = new IntersectionObserver((entries) => {
function toggleToc(this: HTMLElement) {
this.classList.toggle("collapsed")
const content = this.nextElementSibling as HTMLElement
const content = this.nextElementSibling as HTMLElement | undefined
if (!content) return
content.classList.toggle("collapsed")
content.style.maxHeight = content.style.maxHeight === "0px" ? content.scrollHeight + "px" : "0px"
}
@ -25,7 +26,8 @@ function setupToc() {
const toc = document.getElementById("toc")
if (toc) {
const collapsed = toc.classList.contains("collapsed")
const content = toc.nextElementSibling as HTMLElement
const content = toc.nextElementSibling as HTMLElement | undefined
if (!content) return
content.style.maxHeight = collapsed ? "0px" : content.scrollHeight + "px"
toc.removeEventListener("click", toggleToc)
toc.addEventListener("click", toggleToc)

View File

@ -119,7 +119,7 @@ function addGlobalPageResources(
} else if (cfg.analytics?.provider === "umami") {
componentResources.afterDOMLoaded.push(`
const umamiScript = document.createElement("script")
umamiScript.src = "https://analytics.umami.is/script.js"
umamiScript.src = cfg.analytics.host ?? "https://analytics.umami.is/script.js"
umamiScript.setAttribute("data-website-id", "${cfg.analytics.websiteId}")
umamiScript.async = true

View File

@ -5,7 +5,6 @@ import yaml from "js-yaml"
import toml from "toml"
import { slugTag } from "../../util/path"
import { QuartzPluginData } from "../vfile"
import chalk from "chalk"
export interface Options {
delims: string | string[]
@ -17,23 +16,6 @@ const defaultOptions: Options = {
language: "yaml",
}
function coerceDate(fp: string, d: unknown): Date | undefined {
if (d === undefined || d === null) return undefined
const dt = new Date(d as string | number)
const invalidDate = isNaN(dt.getTime()) || dt.getTime() === 0
if (invalidDate) {
console.log(
chalk.yellow(
`\nWarning: found invalid date "${d}" in \`${fp}\`. Supported formats: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format`,
),
)
return undefined
}
return dt
}
function coalesceAliases(data: { [key: string]: any }, aliases: string[]) {
for (const alias of aliases) {
if (data[alias] !== undefined && data[alias] !== null) return data[alias]
@ -66,7 +48,6 @@ export const FrontMatter: QuartzTransformerPlugin<Partial<Options> | undefined>
[remarkFrontmatter, ["yaml", "toml"]],
() => {
return (_, file) => {
const fp = file.data.filePath!
const { data } = matter(Buffer.from(file.value), {
...opts,
engines: {
@ -88,16 +69,6 @@ export const FrontMatter: QuartzTransformerPlugin<Partial<Options> | undefined>
if (aliases) data.aliases = aliases
const cssclasses = coerceToArray(coalesceAliases(data, ["cssclasses", "cssclass"]))
if (cssclasses) data.cssclasses = cssclasses
const created = coerceDate(fp, coalesceAliases(data, ["created", "date"]))
if (created) data.created = created
const modified = coerceDate(
fp,
coalesceAliases(data, ["modified", "lastmod", "updated", "last-modified"]),
)
if (modified) data.modified = modified
const published = coerceDate(fp, coalesceAliases(data, ["published", "publishDate"]))
if (published) data.published = published
// fill in frontmatter
file.data.frontmatter = data as QuartzPluginData["frontmatter"]
@ -120,9 +91,6 @@ declare module "vfile" {
draft: boolean
enableToc: string
cssclasses: string[]
created: Date
modified: Date
published: Date
}>
}
}

View File

@ -37,8 +37,36 @@ export const GitHubFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> |
"data-no-popover": true,
},
content: {
type: "text",
value: " §",
type: "element",
tagName: "svg",
properties: {
width: 18,
height: 18,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
},
children: [
{
type: "element",
tagName: "path",
properties: {
d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71",
},
children: [],
},
{
type: "element",
tagName: "path",
properties: {
d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71",
},
children: [],
},
],
},
},
],

View File

@ -12,6 +12,21 @@ const defaultOptions: Options = {
priority: ["frontmatter", "git", "filesystem"],
}
function coerceDate(fp: string, d: any): Date {
const dt = new Date(d)
const invalidDate = isNaN(dt.getTime()) || dt.getTime() === 0
if (invalidDate && d !== undefined) {
console.log(
chalk.yellow(
`\nWarning: found invalid date "${d}" in \`${fp}\`. Supported formats: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format`,
),
)
}
return invalidDate ? new Date() : dt
}
type MaybeDate = undefined | string | number
export const CreatedModifiedDate: QuartzTransformerPlugin<Partial<Options> | undefined> = (
userOpts,
) => {
@ -23,21 +38,23 @@ export const CreatedModifiedDate: QuartzTransformerPlugin<Partial<Options> | und
() => {
let repo: Repository | undefined = undefined
return async (_tree, file) => {
let created: Date | undefined = undefined
let modified: Date | undefined = undefined
let published: Date | undefined = undefined
let created: MaybeDate = undefined
let modified: MaybeDate = undefined
let published: MaybeDate = undefined
const fp = file.data.filePath!
const fullFp = path.posix.join(file.cwd, fp)
for (const source of opts.priority) {
if (source === "filesystem") {
const st = await fs.promises.stat(fullFp)
created ||= new Date(st.birthtimeMs)
modified ||= new Date(st.mtimeMs)
created ||= st.birthtimeMs
modified ||= st.mtimeMs
} else if (source === "frontmatter" && file.data.frontmatter) {
created ||= file.data.frontmatter.created
modified ||= file.data.frontmatter.modified
published ||= file.data.frontmatter.published
created ||= file.data.frontmatter.date as MaybeDate
modified ||= file.data.frontmatter.lastmod as MaybeDate
modified ||= file.data.frontmatter.updated as MaybeDate
modified ||= file.data.frontmatter["last-modified"] as MaybeDate
published ||= file.data.frontmatter.publishDate as MaybeDate
} else if (source === "git") {
if (!repo) {
// Get a reference to the main git repo.
@ -47,9 +64,7 @@ export const CreatedModifiedDate: QuartzTransformerPlugin<Partial<Options> | und
}
try {
modified ||= new Date(
await repo.getFileLatestModifiedDateAsync(file.data.filePath!),
)
modified ||= await repo.getFileLatestModifiedDateAsync(file.data.filePath!)
} catch {
console.log(
chalk.yellow(
@ -61,13 +76,10 @@ export const CreatedModifiedDate: QuartzTransformerPlugin<Partial<Options> | und
}
}
created ||= new Date()
modified ||= new Date()
published ||= new Date()
file.data.dates = {
created,
modified,
published,
created: coerceDate(fp, created),
modified: coerceDate(fp, modified),
published: coerceDate(fp, published),
}
}
},

View File

@ -138,6 +138,9 @@ const tagRegex = new RegExp(/(?:^| )#((?:[-_\p{L}\p{Emoji}\d])+(?:\/[-_\p{L}\p{E
const blockReferenceRegex = new RegExp(/\^([-_A-Za-z0-9]+)$/, "g")
const ytLinkRegex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/
const videoExtensionRegex = new RegExp(/\.(mp4|webm|ogg|avi|mov|flv|wmv|mkv|mpg|mpeg|3gp|m4v)$/)
const wikilinkImageEmbedRegex = new RegExp(
/^(?<alt>(?!^\d*x?\d*$).*?)?(\|?\s*?(?<width>\d+)(x(?<height>\d+))?)?$/,
)
export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | undefined> = (
userOpts,
@ -222,18 +225,10 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
const ext: string = path.extname(fp).toLowerCase()
const url = slugifyFilePath(fp as FilePath)
if ([".png", ".jpg", ".jpeg", ".gif", ".bmp", ".svg", ".webp"].includes(ext)) {
// either |alt|dims or |dims
let [alt, dims] = (alias ?? "").split("|")
// |dims case, treat first alt slot as dims
if (dims === undefined) {
dims = alt
alt = ""
}
let [width, height] = dims.split("x", 2)
width ||= "auto"
height ||= "auto"
const match = wikilinkImageEmbedRegex.exec(alias ?? "")
const alt = match?.groups?.alt ?? ""
const width = match?.groups?.width ?? "auto"
const height = match?.groups?.height ?? "auto"
return {
type: "image",
url,
@ -441,7 +436,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
value: `<div
class="callout-title"
>
<div class="callout-icon">${callouts[calloutType] ?? callouts.note}</div>
<div class="callout-icon">${callouts[calloutType] ?? callouts.note}</div>
<div class="callout-title-inner">${title}</div>
${collapse ? toggleIcon : ""}
</div>`,

View File

@ -175,9 +175,11 @@ a {
& .sidebar.right {
right: calc(calc(100vw - $pageWidth) / 2 - $sidePanelWidth);
flex-wrap: wrap;
& > * {
@media all and (max-width: $fullPageWidth) {
flex: 1;
min-width: 140px;
}
}
}
@ -280,7 +282,6 @@ h6 {
opacity: 0;
transition: opacity 0.2s ease;
transform: translateY(-0.1rem);
display: inline-block;
font-family: var(--codeFont);
user-select: none;
}

View File

@ -9,3 +9,13 @@ export function pluralize(count: number, s: string): string {
export function capitalize(s: string): string {
return s.substring(0, 1).toUpperCase() + s.substring(1)
}
export function classNames(
displayClass?: "mobile-only" | "desktop-only",
...classes: string[]
): string {
if (displayClass) {
classes.push(displayClass)
}
return classes.join(" ")
}

View File

@ -52,7 +52,7 @@ function sluggify(s: string): string {
.split("/")
.map((segment) =>
segment.replace(/\s/g, "-").replace(/%/g, "-percent").replace(/\?/g, "-q").replace(/#/g, ""),
) // slugify all segments
)
.join("/") // always use / as sep
.replace(/\/$/, "")
}