mirror of
https://github.com/alrayyes/wiki.git
synced 2025-05-11 03:04:21 +00:00
scss support
This commit is contained in:
parent
c1c46ad67e
commit
42d3a7de17
15 changed files with 574 additions and 99 deletions
quartz
|
@ -5,6 +5,7 @@ import { hideBin } from 'yargs/helpers'
|
|||
import esbuild from 'esbuild'
|
||||
import chalk from 'chalk'
|
||||
import requireFromString from 'require-from-string'
|
||||
import { sassPlugin } from 'esbuild-sass-plugin'
|
||||
|
||||
const fp = "./quartz.config.ts"
|
||||
const { version } = JSON.parse(readFileSync("./package.json").toString())
|
||||
|
@ -59,7 +60,10 @@ yargs(hideBin(process.argv))
|
|||
format: "cjs",
|
||||
jsx: "automatic",
|
||||
jsxImportSource: "preact",
|
||||
external: ["@napi-rs/simple-git"]
|
||||
external: ["@napi-rs/simple-git"],
|
||||
plugins: [sassPlugin({
|
||||
type: 'css-text'
|
||||
})]
|
||||
}).catch(err => {
|
||||
console.error(`${chalk.red("Couldn't parse Quartz configuration:")} ${fp}`)
|
||||
console.log(`Reason: ${chalk.grey(err)}`)
|
||||
|
|
|
@ -1,34 +1,16 @@
|
|||
import { PluginTypes } from "./plugins/types"
|
||||
import { Theme } from "./theme"
|
||||
|
||||
export interface ColorScheme {
|
||||
light: string,
|
||||
lightgray: string,
|
||||
gray: string,
|
||||
darkgray: string,
|
||||
dark: string,
|
||||
secondary: string,
|
||||
tertiary: string,
|
||||
highlight: string
|
||||
export interface GlobalConfiguration {
|
||||
siteTitle: string,
|
||||
/** Whether to enable single-page-app style rendering. this prevents flashes of unstyled content and improves smoothness of Quartz */
|
||||
enableSPA: boolean,
|
||||
/** Glob patterns to not search */
|
||||
ignorePatterns: string[],
|
||||
theme: Theme
|
||||
}
|
||||
|
||||
export interface QuartzConfig {
|
||||
configuration: {
|
||||
siteTitle: string,
|
||||
/** Whether to enable single-page-app style rendering. this prevents flashes of unstyled content and improves smoothness of Quartz */
|
||||
enableSPA: boolean,
|
||||
/** Glob patterns to not search */
|
||||
ignorePatterns: string[],
|
||||
},
|
||||
configuration: GlobalConfiguration,
|
||||
plugins: PluginTypes,
|
||||
theme: {
|
||||
typography: {
|
||||
header: string,
|
||||
body: string,
|
||||
code: string
|
||||
},
|
||||
colors: {
|
||||
lightMode: ColorScheme,
|
||||
darkMode: ColorScheme
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ export interface HeadProps {
|
|||
export default function({ title, description, slug, externalResources }: HeadProps) {
|
||||
const { css, js } = externalResources
|
||||
const baseDir = resolveToRoot(slug)
|
||||
const stylePath = baseDir + "/index.css"
|
||||
const iconPath = baseDir + "/static/icon.png"
|
||||
const ogImagePath = baseDir + "/static/og-image.png"
|
||||
return <head>
|
||||
|
@ -26,6 +27,9 @@ export default function({ title, description, slug, externalResources }: HeadPro
|
|||
<meta name="description" content={description} />
|
||||
<meta name="generator" content="Quartz" />
|
||||
<base href={slug} />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||
<link rel="stylesheet" type="text/css" href={stylePath} />
|
||||
{css.map(href => <link key={href} href={href} rel="stylesheet" type="text/css" />)}
|
||||
{js.filter(resource => resource.loadTime === "beforeDOMReady").map(resource => <script key={resource.src} src={resource.src} />)}
|
||||
</head>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
|
||||
import { resolveToRoot } from "../../path"
|
||||
import { StaticResources } from "../../resources"
|
||||
import { EmitCallback, QuartzEmitterPlugin } from "../types"
|
||||
import { ProcessedContent } from "../vfile"
|
||||
|
@ -8,6 +7,10 @@ import { render } from "preact-render-to-string"
|
|||
import { ComponentType } from "preact"
|
||||
import { HeadProps } from "../../components/Head"
|
||||
|
||||
import styles from '../../styles/base.scss'
|
||||
import { googleFontHref, templateThemeStyles } from "../../theme"
|
||||
import { GlobalConfiguration } from "../../cfg"
|
||||
|
||||
interface Options {
|
||||
Head: ComponentType<HeadProps>
|
||||
}
|
||||
|
@ -21,8 +24,18 @@ export class ContentPage extends QuartzEmitterPlugin {
|
|||
this.opts = opts
|
||||
}
|
||||
|
||||
async emit(content: ProcessedContent[], resources: StaticResources, emit: EmitCallback): Promise<string[]> {
|
||||
async emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emit: EmitCallback): Promise<string[]> {
|
||||
const fps: string[] = []
|
||||
|
||||
// emit styles
|
||||
emit({
|
||||
slug: "index",
|
||||
ext: ".css",
|
||||
content: templateThemeStyles(cfg.theme, styles)
|
||||
})
|
||||
fps.push("index.css")
|
||||
resources.css.push(googleFontHref(cfg.theme))
|
||||
|
||||
for (const [tree, file] of content) {
|
||||
|
||||
// @ts-ignore (preact makes it angry)
|
||||
|
@ -36,7 +49,7 @@ export class ContentPage extends QuartzEmitterPlugin {
|
|||
slug={file.data.slug!}
|
||||
externalResources={resources} />
|
||||
<body>
|
||||
<div id="quartz-root">
|
||||
<div id="quartz-root" class="page">
|
||||
<header>
|
||||
<h1>{file.data.frontmatter?.title}</h1>
|
||||
</header>
|
||||
|
|
|
@ -3,3 +3,5 @@ export { GitHubFlavoredMarkdown } from './gfm'
|
|||
export { CreatedModifiedDate } from './lastmod'
|
||||
export { Katex } from './latex'
|
||||
export { Description } from './description'
|
||||
export { ResolveLinks } from './links'
|
||||
export { ObsidianFlavoredMarkdown } from './ofm'
|
||||
|
|
|
@ -38,17 +38,21 @@ export class ObsidianFlavoredMarkdown extends QuartzTransformerPlugin {
|
|||
const backlinkRegex = new RegExp(/!?\[\[([^\[\]\|\#]+)(#[^\[\]\|\#]+)?(\|[^\[\]\|\#]+)?\]\]/, "g")
|
||||
return (tree: Root, _file) => {
|
||||
findAndReplace(tree, backlinkRegex, (value: string, ...capture: string[]) => {
|
||||
const [path, rawHeader, rawAlias] = capture
|
||||
const header = rawHeader?.slice(1).trim() ?? ""
|
||||
const alias = rawAlias?.slice(1).trim() ?? value
|
||||
const url = slugify(path.trim() + header)
|
||||
return {
|
||||
type: 'link',
|
||||
url,
|
||||
children: [{
|
||||
type: 'text',
|
||||
value: alias
|
||||
}]
|
||||
if (value.startsWith("!")) {
|
||||
|
||||
} else {
|
||||
const [path, rawHeader, rawAlias] = capture
|
||||
const header = rawHeader?.slice(1).trim() ?? ""
|
||||
const alias = rawAlias?.slice(1).trim() ?? path
|
||||
const url = slugify(path.trim() + header)
|
||||
return {
|
||||
type: 'link',
|
||||
url,
|
||||
children: [{
|
||||
type: 'text',
|
||||
value: alias
|
||||
}]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { PluggableList } from "unified"
|
||||
import { StaticResources } from "../resources"
|
||||
import { ProcessedContent } from "./vfile"
|
||||
import { GlobalConfiguration } from "../cfg"
|
||||
|
||||
export abstract class QuartzTransformerPlugin {
|
||||
abstract name: string
|
||||
|
@ -23,7 +24,7 @@ export interface EmitOptions {
|
|||
export type EmitCallback = (data: EmitOptions) => Promise<string>
|
||||
export abstract class QuartzEmitterPlugin {
|
||||
abstract name: string
|
||||
abstract emit(content: ProcessedContent[], resources: StaticResources, emitCallback: EmitCallback): Promise<string[]>
|
||||
abstract emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emitCallback: EmitCallback): Promise<string[]>
|
||||
}
|
||||
|
||||
export interface PluginTypes {
|
||||
|
|
|
@ -23,7 +23,7 @@ export async function emitContent(contentFolder: string, output: string, cfg: Qu
|
|||
|
||||
let emittedFiles = 0
|
||||
for (const emitter of cfg.plugins.emitters) {
|
||||
const emitted = await emitter.emit(content, staticResources, emit)
|
||||
const emitted = await emitter.emit(cfg.configuration, content, staticResources, emit)
|
||||
emittedFiles += emitted.length
|
||||
|
||||
if (verbose) {
|
||||
|
|
207
quartz/styles/base.scss
Normal file
207
quartz/styles/base.scss
Normal file
|
@ -0,0 +1,207 @@
|
|||
@import "./syntax.scss";
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
& footer > p {
|
||||
text-align: center !important;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--light);
|
||||
font-family: var(--bodyFont);
|
||||
}
|
||||
|
||||
.text-highlight {
|
||||
background-color: #fff236aa;
|
||||
padding: 0 0.1rem;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
p, ul, text, a, tr, td, li, ol, ul {
|
||||
color: var(--darkgray);
|
||||
fill: var(--darkgray);
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s ease;
|
||||
color: var(--secondary);
|
||||
|
||||
&:hover {
|
||||
color: var(--tertiary) !important;
|
||||
}
|
||||
|
||||
&.internal {
|
||||
text-decoration: none;
|
||||
background-color: var(--highlight);
|
||||
padding: 0 0.1rem;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.page {
|
||||
padding: 4rem 30vw;
|
||||
margin: 0 auto;
|
||||
max-width: 1000px;
|
||||
@media all and (max-width: 1200px) {
|
||||
padding: 25px 5vw;
|
||||
}
|
||||
|
||||
& p {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
& article {
|
||||
& > h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
font-style: italic;
|
||||
margin-left: 0;
|
||||
border-left: 3px solid var(--secondary);
|
||||
padding-left: 1rem;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
thead {
|
||||
font-family: var(--headerFont);
|
||||
color: var(--dark);
|
||||
font-weight: revert;
|
||||
margin: 2rem 0 0;
|
||||
|
||||
&:hover > .hanchor {
|
||||
color: var(--secondary);
|
||||
}
|
||||
|
||||
article > & > a {
|
||||
color: var(--dark)
|
||||
}
|
||||
}
|
||||
|
||||
div[data-rehype-pretty-code-fragment] {
|
||||
line-height: 1.5rem;
|
||||
position: relative;
|
||||
|
||||
& > div[data-rehype-pretty-code-title] {
|
||||
font-family: var(--codeFont);
|
||||
font-size: 0.9rem;
|
||||
padding: 0.1rem 0.8rem;
|
||||
border: 1px solid var(--lightgray);
|
||||
width: max-content;
|
||||
border-radius: 5px;
|
||||
margin-bottom: -0.8rem;
|
||||
color: var(--darkgray);
|
||||
}
|
||||
|
||||
& pre {
|
||||
font-family: var(--codeFont);
|
||||
padding: 0.5rem 0;
|
||||
border-radius: 5px;
|
||||
overflow-x: scroll;
|
||||
border: 1px solid var(--lightgray);
|
||||
|
||||
& > code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
font-size: 0.9rem;
|
||||
counter-reset: line;
|
||||
counter-increment: line 0;
|
||||
display: grid;
|
||||
|
||||
& .line {
|
||||
padding: 0 0.75rem;
|
||||
box-sizing: border-box;
|
||||
border-left: 3px solid transparent;
|
||||
|
||||
&.highlighted {
|
||||
background-color: var(--highlight);
|
||||
border-left: 3px solid var(--secondary);
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: counter(line);
|
||||
counter-increment: line;
|
||||
width: 1rem;
|
||||
margin-right: 1rem;
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
color: rgba(115, 138, 148, 0.4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.9em;
|
||||
font-family: var(--codeFont);
|
||||
border-radius: 5px;
|
||||
padding: 0.1rem 0.2rem;
|
||||
background: var(--lightgray);
|
||||
}
|
||||
|
||||
tbody, li, p {
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 2px solid var(--gray);
|
||||
width: 100%;
|
||||
padding: 1.5rem;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
td, th {
|
||||
padding: 0.2rem 1rem;
|
||||
border: 2px solid var(--gray);
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
border-radius: 5px;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
p > img + em {
|
||||
display: block;
|
||||
transform: translateY(-1rem);
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
margin: 2rem auto;
|
||||
height: 1px;
|
||||
border: none;
|
||||
background-color: var(--lightgray);
|
||||
}
|
||||
|
||||
section {
|
||||
margin: 2rem auto;
|
||||
border-top: 1px solid var(--lightgray);
|
||||
|
||||
& > #footnote-label {
|
||||
& > a {
|
||||
color: var(--dark);
|
||||
}
|
||||
}
|
||||
|
||||
& ol, & ul {
|
||||
padding: 0 1em
|
||||
}
|
||||
}
|
29
quartz/styles/syntax.scss
Normal file
29
quartz/styles/syntax.scss
Normal file
|
@ -0,0 +1,29 @@
|
|||
// npx convert-sh-theme https://raw.githubusercontent.com/shikijs/shiki/main/packages/shiki/themes/github-light.json
|
||||
body {
|
||||
--shiki-color-text: #24292e;
|
||||
--shiki-color-background: #f8f8f8;
|
||||
--shiki-token-constant: #005cc5;
|
||||
--shiki-token-string: #032f62;
|
||||
--shiki-token-comment: #6a737d;
|
||||
--shiki-token-keyword: #d73a49;
|
||||
--shiki-token-parameter: #24292e;
|
||||
--shiki-token-function: #24292e;
|
||||
--shiki-token-string-expression: #22863a;
|
||||
--shiki-token-punctuation: #24292e;
|
||||
--shiki-token-link: #24292e;
|
||||
}
|
||||
|
||||
// npx convert-sh-theme https://raw.githubusercontent.com/shikijs/shiki/main/packages/shiki/themes/github-dark.json
|
||||
:root[saved-theme="dark"] body {
|
||||
--shiki-color-text: #e1e4e8 !important;
|
||||
--shiki-color-background: #24292e !important;
|
||||
--shiki-token-constant: #79b8ff !important;
|
||||
--shiki-token-string: #9ecbff !important;
|
||||
--shiki-token-comment: #6a737d !important;
|
||||
--shiki-token-keyword: #f97583 !important;
|
||||
--shiki-token-parameter: #e1e4e8 !important;
|
||||
--shiki-token-function: #e1e4e8 !important;
|
||||
--shiki-token-string-expression: #85e89d !important;
|
||||
--shiki-token-punctuation: #e1e4e8 !important;
|
||||
--shiki-token-link: #e1e4e8 !important;
|
||||
}
|
59
quartz/theme.ts
Normal file
59
quartz/theme.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
export interface ColorScheme {
|
||||
light: string,
|
||||
lightgray: string,
|
||||
gray: string,
|
||||
darkgray: string,
|
||||
dark: string,
|
||||
secondary: string,
|
||||
tertiary: string,
|
||||
highlight: string
|
||||
}
|
||||
|
||||
export interface Theme {
|
||||
typography: {
|
||||
header: string,
|
||||
body: string,
|
||||
code: string
|
||||
},
|
||||
colors: {
|
||||
lightMode: ColorScheme,
|
||||
darkMode: ColorScheme
|
||||
}
|
||||
}
|
||||
|
||||
export function googleFontHref(theme: Theme) {
|
||||
const { code, header, body } = theme.typography
|
||||
return `https://fonts.googleapis.com/css2?family=${code}&family=${header}:wght@400;700&family=${body}:ital,wght@0,400;0,600;1,400;1,600&display=swap`
|
||||
}
|
||||
|
||||
export function templateThemeStyles(theme: Theme, stylesheet: string) {
|
||||
return `
|
||||
:root {
|
||||
--light: ${theme.colors.lightMode.light};
|
||||
--lightgray: ${theme.colors.lightMode.lightgray};
|
||||
--gray: ${theme.colors.lightMode.gray};
|
||||
--darkgray: ${theme.colors.lightMode.darkgray};
|
||||
--dark: ${theme.colors.lightMode.dark};
|
||||
--secondary: ${theme.colors.lightMode.secondary};
|
||||
--tertiary: ${theme.colors.lightMode.tertiary};
|
||||
--highlight: ${theme.colors.lightMode.highlight};
|
||||
|
||||
--headerFont: ${theme.typography.header};
|
||||
--bodyFont: ${theme.typography.body};
|
||||
--codeFont: ${theme.typography.code};
|
||||
}
|
||||
|
||||
:root[saved-theme="dark"] {
|
||||
--light: ${theme.colors.darkMode.light};
|
||||
--lightgray: ${theme.colors.darkMode.lightgray};
|
||||
--gray: ${theme.colors.darkMode.gray};
|
||||
--darkgray: ${theme.colors.darkMode.darkgray};
|
||||
--dark: ${theme.colors.darkMode.dark};
|
||||
--secondary: ${theme.colors.darkMode.secondary};
|
||||
--tertiary: ${theme.colors.darkMode.tertiary};
|
||||
--highlight: ${theme.colors.darkMode.highlight};
|
||||
}
|
||||
|
||||
${stylesheet}
|
||||
`
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue