summary:
- Nested folder/file explorer for site navigation.
- Customizable.
Info
status:
- Community plugin. purpose:
- External-plugin reference implementation.
Installation
command:
npm install github:quartz-community/explorer --legacy-peer-depsconfig:
plugins:
- source: github:quartz-community/explorer
enabled: true
layout:
position: left
priority: 50Features
navigation:
- Shows all folders/files.
- Reposition via layout. display_names:
- Primary:
titleinfolder/index.md; see Authoring Content. - Fallback: folder name.
Info
state:
- Saved to local storage key
fileTree. clear:- Use browser devtools. disable:
- Set
useSavedState: false.
Customization
yaml_options:
- Configure serializable
Explorer()options inquartz.config.yaml:
plugins:
- source: github:quartz-community/explorer
enabled: true
options:
title: Explorer
folderClickBehavior: collapse # "link" = navigate; "collapse" = toggle
folderDefaultState: collapsed # "collapsed" | "open"
useSavedState: true
layout:
position: left
priority: 50ts_overrides:
- Configure callback options in
quartz.ts:
import { loadQuartzConfig, loadQuartzLayout } from "./quartz/plugins/loader/config-loader"
import * as ExternalPlugin from "./.quartz/plugins"
// Advanced: callback functions cannot be expressed in YAML
ExternalPlugin.Explorer({
sortFn: (a, b) => {
/* ... */
},
filterFn: (node) => {
/* ... */
},
mapFn: (node) => {
/* ... */
},
order: ["filter", "map", "sort"],
})
const config = await loadQuartzConfig()
export default config
export const layout = await loadQuartzLayout()How overrides work
merge:
quartz.tsoverrides merge with YAML config during build. precedence:- defaults < YAML < TS overrides name_conflicts:
- Use
pluginsmap:quartz.ts import * as ExternalPlugin from "./.quartz/plugins" ExternalPlugin.plugins["my-explorer"].Explorer({ mapFn: ... })
disable:
- Remove from
quartz.config.yamlor setenabled: false. - Optional: move Table of Contents to
leftlayout. behavior_logic: - See Advanced customization.
Advanced customization
callbacks:
- Mutate
FileTrieNodein place. - Semantics mirror
Array.prototypesort/filter/map.
class FileTrieNode {
isFolder: boolean
children: Array<FileTrieNode>
data: ContentDetails | null
}export type ContentDetails = {
slug: FullSlug
title: string
links: SimpleSlug[]
tags: string[]
content: string
}default_sort:
- Folders first.
- Alphabetical within folders/files.
// Sort order: folders first, then files. Sort folders and files alphabetically
ExternalPlugin.Explorer({
sortFn: (a, b) => {
if ((!a.isFolder && !b.isFolder) || (a.isFolder && b.isFolder)) {
return a.displayName.localeCompare(b.displayName, undefined, {
numeric: true,
sensitivity: "base",
})
}
if (!a.isFolder && b.isFolder) {
return 1
} else {
return -1
}
},
})execution:
- Sequence:
order.
type SortFn = (a: FileTrieNode, b: FileTrieNode) => number
type FilterFn = (node: FileTrieNode) => boolean
type MapFn = (node: FileTrieNode) => voidBasic examples
Sort all nodes alphabetically
yaml_options:
plugins:
- source: github:quartz-community/explorer
enabled: true
options:
# Simple options go in YAML
title: Explorer
folderDefaultState: collapsedts_override:
ExternalPlugin.Explorer({
sortFn: (a, b) => {
return a.displayName.localeCompare(b.displayName)
},
})Change display names (map)
ts_override:
ExternalPlugin.Explorer({
mapFn: (node) => {
node.displayName = node.displayName.toUpperCase()
return node
},
})Note
callbacks:
- JS callbacks (
mapFn,filterFn,sortFn) require TS overrides.
Omit by display name (filter)
ts_override:
ExternalPlugin.Explorer({
filterFn: (node) => {
// Names to omit
const omit = new Set(["authoring content", "tags", "advanced"])
// Can also use node.slug or node.data fields
// node.data exists only for files present on disk
// Implicit folder nodes may lack index.md and node.data
return !omit.has(node.displayName.toLowerCase())
},
})Remove files by tag
ts_override:
ExternalPlugin.Explorer({
filterFn: (node) => {
// Exclude files tagged "explorerexclude"
return node.data?.tags?.includes("explorerexclude") !== true
},
})Show every element
ts_override:
ExternalPlugin.Explorer({
filterFn: undefined, // no filter; show every file/folder
})Advanced examples
Tip
structure:
- Define callbacks outside component to keep
quartz.tssmall:quartz.ts import * as ExternalPlugin from "./.quartz/plugins" import type { ExplorerOptions } from "./.quartz/plugins" const mapFn: ExplorerOptions["mapFn"] = (node) => { // ... } const filterFn: ExplorerOptions["filterFn"] = (node) => { // ... } const sortFn: ExplorerOptions["sortFn"] = (a, b) => { // ... } ExternalPlugin.Explorer({ // ... mapFn, filterFn, sortFn, })
Add emoji prefix
ts_override:
ExternalPlugin.Explorer({
mapFn: (node) => {
if (node.isFolder) {
node.displayName = "📁 " + node.displayName
} else {
node.displayName = "📄 " + node.displayName
}
},
})