mirror of
https://github.com/jackyzha0/quartz.git
synced 2026-03-21 21:45:42 -05:00
feat: migrate 7 feature components to community plugins (Phase B)
Migrate ArticleTitle, TagList, PageTitle, Darkmode, ReaderMode, ContentMeta, and Footer from internal components to community plugins. Update layout to use Plugin.X() pattern, remove internal component files and their styles/scripts. Add MIGRATION_TASKS.md documenting the full migration roadmap.
This commit is contained in:
parent
0f96097745
commit
264bb7cfca
1116
MIGRATION_TASKS.md
Normal file
1116
MIGRATION_TASKS.md
Normal file
File diff suppressed because it is too large
Load Diff
@ -99,6 +99,13 @@ const config: QuartzConfig = {
|
|||||||
"github:quartz-community/table-of-contents",
|
"github:quartz-community/table-of-contents",
|
||||||
"github:quartz-community/backlinks",
|
"github:quartz-community/backlinks",
|
||||||
"github:quartz-community/comments",
|
"github:quartz-community/comments",
|
||||||
|
"github:quartz-community/article-title",
|
||||||
|
"github:quartz-community/tag-list",
|
||||||
|
"github:quartz-community/page-title",
|
||||||
|
"github:quartz-community/darkmode",
|
||||||
|
"github:quartz-community/reader-mode",
|
||||||
|
"github:quartz-community/content-meta",
|
||||||
|
"github:quartz-community/footer",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,12 @@ const graphComponent = Plugin.Graph() as QuartzComponent
|
|||||||
const searchComponent = Plugin.Search() as QuartzComponent
|
const searchComponent = Plugin.Search() as QuartzComponent
|
||||||
const backlinksComponent = Plugin.Backlinks() as QuartzComponent
|
const backlinksComponent = Plugin.Backlinks() as QuartzComponent
|
||||||
const tocComponent = Plugin.TableOfContents() as QuartzComponent
|
const tocComponent = Plugin.TableOfContents() as QuartzComponent
|
||||||
|
const articleTitleComponent = Plugin.ArticleTitle() as QuartzComponent
|
||||||
|
const contentMetaComponent = Plugin.ContentMeta() as QuartzComponent
|
||||||
|
const tagListComponent = Plugin.TagList() as QuartzComponent
|
||||||
|
const pageTitleComponent = Plugin.PageTitle() as QuartzComponent
|
||||||
|
const darkmodeComponent = Plugin.Darkmode() as QuartzComponent
|
||||||
|
const readerModeComponent = Plugin.ReaderMode() as QuartzComponent
|
||||||
|
|
||||||
// components shared across all pages
|
// components shared across all pages
|
||||||
export const sharedPageComponents: SharedLayout = {
|
export const sharedPageComponents: SharedLayout = {
|
||||||
@ -19,12 +25,12 @@ export const sharedPageComponents: SharedLayout = {
|
|||||||
// provider: "giscus",
|
// provider: "giscus",
|
||||||
// options: {}) as QuartzComponent,
|
// options: {}) as QuartzComponent,
|
||||||
],
|
],
|
||||||
footer: Component.Footer({
|
footer: Plugin.Footer({
|
||||||
links: {
|
links: {
|
||||||
GitHub: "https://github.com/jackyzha0/quartz",
|
GitHub: "https://github.com/jackyzha0/quartz",
|
||||||
"Discord Community": "https://discord.gg/cRFFHYye7t",
|
"Discord Community": "https://discord.gg/cRFFHYye7t",
|
||||||
},
|
},
|
||||||
}),
|
}) as QuartzComponent,
|
||||||
}
|
}
|
||||||
|
|
||||||
// components for pages that display a single page (e.g. a single note)
|
// components for pages that display a single page (e.g. a single note)
|
||||||
@ -34,12 +40,12 @@ export const defaultContentPageLayout: PageLayout = {
|
|||||||
component: Component.Breadcrumbs(),
|
component: Component.Breadcrumbs(),
|
||||||
condition: (page) => page.fileData.slug !== "index",
|
condition: (page) => page.fileData.slug !== "index",
|
||||||
}),
|
}),
|
||||||
Component.ArticleTitle(),
|
articleTitleComponent,
|
||||||
Component.ContentMeta(),
|
contentMetaComponent,
|
||||||
Component.TagList(),
|
tagListComponent,
|
||||||
],
|
],
|
||||||
left: [
|
left: [
|
||||||
Component.PageTitle(),
|
pageTitleComponent,
|
||||||
Component.MobileOnly(Component.Spacer()),
|
Component.MobileOnly(Component.Spacer()),
|
||||||
Component.Flex({
|
Component.Flex({
|
||||||
components: [
|
components: [
|
||||||
@ -47,8 +53,8 @@ export const defaultContentPageLayout: PageLayout = {
|
|||||||
Component: searchComponent,
|
Component: searchComponent,
|
||||||
grow: true,
|
grow: true,
|
||||||
},
|
},
|
||||||
{ Component: Component.Darkmode() },
|
{ Component: darkmodeComponent },
|
||||||
{ Component: Component.ReaderMode() },
|
{ Component: readerModeComponent },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
explorerComponent,
|
explorerComponent,
|
||||||
@ -58,9 +64,9 @@ export const defaultContentPageLayout: PageLayout = {
|
|||||||
|
|
||||||
// components for pages that display lists of pages (e.g. tags or folders)
|
// components for pages that display lists of pages (e.g. tags or folders)
|
||||||
export const defaultListPageLayout: PageLayout = {
|
export const defaultListPageLayout: PageLayout = {
|
||||||
beforeBody: [Component.Breadcrumbs(), Component.ArticleTitle(), Component.ContentMeta()],
|
beforeBody: [Component.Breadcrumbs(), articleTitleComponent, contentMetaComponent],
|
||||||
left: [
|
left: [
|
||||||
Component.PageTitle(),
|
pageTitleComponent,
|
||||||
Component.MobileOnly(Component.Spacer()),
|
Component.MobileOnly(Component.Spacer()),
|
||||||
Component.Flex({
|
Component.Flex({
|
||||||
components: [
|
components: [
|
||||||
@ -68,7 +74,7 @@ export const defaultListPageLayout: PageLayout = {
|
|||||||
Component: searchComponent,
|
Component: searchComponent,
|
||||||
grow: true,
|
grow: true,
|
||||||
},
|
},
|
||||||
{ Component: Component.Darkmode() },
|
{ Component: darkmodeComponent },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
explorerComponent,
|
explorerComponent,
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
|
||||||
import { classNames } from "../util/lang"
|
|
||||||
|
|
||||||
const ArticleTitle: QuartzComponent = ({ fileData, displayClass }: QuartzComponentProps) => {
|
|
||||||
const title = fileData.frontmatter?.title
|
|
||||||
if (title) {
|
|
||||||
return <h1 class={classNames(displayClass, "article-title")}>{title}</h1>
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ArticleTitle.css = `
|
|
||||||
.article-title {
|
|
||||||
margin: 2rem 0 0 0;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export default (() => ArticleTitle) satisfies QuartzComponentConstructor
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
import { Date, getDate } from "./Date"
|
|
||||||
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
|
||||||
import readingTime from "reading-time"
|
|
||||||
import { classNames } from "../util/lang"
|
|
||||||
import { i18n } from "../i18n"
|
|
||||||
import { JSX } from "preact"
|
|
||||||
import style from "./styles/contentMeta.scss"
|
|
||||||
|
|
||||||
interface ContentMetaOptions {
|
|
||||||
/**
|
|
||||||
* Whether to display reading time
|
|
||||||
*/
|
|
||||||
showReadingTime: boolean
|
|
||||||
showComma: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultOptions: ContentMetaOptions = {
|
|
||||||
showReadingTime: true,
|
|
||||||
showComma: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ((opts?: Partial<ContentMetaOptions>) => {
|
|
||||||
// Merge options with defaults
|
|
||||||
const options: ContentMetaOptions = { ...defaultOptions, ...opts }
|
|
||||||
|
|
||||||
function ContentMetadata({ cfg, fileData, displayClass }: QuartzComponentProps) {
|
|
||||||
const text = fileData.text
|
|
||||||
|
|
||||||
if (text) {
|
|
||||||
const segments: (string | JSX.Element)[] = []
|
|
||||||
|
|
||||||
if (fileData.dates) {
|
|
||||||
segments.push(<Date date={getDate(cfg, fileData)!} locale={cfg.locale} />)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display reading time if enabled
|
|
||||||
if (options.showReadingTime) {
|
|
||||||
const { minutes, words: _words } = readingTime(text)
|
|
||||||
const displayedTime = i18n(cfg.locale).components.contentMeta.readingTime({
|
|
||||||
minutes: Math.ceil(minutes),
|
|
||||||
})
|
|
||||||
segments.push(<span>{displayedTime}</span>)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<p show-comma={options.showComma} class={classNames(displayClass, "content-meta")}>
|
|
||||||
{segments}
|
|
||||||
</p>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentMetadata.css = style
|
|
||||||
|
|
||||||
return ContentMetadata
|
|
||||||
}) satisfies QuartzComponentConstructor
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
// @ts-ignore
|
|
||||||
import darkmodeScript from "./scripts/darkmode.inline"
|
|
||||||
import styles from "./styles/darkmode.scss"
|
|
||||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
|
||||||
import { i18n } from "../i18n"
|
|
||||||
import { classNames } from "../util/lang"
|
|
||||||
|
|
||||||
const Darkmode: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
|
|
||||||
return (
|
|
||||||
<button class={classNames(displayClass, "darkmode")}>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
|
||||||
version="1.1"
|
|
||||||
class="dayIcon"
|
|
||||||
x="0px"
|
|
||||||
y="0px"
|
|
||||||
viewBox="0 0 35 35"
|
|
||||||
style="enable-background:new 0 0 35 35"
|
|
||||||
xmlSpace="preserve"
|
|
||||||
aria-label={i18n(cfg.locale).components.themeToggle.darkMode}
|
|
||||||
>
|
|
||||||
<title>{i18n(cfg.locale).components.themeToggle.darkMode}</title>
|
|
||||||
<path d="M6,17.5C6,16.672,5.328,16,4.5,16h-3C0.672,16,0,16.672,0,17.5 S0.672,19,1.5,19h3C5.328,19,6,18.328,6,17.5z M7.5,26c-0.414,0-0.789,0.168-1.061,0.439l-2,2C4.168,28.711,4,29.086,4,29.5 C4,30.328,4.671,31,5.5,31c0.414,0,0.789-0.168,1.06-0.44l2-2C8.832,28.289,9,27.914,9,27.5C9,26.672,8.329,26,7.5,26z M17.5,6 C18.329,6,19,5.328,19,4.5v-3C19,0.672,18.329,0,17.5,0S16,0.672,16,1.5v3C16,5.328,16.671,6,17.5,6z M27.5,9 c0.414,0,0.789-0.168,1.06-0.439l2-2C30.832,6.289,31,5.914,31,5.5C31,4.672,30.329,4,29.5,4c-0.414,0-0.789,0.168-1.061,0.44 l-2,2C26.168,6.711,26,7.086,26,7.5C26,8.328,26.671,9,27.5,9z M6.439,8.561C6.711,8.832,7.086,9,7.5,9C8.328,9,9,8.328,9,7.5 c0-0.414-0.168-0.789-0.439-1.061l-2-2C6.289,4.168,5.914,4,5.5,4C4.672,4,4,4.672,4,5.5c0,0.414,0.168,0.789,0.439,1.06 L6.439,8.561z M33.5,16h-3c-0.828,0-1.5,0.672-1.5,1.5s0.672,1.5,1.5,1.5h3c0.828,0,1.5-0.672,1.5-1.5S34.328,16,33.5,16z M28.561,26.439C28.289,26.168,27.914,26,27.5,26c-0.828,0-1.5,0.672-1.5,1.5c0,0.414,0.168,0.789,0.439,1.06l2,2 C28.711,30.832,29.086,31,29.5,31c0.828,0,1.5-0.672,1.5-1.5c0-0.414-0.168-0.789-0.439-1.061L28.561,26.439z M17.5,29 c-0.829,0-1.5,0.672-1.5,1.5v3c0,0.828,0.671,1.5,1.5,1.5s1.5-0.672,1.5-1.5v-3C19,29.672,18.329,29,17.5,29z M17.5,7 C11.71,7,7,11.71,7,17.5S11.71,28,17.5,28S28,23.29,28,17.5S23.29,7,17.5,7z M17.5,25c-4.136,0-7.5-3.364-7.5-7.5 c0-4.136,3.364-7.5,7.5-7.5c4.136,0,7.5,3.364,7.5,7.5C25,21.636,21.636,25,17.5,25z"></path>
|
|
||||||
</svg>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
|
||||||
version="1.1"
|
|
||||||
class="nightIcon"
|
|
||||||
x="0px"
|
|
||||||
y="0px"
|
|
||||||
viewBox="0 0 100 100"
|
|
||||||
style="enable-background:new 0 0 100 100"
|
|
||||||
xmlSpace="preserve"
|
|
||||||
aria-label={i18n(cfg.locale).components.themeToggle.lightMode}
|
|
||||||
>
|
|
||||||
<title>{i18n(cfg.locale).components.themeToggle.lightMode}</title>
|
|
||||||
<path d="M96.76,66.458c-0.853-0.852-2.15-1.064-3.23-0.534c-6.063,2.991-12.858,4.571-19.655,4.571 C62.022,70.495,50.88,65.88,42.5,57.5C29.043,44.043,25.658,23.536,34.076,6.47c0.532-1.08,0.318-2.379-0.534-3.23 c-0.851-0.852-2.15-1.064-3.23-0.534c-4.918,2.427-9.375,5.619-13.246,9.491c-9.447,9.447-14.65,22.008-14.65,35.369 c0,13.36,5.203,25.921,14.65,35.368s22.008,14.65,35.368,14.65c13.361,0,25.921-5.203,35.369-14.65 c3.872-3.871,7.064-8.328,9.491-13.246C97.826,68.608,97.611,67.309,96.76,66.458z"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Darkmode.beforeDOMLoaded = darkmodeScript
|
|
||||||
Darkmode.css = styles
|
|
||||||
|
|
||||||
export default (() => Darkmode) satisfies QuartzComponentConstructor
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
|
||||||
import style from "./styles/footer.scss"
|
|
||||||
import { version } from "../../package.json"
|
|
||||||
import { i18n } from "../i18n"
|
|
||||||
|
|
||||||
interface Options {
|
|
||||||
links: Record<string, string>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ((opts?: Options) => {
|
|
||||||
const Footer: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
|
|
||||||
const year = new Date().getFullYear()
|
|
||||||
const links = opts?.links ?? []
|
|
||||||
return (
|
|
||||||
<footer class={`${displayClass ?? ""}`}>
|
|
||||||
<p>
|
|
||||||
{i18n(cfg.locale).components.footer.createdWith}{" "}
|
|
||||||
<a href="https://quartz.jzhao.xyz/">Quartz v{version}</a> © {year}
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
{Object.entries(links).map(([text, link]) => (
|
|
||||||
<li>
|
|
||||||
<a href={link}>{text}</a>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</footer>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Footer.css = style
|
|
||||||
return Footer
|
|
||||||
}) satisfies QuartzComponentConstructor
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
import { pathToRoot } from "../util/path"
|
|
||||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
|
||||||
import { classNames } from "../util/lang"
|
|
||||||
import { i18n } from "../i18n"
|
|
||||||
|
|
||||||
const PageTitle: QuartzComponent = ({ fileData, cfg, displayClass }: QuartzComponentProps) => {
|
|
||||||
const title = cfg?.pageTitle ?? i18n(cfg.locale).propertyDefaults.title
|
|
||||||
const baseDir = pathToRoot(fileData.slug!)
|
|
||||||
return (
|
|
||||||
<h2 class={classNames(displayClass, "page-title")}>
|
|
||||||
<a href={baseDir}>{title}</a>
|
|
||||||
</h2>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
PageTitle.css = `
|
|
||||||
.page-title {
|
|
||||||
font-size: 1.75rem;
|
|
||||||
margin: 0;
|
|
||||||
font-family: var(--titleFont);
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export default (() => PageTitle) satisfies QuartzComponentConstructor
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
// @ts-ignore
|
|
||||||
import readerModeScript from "./scripts/readermode.inline"
|
|
||||||
import styles from "./styles/readermode.scss"
|
|
||||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
|
||||||
import { i18n } from "../i18n"
|
|
||||||
import { classNames } from "../util/lang"
|
|
||||||
|
|
||||||
const ReaderMode: QuartzComponent = ({ displayClass, cfg }: QuartzComponentProps) => {
|
|
||||||
return (
|
|
||||||
<button class={classNames(displayClass, "readermode")}>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
|
||||||
version="1.1"
|
|
||||||
class="readerIcon"
|
|
||||||
fill="currentColor"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="0.2"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
width="64px"
|
|
||||||
height="64px"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
aria-label={i18n(cfg.locale).components.readerMode.title}
|
|
||||||
>
|
|
||||||
<title>{i18n(cfg.locale).components.readerMode.title}</title>
|
|
||||||
<g transform="translate(-1.8, -1.8) scale(1.15, 1.2)">
|
|
||||||
<path d="M8.9891247,2.5 C10.1384702,2.5 11.2209868,2.96705384 12.0049645,3.76669482 C12.7883914,2.96705384 13.8709081,2.5 15.0202536,2.5 L18.7549359,2.5 C19.1691495,2.5 19.5049359,2.83578644 19.5049359,3.25 L19.5046891,4.004 L21.2546891,4.00457396 C21.6343849,4.00457396 21.9481801,4.28672784 21.9978425,4.6528034 L22.0046891,4.75457396 L22.0046891,20.25 C22.0046891,20.6296958 21.7225353,20.943491 21.3564597,20.9931534 L21.2546891,21 L2.75468914,21 C2.37499337,21 2.06119817,20.7178461 2.01153575,20.3517706 L2.00468914,20.25 L2.00468914,4.75457396 C2.00468914,4.37487819 2.28684302,4.061083 2.65291858,4.01142057 L2.75468914,4.00457396 L4.50368914,4.004 L4.50444233,3.25 C4.50444233,2.87030423 4.78659621,2.55650904 5.15267177,2.50684662 L5.25444233,2.5 L8.9891247,2.5 Z M4.50368914,5.504 L3.50468914,5.504 L3.50468914,19.5 L10.9478955,19.4998273 C10.4513189,18.9207296 9.73864328,18.5588115 8.96709342,18.5065584 L8.77307039,18.5 L5.25444233,18.5 C4.87474657,18.5 4.56095137,18.2178461 4.51128895,17.8517706 L4.50444233,17.75 L4.50368914,5.504 Z M19.5049359,17.75 C19.5049359,18.1642136 19.1691495,18.5 18.7549359,18.5 L15.2363079,18.5 C14.3910149,18.5 13.5994408,18.8724714 13.0614828,19.4998273 L20.5046891,19.5 L20.5046891,5.504 L19.5046891,5.504 L19.5049359,17.75 Z M18.0059359,3.999 L15.0202536,4 L14.8259077,4.00692283 C13.9889509,4.06666544 13.2254227,4.50975805 12.7549359,5.212 L12.7549359,17.777 L12.7782651,17.7601316 C13.4923805,17.2719483 14.3447024,17 15.2363079,17 L18.0059359,16.999 L18.0056891,4.798 L18.0033792,4.75457396 L18.0056891,4.71 L18.0059359,3.999 Z M8.9891247,4 L6.00368914,3.999 L6.00599909,4.75457396 L6.00599909,4.75457396 L6.00368914,4.783 L6.00368914,16.999 L8.77307039,17 C9.57551536,17 10.3461406,17.2202781 11.0128313,17.6202194 L11.2536891,17.776 L11.2536891,5.211 C10.8200889,4.56369974 10.1361548,4.13636104 9.37521067,4.02745763 L9.18347055,4.00692283 L8.9891247,4 Z" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ReaderMode.beforeDOMLoaded = readerModeScript
|
|
||||||
ReaderMode.css = styles
|
|
||||||
|
|
||||||
export default (() => ReaderMode) satisfies QuartzComponentConstructor
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
import { FullSlug, resolveRelative } from "../util/path"
|
|
||||||
import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
|
||||||
import { classNames } from "../util/lang"
|
|
||||||
|
|
||||||
const TagList: QuartzComponent = ({ fileData, displayClass }: QuartzComponentProps) => {
|
|
||||||
const tags = fileData.frontmatter?.tags
|
|
||||||
if (tags && tags.length > 0) {
|
|
||||||
return (
|
|
||||||
<ul class={classNames(displayClass, "tags")}>
|
|
||||||
{tags.map((tag) => {
|
|
||||||
const linkDest = resolveRelative(fileData.slug!, `tags/${tag}` as FullSlug)
|
|
||||||
return (
|
|
||||||
<li>
|
|
||||||
<a href={linkDest} class="internal tag-link">
|
|
||||||
{tag}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TagList.css = `
|
|
||||||
.tags {
|
|
||||||
list-style: none;
|
|
||||||
display: flex;
|
|
||||||
padding-left: 0;
|
|
||||||
gap: 0.4rem;
|
|
||||||
margin: 1rem 0;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-li > .section > .tags {
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tags > li {
|
|
||||||
display: inline-block;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin: 0;
|
|
||||||
overflow-wrap: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.internal.tag-link {
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: var(--highlight);
|
|
||||||
padding: 0.2rem 0.4rem;
|
|
||||||
margin: 0 0.1rem;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export default (() => TagList) satisfies QuartzComponentConstructor
|
|
||||||
@ -2,18 +2,11 @@ import Content from "./pages/Content"
|
|||||||
import TagContent from "./pages/TagContent"
|
import TagContent from "./pages/TagContent"
|
||||||
import FolderContent from "./pages/FolderContent"
|
import FolderContent from "./pages/FolderContent"
|
||||||
import NotFound from "./pages/404"
|
import NotFound from "./pages/404"
|
||||||
import ArticleTitle from "./ArticleTitle"
|
|
||||||
import Darkmode from "./Darkmode"
|
|
||||||
import ReaderMode from "./ReaderMode"
|
|
||||||
import Head from "./Head"
|
import Head from "./Head"
|
||||||
import PageTitle from "./PageTitle"
|
|
||||||
import ContentMeta from "./ContentMeta"
|
|
||||||
import Spacer from "./Spacer"
|
import Spacer from "./Spacer"
|
||||||
import TableOfContents from "./TableOfContents"
|
import TableOfContents from "./TableOfContents"
|
||||||
import TagList from "./TagList"
|
|
||||||
import Backlinks from "./Backlinks"
|
import Backlinks from "./Backlinks"
|
||||||
import Search from "./Search"
|
import Search from "./Search"
|
||||||
import Footer from "./Footer"
|
|
||||||
import DesktopOnly from "./DesktopOnly"
|
import DesktopOnly from "./DesktopOnly"
|
||||||
import MobileOnly from "./MobileOnly"
|
import MobileOnly from "./MobileOnly"
|
||||||
import RecentNotes from "./RecentNotes"
|
import RecentNotes from "./RecentNotes"
|
||||||
@ -28,21 +21,14 @@ export type { ComponentManifest, RegisteredComponent } from "./registry"
|
|||||||
export type { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
export type { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types"
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ArticleTitle,
|
|
||||||
Content,
|
Content,
|
||||||
TagContent,
|
TagContent,
|
||||||
FolderContent,
|
FolderContent,
|
||||||
Darkmode,
|
|
||||||
ReaderMode,
|
|
||||||
Head,
|
Head,
|
||||||
PageTitle,
|
|
||||||
ContentMeta,
|
|
||||||
Spacer,
|
Spacer,
|
||||||
TableOfContents,
|
TableOfContents,
|
||||||
TagList,
|
|
||||||
Backlinks,
|
Backlinks,
|
||||||
Search,
|
Search,
|
||||||
Footer,
|
|
||||||
DesktopOnly,
|
DesktopOnly,
|
||||||
MobileOnly,
|
MobileOnly,
|
||||||
RecentNotes,
|
RecentNotes,
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
const userPref = window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"
|
|
||||||
const currentTheme = localStorage.getItem("theme") ?? userPref
|
|
||||||
document.documentElement.setAttribute("saved-theme", currentTheme)
|
|
||||||
|
|
||||||
const emitThemeChangeEvent = (theme: "light" | "dark") => {
|
|
||||||
const event: CustomEventMap["themechange"] = new CustomEvent("themechange", {
|
|
||||||
detail: { theme },
|
|
||||||
})
|
|
||||||
document.dispatchEvent(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("nav", () => {
|
|
||||||
const switchTheme = () => {
|
|
||||||
const newTheme =
|
|
||||||
document.documentElement.getAttribute("saved-theme") === "dark" ? "light" : "dark"
|
|
||||||
document.documentElement.setAttribute("saved-theme", newTheme)
|
|
||||||
localStorage.setItem("theme", newTheme)
|
|
||||||
emitThemeChangeEvent(newTheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
const themeChange = (e: MediaQueryListEvent) => {
|
|
||||||
const newTheme = e.matches ? "dark" : "light"
|
|
||||||
document.documentElement.setAttribute("saved-theme", newTheme)
|
|
||||||
localStorage.setItem("theme", newTheme)
|
|
||||||
emitThemeChangeEvent(newTheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const darkmodeButton of document.getElementsByClassName("darkmode")) {
|
|
||||||
darkmodeButton.addEventListener("click", switchTheme)
|
|
||||||
window.addCleanup(() => darkmodeButton.removeEventListener("click", switchTheme))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen for changes in prefers-color-scheme
|
|
||||||
const colorSchemeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)")
|
|
||||||
colorSchemeMediaQuery.addEventListener("change", themeChange)
|
|
||||||
window.addCleanup(() => colorSchemeMediaQuery.removeEventListener("change", themeChange))
|
|
||||||
})
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
let isReaderMode = false
|
|
||||||
|
|
||||||
const emitReaderModeChangeEvent = (mode: "on" | "off") => {
|
|
||||||
const event: CustomEventMap["readermodechange"] = new CustomEvent("readermodechange", {
|
|
||||||
detail: { mode },
|
|
||||||
})
|
|
||||||
document.dispatchEvent(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("nav", () => {
|
|
||||||
const switchReaderMode = () => {
|
|
||||||
isReaderMode = !isReaderMode
|
|
||||||
const newMode = isReaderMode ? "on" : "off"
|
|
||||||
document.documentElement.setAttribute("reader-mode", newMode)
|
|
||||||
emitReaderModeChangeEvent(newMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const readerModeButton of document.getElementsByClassName("readermode")) {
|
|
||||||
readerModeButton.addEventListener("click", switchReaderMode)
|
|
||||||
window.addCleanup(() => readerModeButton.removeEventListener("click", switchReaderMode))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set initial state
|
|
||||||
document.documentElement.setAttribute("reader-mode", isReaderMode ? "on" : "off")
|
|
||||||
})
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
.content-meta {
|
|
||||||
margin-top: 0;
|
|
||||||
color: var(--darkgray);
|
|
||||||
|
|
||||||
&[show-comma="true"] {
|
|
||||||
> *:not(:last-child) {
|
|
||||||
margin-right: 8px;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: ",";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
.darkmode {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0;
|
|
||||||
position: relative;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
width: 20px;
|
|
||||||
height: 32px;
|
|
||||||
margin: 0;
|
|
||||||
text-align: inherit;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
& svg {
|
|
||||||
position: absolute;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
top: calc(50% - 10px);
|
|
||||||
fill: var(--darkgray);
|
|
||||||
transition: opacity 0.1s ease;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:root[saved-theme="dark"] {
|
|
||||||
color-scheme: dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root[saved-theme="light"] {
|
|
||||||
color-scheme: light;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root[saved-theme="dark"] .darkmode {
|
|
||||||
& > .dayIcon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
& > .nightIcon {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:root .darkmode {
|
|
||||||
& > .dayIcon {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
& > .nightIcon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
footer {
|
|
||||||
text-align: left;
|
|
||||||
margin-bottom: 4rem;
|
|
||||||
opacity: 0.7;
|
|
||||||
|
|
||||||
& ul {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: 1rem;
|
|
||||||
margin-top: -1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
.readermode {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0;
|
|
||||||
position: relative;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
width: 20px;
|
|
||||||
height: 32px;
|
|
||||||
margin: 0;
|
|
||||||
text-align: inherit;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
& svg {
|
|
||||||
position: absolute;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
top: calc(50% - 10px);
|
|
||||||
fill: var(--darkgray);
|
|
||||||
stroke: var(--darkgray);
|
|
||||||
transition: opacity 0.1s ease;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:root[reader-mode="on"] {
|
|
||||||
& .sidebar.left,
|
|
||||||
& .sidebar.right {
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.2s ease;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user