Compare commits

...

4 Commits

Author SHA1 Message Date
Emile Bangma
580c1bd608
fix(typography): properly pass Google font options (#1825)
Some checks are pending
Build and Test / build-and-test (macos-latest) (push) Waiting to run
Build and Test / build-and-test (windows-latest) (push) Waiting to run
Build and Test / build-and-test (ubuntu-latest) (push) Has been skipped
Build and Test / publish-tag (push) Has been skipped
2025-03-12 11:27:41 -07:00
Jacky Zhao
270a5dc14a fix(explorer): show file name instead of slug if no file data (closes #1822) 2025-03-12 11:24:28 -07:00
Jacky Zhao
bfa938cc62 fix(explorer): allow setting displayName (closes #1824) 2025-03-12 10:42:07 -07:00
Jacky Zhao
e3c50caf13 fix(explorer): dont invert mobile css, properly toggle .collapsed 2025-03-12 10:15:54 -07:00
8 changed files with 127 additions and 27 deletions

View File

@ -108,3 +108,25 @@ Some plugins are included by default in the [`quartz.config.ts`](https://github.
You can see a list of all plugins and their configuration options [[tags/plugin|here]].
If you'd like to make your own plugins, see the [[making plugins|making custom plugins]] guide.
## Fonts
Fonts can be specified as a `string` or a `FontSpecification`:
```ts
// string
typography: {
header: "Schibsted Grotesk",
...
}
// FontSpecification
typography: {
header: {
name: "Schibsted Grotesk",
weights: [400, 700],
includeItalic: true,
},
...
}
```

View File

@ -49,8 +49,15 @@ export const defaultListPageLayout: PageLayout = {
left: [
Component.PageTitle(),
Component.MobileOnly(Component.Spacer()),
Component.Search(),
Component.Darkmode(),
Component.Flex({
components: [
{
Component: Component.Search(),
grow: true,
},
{ Component: Component.Darkmode() },
],
}),
Component.Explorer(),
],
right: [],

View File

@ -259,15 +259,17 @@ document.addEventListener("nav", async (e: CustomEventMap["nav"]) => {
await setupExplorer(currentSlug)
// if mobile hamburger is visible, collapse by default
for (const explorer of document.getElementsByClassName("mobile-explorer")) {
if (explorer.checkVisibility()) {
for (const explorer of document.getElementsByClassName("explorer")) {
const mobileExplorer = explorer.querySelector(".mobile-explorer")
if (!mobileExplorer) return
if (mobileExplorer.checkVisibility()) {
explorer.classList.add("collapsed")
explorer.setAttribute("aria-expanded", "false")
}
}
const hiddenUntilDoneLoading = document.querySelector(".mobile-explorer")
hiddenUntilDoneLoading?.classList.remove("hide-until-loaded")
mobileExplorer.classList.remove("hide-until-loaded")
}
})
function setFolderState(folderElement: HTMLElement, collapsed: boolean) {

View File

@ -212,8 +212,8 @@ li:has(> .folder-outer:not(.open)) > .folder-container > svg {
flex: 0 0 34px;
& > .explorer-content {
transform: translateX(0);
visibility: visible;
transform: translateX(-100vw);
visibility: hidden;
}
}
@ -221,8 +221,8 @@ li:has(> .folder-outer:not(.open)) > .folder-container > svg {
flex: 0 0 34px;
& > .explorer-content {
transform: translateX(-100vw);
visibility: hidden;
transform: translateX(0);
visibility: visible;
}
}
@ -236,7 +236,7 @@ li:has(> .folder-outer:not(.open)) > .folder-container > svg {
background-color: var(--light);
max-width: 100vw;
width: 100%;
transform: translateX(0);
transform: translateX(-100vw);
transition:
transform 200ms ease,
visibility 200ms ease;

View File

@ -12,6 +12,7 @@ import DepGraph from "../../depgraph"
export type ContentIndexMap = Map<FullSlug, ContentDetails>
export type ContentDetails = {
slug: FullSlug
filePath: FilePath
title: string
links: SimpleSlug[]
tags: string[]
@ -126,6 +127,7 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
if (opts?.includeEmptyFiles || (file.data.text && file.data.text !== "")) {
linkIndex.set(slug, {
slug,
filePath: file.data.filePath!,
title: file.data.frontmatter?.title!,
links: file.data.links ?? [],
tags: file.data.frontmatter?.tags ?? [],

View File

@ -5,6 +5,7 @@ import { FileTrieNode } from "./fileTrie"
interface TestData {
title: string
slug: string
filePath: string
}
describe("FileTrie", () => {
@ -26,11 +27,24 @@ describe("FileTrie", () => {
const data = {
title: "Test Title",
slug: "test",
filePath: "test.md",
}
trie.add(data)
assert.strictEqual(trie.children[0].displayName, "Test Title")
})
test("should be able to set displayName", () => {
const data = {
title: "Test Title",
slug: "test",
filePath: "test.md",
}
trie.add(data)
trie.children[0].displayName = "Modified"
assert.strictEqual(trie.children[0].displayName, "Modified")
})
})
describe("add", () => {
@ -38,6 +52,7 @@ describe("FileTrie", () => {
const data = {
title: "Test",
slug: "test",
filePath: "test.md",
}
trie.add(data)
@ -50,6 +65,7 @@ describe("FileTrie", () => {
const data = {
title: "Index",
slug: "index",
filePath: "index.md",
}
trie.add(data)
@ -61,11 +77,13 @@ describe("FileTrie", () => {
const data1 = {
title: "Nested",
slug: "folder/test",
filePath: "folder/test.md",
}
const data2 = {
title: "Really nested index",
slug: "a/b/c/index",
filePath: "a/b/c/index.md",
}
trie.add(data1)
@ -92,8 +110,8 @@ describe("FileTrie", () => {
describe("filter", () => {
test("should filter nodes based on condition", () => {
const data1 = { title: "Test1", slug: "test1" }
const data2 = { title: "Test2", slug: "test2" }
const data1 = { title: "Test1", slug: "test1", filePath: "test1.md" }
const data2 = { title: "Test2", slug: "test2", filePath: "test2.md" }
trie.add(data1)
trie.add(data2)
@ -106,8 +124,8 @@ describe("FileTrie", () => {
describe("map", () => {
test("should apply function to all nodes", () => {
const data1 = { title: "Test1", slug: "test1" }
const data2 = { title: "Test2", slug: "test2" }
const data1 = { title: "Test1", slug: "test1", filePath: "test1.md" }
const data2 = { title: "Test2", slug: "test2", filePath: "test2.md" }
trie.add(data1)
trie.add(data2)
@ -121,12 +139,41 @@ describe("FileTrie", () => {
assert.strictEqual(trie.children[0].displayName, "Modified")
assert.strictEqual(trie.children[1].displayName, "Modified")
})
test("map over folders should work", () => {
const data1 = { title: "Test1", slug: "test1", filePath: "test1.md" }
const data2 = {
title: "Test2",
slug: "a/b-with-space/test2",
filePath: "a/b with space/test2.md",
}
trie.add(data1)
trie.add(data2)
trie.map((node) => {
if (node.isFolder) {
node.displayName = `Folder: ${node.displayName}`
} else {
node.displayName = `File: ${node.displayName}`
}
})
assert.strictEqual(trie.children[0].displayName, "File: Test1")
assert.strictEqual(trie.children[1].displayName, "Folder: a")
assert.strictEqual(trie.children[1].children[0].displayName, "Folder: b with space")
assert.strictEqual(trie.children[1].children[0].children[0].displayName, "File: Test2")
})
})
describe("entries", () => {
test("should return all entries", () => {
const data1 = { title: "Test1", slug: "test1" }
const data2 = { title: "Test2", slug: "a/b/test2" }
const data1 = { title: "Test1", slug: "test1", filePath: "test1.md" }
const data2 = {
title: "Test2",
slug: "a/b-with-space/test2",
filePath: "a/b with space/test2.md",
}
trie.add(data1)
trie.add(data2)
@ -138,8 +185,8 @@ describe("FileTrie", () => {
["index", trie.data],
["test1", data1],
["a/index", null],
["a/b/index", null],
["a/b/test2", data2],
["a/b-with-space/index", null],
["a/b-with-space/test2", data2],
],
)
})
@ -150,14 +197,17 @@ describe("FileTrie", () => {
const data1 = {
title: "Root",
slug: "index",
filePath: "index.md",
}
const data2 = {
title: "Test",
slug: "folder/subfolder/test",
filePath: "folder/subfolder/test.md",
}
const data3 = {
title: "Folder Index",
slug: "abc/index",
filePath: "abc/index.md",
}
trie.add(data1)
@ -176,9 +226,9 @@ describe("FileTrie", () => {
describe("sort", () => {
test("should sort nodes according to sort function", () => {
const data1 = { title: "A", slug: "a" }
const data2 = { title: "B", slug: "b" }
const data3 = { title: "C", slug: "c" }
const data1 = { title: "A", slug: "a", filePath: "a.md" }
const data2 = { title: "B", slug: "b", filePath: "b.md" }
const data3 = { title: "C", slug: "c", filePath: "c.md" }
trie.add(data3)
trie.add(data1)

View File

@ -4,6 +4,7 @@ import { FullSlug, joinSegments } from "./path"
interface FileTrieData {
slug: string
title: string
filePath: string
}
export class FileTrieNode<T extends FileTrieData = ContentDetails> {
@ -11,6 +12,11 @@ export class FileTrieNode<T extends FileTrieData = ContentDetails> {
children: Array<FileTrieNode<T>>
private slugSegments: string[]
// prefer showing the file path segment over the slug segment
// so that folders that dont have index files can be shown as is
// without dashes in the slug
private fileSegmentHint?: string
private displayNameOverride?: string
data: T | null
constructor(segments: string[], data?: T) {
@ -18,10 +24,18 @@ export class FileTrieNode<T extends FileTrieData = ContentDetails> {
this.slugSegments = segments
this.data = data ?? null
this.isFolder = false
this.displayNameOverride = undefined
}
get displayName(): string {
return this.data?.title ?? this.slugSegment ?? ""
const nonIndexTitle = this.data?.title === "index" ? undefined : this.data?.title
return (
this.displayNameOverride ?? nonIndexTitle ?? this.fileSegmentHint ?? this.slugSegment ?? ""
)
}
set displayName(name: string) {
this.displayNameOverride = name
}
get slug(): FullSlug {
@ -63,6 +77,9 @@ export class FileTrieNode<T extends FileTrieData = ContentDetails> {
// recursive case, we are not at the end of the path
const child =
this.children.find((c) => c.slugSegment === segment) ?? this.makeChild(path, undefined)
const fileParts = file.filePath.split("/")
child.fileSegmentHint = fileParts.at(-path.length)
child.insert(path.slice(1), file)
}
}

View File

@ -105,9 +105,9 @@ ${stylesheet.join("\n\n")}
--highlight: ${theme.colors.lightMode.highlight};
--textHighlight: ${theme.colors.lightMode.textHighlight};
--headerFont: "${theme.typography.header}", ${DEFAULT_SANS_SERIF};
--bodyFont: "${theme.typography.body}", ${DEFAULT_SANS_SERIF};
--codeFont: "${theme.typography.code}", ${DEFAULT_MONO};
--headerFont: "${getFontSpecificationName(theme.typography.header)}", ${DEFAULT_SANS_SERIF};
--bodyFont: "${getFontSpecificationName(theme.typography.body)}", ${DEFAULT_SANS_SERIF};
--codeFont: "${getFontSpecificationName(theme.typography.code)}", ${DEFAULT_MONO};
}
:root[saved-theme="dark"] {