From b7b56d16fed47fae4e68d69cc0035e6289c990c1 Mon Sep 17 00:00:00 2001 From: saberzero1 Date: Wed, 18 Mar 2026 23:56:39 +0100 Subject: [PATCH] fix: stop replacing dashes with spaces in breadcrumb display names The replaceAll('-', ' ') in formatCrumb() was unnecessary since FileTrieNode.displayName already resolves correctly via the priority chain. --- dist/components/index.js | 2 +- dist/components/index.js.map | 2 +- dist/index.js | 2 +- dist/index.js.map | 2 +- src/components/Breadcrumbs.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dist/components/index.js b/dist/components/index.js index f0d6c6d..4c47212 100644 --- a/dist/components/index.js +++ b/dist/components/index.js @@ -146,7 +146,7 @@ var defaultOptions = { }; function formatCrumb(displayName, baseSlug, currentSlug) { return { - displayName: displayName.replaceAll("-", " "), + displayName, path: resolveRelative(baseSlug, currentSlug) }; } diff --git a/dist/components/index.js.map b/dist/components/index.js.map index 71e3cce..f1972ba 100644 --- a/dist/components/index.js.map +++ b/dist/components/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../../src/util/lang.ts","../../src/util/path.ts","../../src/util/fileTrie.ts","../../src/components/styles/breadcrumbs.scss","../../src/components/Breadcrumbs.tsx"],"names":["utilSimplifySlug","joinSegments"],"mappings":"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","file":"index.js","sourcesContent":["export function classNames(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(\" \");\n}\n","import { simplifySlug as utilSimplifySlug, joinSegments } from \"@quartz-community/utils\";\n\nexport function simplifySlug(fp: string): string {\n return utilSimplifySlug(fp);\n}\n\nexport function resolveRelative(current: string, target: string): string {\n const simplified = simplifySlug(target);\n const rootPath = pathToRoot(current);\n return joinSegments(rootPath, simplified);\n}\n\nfunction pathToRoot(slug: string): string {\n let rootPath = slug\n .split(\"/\")\n .filter((x) => x !== \"\")\n .slice(0, -1)\n .map((_) => \"..\")\n .join(\"/\");\n\n if (rootPath.length === 0) {\n rootPath = \".\";\n }\n\n return rootPath;\n}\n","import { joinSegments } from \"@quartz-community/utils\";\n\ninterface FileTrieData {\n slug: string;\n title: string;\n filePath: string;\n}\n\nexport class FileTrieNode {\n isFolder: boolean;\n children: Array>;\n\n private slugSegments: string[];\n private fileSegmentHint?: string;\n private displayNameOverride?: string;\n data: T | null;\n\n constructor(segments: string[], data?: T) {\n this.children = [];\n this.slugSegments = segments;\n this.data = data ?? null;\n this.isFolder = false;\n this.displayNameOverride = undefined;\n }\n\n get displayName(): string {\n const nonIndexTitle = this.data?.title === \"index\" ? undefined : this.data?.title;\n return (\n this.displayNameOverride ?? nonIndexTitle ?? this.fileSegmentHint ?? this.slugSegment ?? \"\"\n );\n }\n\n set displayName(name: string) {\n this.displayNameOverride = name;\n }\n\n get slug(): string {\n const path = joinSegments(...this.slugSegments);\n if (this.isFolder) {\n return joinSegments(path, \"index\");\n }\n\n return path;\n }\n\n get slugSegment(): string {\n return this.slugSegments[this.slugSegments.length - 1] ?? \"\";\n }\n\n private makeChild(path: string[], file?: T): FileTrieNode {\n const nextSegment = path[0];\n if (!nextSegment) {\n throw new Error(\"path is empty\");\n }\n const fullPath = [...this.slugSegments, nextSegment];\n const child = new FileTrieNode(fullPath, file);\n this.children.push(child);\n return child;\n }\n\n private insert(path: string[], file: T): void {\n if (path.length === 0) {\n throw new Error(\"path is empty\");\n }\n\n this.isFolder = true;\n const segment = path[0];\n if (!segment) {\n throw new Error(\"path is empty\");\n }\n if (path.length === 1) {\n if (segment === \"index\") {\n this.data ??= file;\n } else {\n this.makeChild(path, file);\n }\n } else if (path.length > 1) {\n const child =\n this.children.find((c) => c.slugSegment === segment) ?? this.makeChild(path, undefined);\n\n const fileParts = file.filePath.split(\"/\");\n const hint = fileParts.at(-path.length);\n if (hint) {\n child.fileSegmentHint = hint;\n }\n child.insert(path.slice(1), file);\n }\n }\n\n add(file: T): void {\n this.insert(file.slug.split(\"/\"), file);\n }\n\n findNode(path: string[]): FileTrieNode | undefined {\n if (path.length === 0 || (path.length === 1 && path[0] === \"index\")) {\n return this;\n }\n\n return this.children.find((c) => c.slugSegment === path[0])?.findNode(path.slice(1));\n }\n\n ancestryChain(path: string[]): Array> | undefined {\n if (path.length === 0 || (path.length === 1 && path[0] === \"index\")) {\n return [this];\n }\n\n const child = this.children.find((c) => c.slugSegment === path[0]);\n if (!child) {\n return undefined;\n }\n\n const childPath = child.ancestryChain(path.slice(1));\n if (!childPath) {\n return undefined;\n }\n\n return [this, ...childPath];\n }\n\n filter(filterFn: (node: FileTrieNode) => boolean): void {\n this.children = this.children.filter(filterFn);\n this.children.forEach((child) => child.filter(filterFn));\n }\n\n map(mapFn: (node: FileTrieNode) => void): void {\n mapFn(this);\n this.children.forEach((child) => child.map(mapFn));\n }\n\n sort(sortFn: (a: FileTrieNode, b: FileTrieNode) => number): void {\n this.children = this.children.sort(sortFn);\n this.children.forEach((e) => e.sort(sortFn));\n }\n}\n\nexport function trieFromAllFiles(\n allFiles: Array<{\n slug?: string;\n filePath?: string;\n frontmatter?: { title?: string; [key: string]: unknown };\n }>,\n): FileTrieNode {\n const trie = new FileTrieNode([]);\n allFiles.forEach((file) => {\n if (file.frontmatter) {\n trie.add({\n slug: file.slug!,\n title: file.frontmatter.title ?? \"\",\n filePath: file.filePath!,\n });\n }\n });\n\n return trie;\n}\n",".breadcrumb-container {\n margin: 0;\n margin-top: 0.75rem;\n padding: 0;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.breadcrumb-element p {\n margin: 0;\n margin-left: 0.5rem;\n padding: 0;\n line-height: normal;\n}\n.breadcrumb-element {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: center;\n}","import type { QuartzComponent, QuartzComponentProps } from \"@quartz-community/types\";\nimport { classNames } from \"../util/lang\";\nimport { resolveRelative, simplifySlug } from \"../util/path\";\nimport { FileTrieNode, trieFromAllFiles } from \"../util/fileTrie\";\nimport style from \"./styles/breadcrumbs.scss\";\n\ntype QuartzComponentConstructor = (\n opts: Options,\n) => QuartzComponent;\n\ntype CrumbData = {\n displayName: string;\n path: string;\n};\n\nexport interface BreadcrumbOptions {\n /** Symbol between crumbs */\n spacerSymbol: string;\n /** Name of first crumb */\n rootName: string;\n /** Whether to look up frontmatter title for folders */\n resolveFrontmatterTitle: boolean;\n /** Whether to display the current page in the breadcrumbs */\n showCurrentPage: boolean;\n}\n\nconst defaultOptions: BreadcrumbOptions = {\n spacerSymbol: \"❯\",\n rootName: \"Home\",\n resolveFrontmatterTitle: true,\n showCurrentPage: true,\n};\n\nfunction formatCrumb(displayName: string, baseSlug: string, currentSlug: string): CrumbData {\n return {\n displayName: displayName.replaceAll(\"-\", \" \"),\n path: resolveRelative(baseSlug, currentSlug),\n };\n}\n\nexport default ((opts?: Partial) => {\n const options: BreadcrumbOptions = { ...defaultOptions, ...opts };\n const Breadcrumbs: QuartzComponent = ({\n fileData,\n allFiles,\n displayClass,\n ctx,\n }: QuartzComponentProps) => {\n const typedCtx = (ctx ?? {}) as Record as { trie?: FileTrieNode };\n typedCtx.trie ??= trieFromAllFiles(\n allFiles as Array<{\n slug?: string;\n filePath?: string;\n frontmatter?: { title?: string; [key: string]: unknown };\n }>,\n );\n const trie = typedCtx.trie;\n const slug = fileData.slug as string;\n const slugParts = slug.split(\"/\");\n const pathNodes = trie.ancestryChain(slugParts);\n\n if (!pathNodes) {\n return null;\n }\n\n const crumbs: CrumbData[] = pathNodes.map((node, idx) => {\n const crumb = formatCrumb(node.displayName, slug, simplifySlug(node.slug));\n if (idx === 0) {\n crumb.displayName = options.rootName;\n }\n\n if (idx === pathNodes.length - 1) {\n crumb.path = \"\";\n }\n\n return crumb;\n });\n\n if (!options.showCurrentPage) {\n crumbs.pop();\n }\n\n return (\n \n );\n };\n Breadcrumbs.css = style;\n\n return Breadcrumbs;\n}) satisfies QuartzComponentConstructor;\n"]} \ No newline at end of file +{"version":3,"sources":["../../src/util/lang.ts","../../src/util/path.ts","../../src/util/fileTrie.ts","../../src/components/styles/breadcrumbs.scss","../../src/components/Breadcrumbs.tsx"],"names":["utilSimplifySlug","joinSegments"],"mappings":"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","file":"index.js","sourcesContent":["export function classNames(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(\" \");\n}\n","import { simplifySlug as utilSimplifySlug, joinSegments } from \"@quartz-community/utils\";\n\nexport function simplifySlug(fp: string): string {\n return utilSimplifySlug(fp);\n}\n\nexport function resolveRelative(current: string, target: string): string {\n const simplified = simplifySlug(target);\n const rootPath = pathToRoot(current);\n return joinSegments(rootPath, simplified);\n}\n\nfunction pathToRoot(slug: string): string {\n let rootPath = slug\n .split(\"/\")\n .filter((x) => x !== \"\")\n .slice(0, -1)\n .map((_) => \"..\")\n .join(\"/\");\n\n if (rootPath.length === 0) {\n rootPath = \".\";\n }\n\n return rootPath;\n}\n","import { joinSegments } from \"@quartz-community/utils\";\n\ninterface FileTrieData {\n slug: string;\n title: string;\n filePath: string;\n}\n\nexport class FileTrieNode {\n isFolder: boolean;\n children: Array>;\n\n private slugSegments: string[];\n private fileSegmentHint?: string;\n private displayNameOverride?: string;\n data: T | null;\n\n constructor(segments: string[], data?: T) {\n this.children = [];\n this.slugSegments = segments;\n this.data = data ?? null;\n this.isFolder = false;\n this.displayNameOverride = undefined;\n }\n\n get displayName(): string {\n const nonIndexTitle = this.data?.title === \"index\" ? undefined : this.data?.title;\n return (\n this.displayNameOverride ?? nonIndexTitle ?? this.fileSegmentHint ?? this.slugSegment ?? \"\"\n );\n }\n\n set displayName(name: string) {\n this.displayNameOverride = name;\n }\n\n get slug(): string {\n const path = joinSegments(...this.slugSegments);\n if (this.isFolder) {\n return joinSegments(path, \"index\");\n }\n\n return path;\n }\n\n get slugSegment(): string {\n return this.slugSegments[this.slugSegments.length - 1] ?? \"\";\n }\n\n private makeChild(path: string[], file?: T): FileTrieNode {\n const nextSegment = path[0];\n if (!nextSegment) {\n throw new Error(\"path is empty\");\n }\n const fullPath = [...this.slugSegments, nextSegment];\n const child = new FileTrieNode(fullPath, file);\n this.children.push(child);\n return child;\n }\n\n private insert(path: string[], file: T): void {\n if (path.length === 0) {\n throw new Error(\"path is empty\");\n }\n\n this.isFolder = true;\n const segment = path[0];\n if (!segment) {\n throw new Error(\"path is empty\");\n }\n if (path.length === 1) {\n if (segment === \"index\") {\n this.data ??= file;\n } else {\n this.makeChild(path, file);\n }\n } else if (path.length > 1) {\n const child =\n this.children.find((c) => c.slugSegment === segment) ?? this.makeChild(path, undefined);\n\n const fileParts = file.filePath.split(\"/\");\n const hint = fileParts.at(-path.length);\n if (hint) {\n child.fileSegmentHint = hint;\n }\n child.insert(path.slice(1), file);\n }\n }\n\n add(file: T): void {\n this.insert(file.slug.split(\"/\"), file);\n }\n\n findNode(path: string[]): FileTrieNode | undefined {\n if (path.length === 0 || (path.length === 1 && path[0] === \"index\")) {\n return this;\n }\n\n return this.children.find((c) => c.slugSegment === path[0])?.findNode(path.slice(1));\n }\n\n ancestryChain(path: string[]): Array> | undefined {\n if (path.length === 0 || (path.length === 1 && path[0] === \"index\")) {\n return [this];\n }\n\n const child = this.children.find((c) => c.slugSegment === path[0]);\n if (!child) {\n return undefined;\n }\n\n const childPath = child.ancestryChain(path.slice(1));\n if (!childPath) {\n return undefined;\n }\n\n return [this, ...childPath];\n }\n\n filter(filterFn: (node: FileTrieNode) => boolean): void {\n this.children = this.children.filter(filterFn);\n this.children.forEach((child) => child.filter(filterFn));\n }\n\n map(mapFn: (node: FileTrieNode) => void): void {\n mapFn(this);\n this.children.forEach((child) => child.map(mapFn));\n }\n\n sort(sortFn: (a: FileTrieNode, b: FileTrieNode) => number): void {\n this.children = this.children.sort(sortFn);\n this.children.forEach((e) => e.sort(sortFn));\n }\n}\n\nexport function trieFromAllFiles(\n allFiles: Array<{\n slug?: string;\n filePath?: string;\n frontmatter?: { title?: string; [key: string]: unknown };\n }>,\n): FileTrieNode {\n const trie = new FileTrieNode([]);\n allFiles.forEach((file) => {\n if (file.frontmatter) {\n trie.add({\n slug: file.slug!,\n title: file.frontmatter.title ?? \"\",\n filePath: file.filePath!,\n });\n }\n });\n\n return trie;\n}\n",".breadcrumb-container {\n margin: 0;\n margin-top: 0.75rem;\n padding: 0;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.breadcrumb-element p {\n margin: 0;\n margin-left: 0.5rem;\n padding: 0;\n line-height: normal;\n}\n.breadcrumb-element {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: center;\n}","import type { QuartzComponent, QuartzComponentProps } from \"@quartz-community/types\";\nimport { classNames } from \"../util/lang\";\nimport { resolveRelative, simplifySlug } from \"../util/path\";\nimport { FileTrieNode, trieFromAllFiles } from \"../util/fileTrie\";\nimport style from \"./styles/breadcrumbs.scss\";\n\ntype QuartzComponentConstructor = (\n opts: Options,\n) => QuartzComponent;\n\ntype CrumbData = {\n displayName: string;\n path: string;\n};\n\nexport interface BreadcrumbOptions {\n /** Symbol between crumbs */\n spacerSymbol: string;\n /** Name of first crumb */\n rootName: string;\n /** Whether to look up frontmatter title for folders */\n resolveFrontmatterTitle: boolean;\n /** Whether to display the current page in the breadcrumbs */\n showCurrentPage: boolean;\n}\n\nconst defaultOptions: BreadcrumbOptions = {\n spacerSymbol: \"❯\",\n rootName: \"Home\",\n resolveFrontmatterTitle: true,\n showCurrentPage: true,\n};\n\nfunction formatCrumb(displayName: string, baseSlug: string, currentSlug: string): CrumbData {\n return {\n displayName,\n path: resolveRelative(baseSlug, currentSlug),\n };\n}\n\nexport default ((opts?: Partial) => {\n const options: BreadcrumbOptions = { ...defaultOptions, ...opts };\n const Breadcrumbs: QuartzComponent = ({\n fileData,\n allFiles,\n displayClass,\n ctx,\n }: QuartzComponentProps) => {\n const typedCtx = (ctx ?? {}) as Record as { trie?: FileTrieNode };\n typedCtx.trie ??= trieFromAllFiles(\n allFiles as Array<{\n slug?: string;\n filePath?: string;\n frontmatter?: { title?: string; [key: string]: unknown };\n }>,\n );\n const trie = typedCtx.trie;\n const slug = fileData.slug as string;\n const slugParts = slug.split(\"/\");\n const pathNodes = trie.ancestryChain(slugParts);\n\n if (!pathNodes) {\n return null;\n }\n\n const crumbs: CrumbData[] = pathNodes.map((node, idx) => {\n const crumb = formatCrumb(node.displayName, slug, simplifySlug(node.slug));\n if (idx === 0) {\n crumb.displayName = options.rootName;\n }\n\n if (idx === pathNodes.length - 1) {\n crumb.path = \"\";\n }\n\n return crumb;\n });\n\n if (!options.showCurrentPage) {\n crumbs.pop();\n }\n\n return (\n \n );\n };\n Breadcrumbs.css = style;\n\n return Breadcrumbs;\n}) satisfies QuartzComponentConstructor;\n"]} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index f0d6c6d..4c47212 100644 --- a/dist/index.js +++ b/dist/index.js @@ -146,7 +146,7 @@ var defaultOptions = { }; function formatCrumb(displayName, baseSlug, currentSlug) { return { - displayName: displayName.replaceAll("-", " "), + displayName, path: resolveRelative(baseSlug, currentSlug) }; } diff --git a/dist/index.js.map b/dist/index.js.map index 89064a9..23e9090 100644 --- a/dist/index.js.map +++ b/dist/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/util/lang.ts","../src/util/path.ts","../src/util/fileTrie.ts","../src/components/styles/breadcrumbs.scss","../src/components/Breadcrumbs.tsx"],"names":["utilSimplifySlug","joinSegments"],"mappings":"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","file":"index.js","sourcesContent":["export function classNames(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(\" \");\n}\n","import { simplifySlug as utilSimplifySlug, joinSegments } from \"@quartz-community/utils\";\n\nexport function simplifySlug(fp: string): string {\n return utilSimplifySlug(fp);\n}\n\nexport function resolveRelative(current: string, target: string): string {\n const simplified = simplifySlug(target);\n const rootPath = pathToRoot(current);\n return joinSegments(rootPath, simplified);\n}\n\nfunction pathToRoot(slug: string): string {\n let rootPath = slug\n .split(\"/\")\n .filter((x) => x !== \"\")\n .slice(0, -1)\n .map((_) => \"..\")\n .join(\"/\");\n\n if (rootPath.length === 0) {\n rootPath = \".\";\n }\n\n return rootPath;\n}\n","import { joinSegments } from \"@quartz-community/utils\";\n\ninterface FileTrieData {\n slug: string;\n title: string;\n filePath: string;\n}\n\nexport class FileTrieNode {\n isFolder: boolean;\n children: Array>;\n\n private slugSegments: string[];\n private fileSegmentHint?: string;\n private displayNameOverride?: string;\n data: T | null;\n\n constructor(segments: string[], data?: T) {\n this.children = [];\n this.slugSegments = segments;\n this.data = data ?? null;\n this.isFolder = false;\n this.displayNameOverride = undefined;\n }\n\n get displayName(): string {\n const nonIndexTitle = this.data?.title === \"index\" ? undefined : this.data?.title;\n return (\n this.displayNameOverride ?? nonIndexTitle ?? this.fileSegmentHint ?? this.slugSegment ?? \"\"\n );\n }\n\n set displayName(name: string) {\n this.displayNameOverride = name;\n }\n\n get slug(): string {\n const path = joinSegments(...this.slugSegments);\n if (this.isFolder) {\n return joinSegments(path, \"index\");\n }\n\n return path;\n }\n\n get slugSegment(): string {\n return this.slugSegments[this.slugSegments.length - 1] ?? \"\";\n }\n\n private makeChild(path: string[], file?: T): FileTrieNode {\n const nextSegment = path[0];\n if (!nextSegment) {\n throw new Error(\"path is empty\");\n }\n const fullPath = [...this.slugSegments, nextSegment];\n const child = new FileTrieNode(fullPath, file);\n this.children.push(child);\n return child;\n }\n\n private insert(path: string[], file: T): void {\n if (path.length === 0) {\n throw new Error(\"path is empty\");\n }\n\n this.isFolder = true;\n const segment = path[0];\n if (!segment) {\n throw new Error(\"path is empty\");\n }\n if (path.length === 1) {\n if (segment === \"index\") {\n this.data ??= file;\n } else {\n this.makeChild(path, file);\n }\n } else if (path.length > 1) {\n const child =\n this.children.find((c) => c.slugSegment === segment) ?? this.makeChild(path, undefined);\n\n const fileParts = file.filePath.split(\"/\");\n const hint = fileParts.at(-path.length);\n if (hint) {\n child.fileSegmentHint = hint;\n }\n child.insert(path.slice(1), file);\n }\n }\n\n add(file: T): void {\n this.insert(file.slug.split(\"/\"), file);\n }\n\n findNode(path: string[]): FileTrieNode | undefined {\n if (path.length === 0 || (path.length === 1 && path[0] === \"index\")) {\n return this;\n }\n\n return this.children.find((c) => c.slugSegment === path[0])?.findNode(path.slice(1));\n }\n\n ancestryChain(path: string[]): Array> | undefined {\n if (path.length === 0 || (path.length === 1 && path[0] === \"index\")) {\n return [this];\n }\n\n const child = this.children.find((c) => c.slugSegment === path[0]);\n if (!child) {\n return undefined;\n }\n\n const childPath = child.ancestryChain(path.slice(1));\n if (!childPath) {\n return undefined;\n }\n\n return [this, ...childPath];\n }\n\n filter(filterFn: (node: FileTrieNode) => boolean): void {\n this.children = this.children.filter(filterFn);\n this.children.forEach((child) => child.filter(filterFn));\n }\n\n map(mapFn: (node: FileTrieNode) => void): void {\n mapFn(this);\n this.children.forEach((child) => child.map(mapFn));\n }\n\n sort(sortFn: (a: FileTrieNode, b: FileTrieNode) => number): void {\n this.children = this.children.sort(sortFn);\n this.children.forEach((e) => e.sort(sortFn));\n }\n}\n\nexport function trieFromAllFiles(\n allFiles: Array<{\n slug?: string;\n filePath?: string;\n frontmatter?: { title?: string; [key: string]: unknown };\n }>,\n): FileTrieNode {\n const trie = new FileTrieNode([]);\n allFiles.forEach((file) => {\n if (file.frontmatter) {\n trie.add({\n slug: file.slug!,\n title: file.frontmatter.title ?? \"\",\n filePath: file.filePath!,\n });\n }\n });\n\n return trie;\n}\n",".breadcrumb-container {\n margin: 0;\n margin-top: 0.75rem;\n padding: 0;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.breadcrumb-element p {\n margin: 0;\n margin-left: 0.5rem;\n padding: 0;\n line-height: normal;\n}\n.breadcrumb-element {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: center;\n}","import type { QuartzComponent, QuartzComponentProps } from \"@quartz-community/types\";\nimport { classNames } from \"../util/lang\";\nimport { resolveRelative, simplifySlug } from \"../util/path\";\nimport { FileTrieNode, trieFromAllFiles } from \"../util/fileTrie\";\nimport style from \"./styles/breadcrumbs.scss\";\n\ntype QuartzComponentConstructor = (\n opts: Options,\n) => QuartzComponent;\n\ntype CrumbData = {\n displayName: string;\n path: string;\n};\n\nexport interface BreadcrumbOptions {\n /** Symbol between crumbs */\n spacerSymbol: string;\n /** Name of first crumb */\n rootName: string;\n /** Whether to look up frontmatter title for folders */\n resolveFrontmatterTitle: boolean;\n /** Whether to display the current page in the breadcrumbs */\n showCurrentPage: boolean;\n}\n\nconst defaultOptions: BreadcrumbOptions = {\n spacerSymbol: \"❯\",\n rootName: \"Home\",\n resolveFrontmatterTitle: true,\n showCurrentPage: true,\n};\n\nfunction formatCrumb(displayName: string, baseSlug: string, currentSlug: string): CrumbData {\n return {\n displayName: displayName.replaceAll(\"-\", \" \"),\n path: resolveRelative(baseSlug, currentSlug),\n };\n}\n\nexport default ((opts?: Partial) => {\n const options: BreadcrumbOptions = { ...defaultOptions, ...opts };\n const Breadcrumbs: QuartzComponent = ({\n fileData,\n allFiles,\n displayClass,\n ctx,\n }: QuartzComponentProps) => {\n const typedCtx = (ctx ?? {}) as Record as { trie?: FileTrieNode };\n typedCtx.trie ??= trieFromAllFiles(\n allFiles as Array<{\n slug?: string;\n filePath?: string;\n frontmatter?: { title?: string; [key: string]: unknown };\n }>,\n );\n const trie = typedCtx.trie;\n const slug = fileData.slug as string;\n const slugParts = slug.split(\"/\");\n const pathNodes = trie.ancestryChain(slugParts);\n\n if (!pathNodes) {\n return null;\n }\n\n const crumbs: CrumbData[] = pathNodes.map((node, idx) => {\n const crumb = formatCrumb(node.displayName, slug, simplifySlug(node.slug));\n if (idx === 0) {\n crumb.displayName = options.rootName;\n }\n\n if (idx === pathNodes.length - 1) {\n crumb.path = \"\";\n }\n\n return crumb;\n });\n\n if (!options.showCurrentPage) {\n crumbs.pop();\n }\n\n return (\n \n );\n };\n Breadcrumbs.css = style;\n\n return Breadcrumbs;\n}) satisfies QuartzComponentConstructor;\n"]} \ No newline at end of file +{"version":3,"sources":["../src/util/lang.ts","../src/util/path.ts","../src/util/fileTrie.ts","../src/components/styles/breadcrumbs.scss","../src/components/Breadcrumbs.tsx"],"names":["utilSimplifySlug","joinSegments"],"mappings":";;;;AAAO,SAAS,cAAc,OAAA,EAAwD;AACpF,EAAA,OAAO,OAAA,CAAQ,MAAA,CAAO,OAAO,CAAA,CAAE,KAAK,GAAG,CAAA;AACzC;ACAO,SAAS,aAAa,EAAA,EAAoB;AAC/C,EAAA,OAAOA,eAAiB,EAAE,CAAA;AAC5B;AAEO,SAAS,eAAA,CAAgB,SAAiB,MAAA,EAAwB;AACvE,EAAA,MAAM,UAAA,GAAa,aAAa,MAAM,CAAA;AACtC,EAAA,MAAM,QAAA,GAAW,WAAW,OAAO,CAAA;AACnC,EAAA,OAAO,YAAA,CAAa,UAAU,UAAU,CAAA;AAC1C;AAEA,SAAS,WAAW,IAAA,EAAsB;AACxC,EAAA,IAAI,QAAA,GAAW,KACZ,KAAA,CAAM,GAAG,EACT,MAAA,CAAO,CAAC,MAAM,CAAA,KAAM,EAAE,EACtB,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,CACX,GAAA,CAAI,CAAC,CAAA,KAAM,IAAI,CAAA,CACf,IAAA,CAAK,GAAG,CAAA;AAEX,EAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AACzB,IAAA,QAAA,GAAW,GAAA;AAAA,EACb;AAEA,EAAA,OAAO,QAAA;AACT;ACjBO,IAAM,YAAA,GAAN,MAAM,aAAA,CAAoD;AAAA,EAC/D,QAAA;AAAA,EACA,QAAA;AAAA,EAEQ,YAAA;AAAA,EACA,eAAA;AAAA,EACA,mBAAA;AAAA,EACR,IAAA;AAAA,EAEA,WAAA,CAAY,UAAoB,IAAA,EAAU;AACxC,IAAA,IAAA,CAAK,WAAW,EAAC;AACjB,IAAA,IAAA,CAAK,YAAA,GAAe,QAAA;AACpB,IAAA,IAAA,CAAK,OAAO,IAAA,IAAQ,IAAA;AACpB,IAAA,IAAA,CAAK,QAAA,GAAW,KAAA;AAChB,IAAA,IAAA,CAAK,mBAAA,GAAsB,MAAA;AAAA,EAC7B;AAAA,EAEA,IAAI,WAAA,GAAsB;AACxB,IAAA,MAAM,gBAAgB,IAAA,CAAK,IAAA,EAAM,UAAU,OAAA,GAAU,MAAA,GAAY,KAAK,IAAA,EAAM,KAAA;AAC5E,IAAA,OACE,KAAK,mBAAA,IAAuB,aAAA,IAAiB,IAAA,CAAK,eAAA,IAAmB,KAAK,WAAA,IAAe,EAAA;AAAA,EAE7F;AAAA,EAEA,IAAI,YAAY,IAAA,EAAc;AAC5B,IAAA,IAAA,CAAK,mBAAA,GAAsB,IAAA;AAAA,EAC7B;AAAA,EAEA,IAAI,IAAA,GAAe;AACjB,IAAA,MAAM,IAAA,GAAOC,YAAAA,CAAa,GAAG,IAAA,CAAK,YAAY,CAAA;AAC9C,IAAA,IAAI,KAAK,QAAA,EAAU;AACjB,MAAA,OAAOA,YAAAA,CAAa,MAAM,OAAO,CAAA;AAAA,IACnC;AAEA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,IAAI,WAAA,GAAsB;AACxB,IAAA,OAAO,KAAK,YAAA,CAAa,IAAA,CAAK,YAAA,CAAa,MAAA,GAAS,CAAC,CAAA,IAAK,EAAA;AAAA,EAC5D;AAAA,EAEQ,SAAA,CAAU,MAAgB,IAAA,EAA2B;AAC3D,IAAA,MAAM,WAAA,GAAc,KAAK,CAAC,CAAA;AAC1B,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA,MAAM,IAAI,MAAM,eAAe,CAAA;AAAA,IACjC;AACA,IAAA,MAAM,QAAA,GAAW,CAAC,GAAG,IAAA,CAAK,cAAc,WAAW,CAAA;AACnD,IAAA,MAAM,KAAA,GAAQ,IAAI,aAAA,CAAgB,QAAA,EAAU,IAAI,CAAA;AAChD,IAAA,IAAA,CAAK,QAAA,CAAS,KAAK,KAAK,CAAA;AACxB,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEQ,MAAA,CAAO,MAAgB,IAAA,EAAe;AAC5C,IAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACrB,MAAA,MAAM,IAAI,MAAM,eAAe,CAAA;AAAA,IACjC;AAEA,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAChB,IAAA,MAAM,OAAA,GAAU,KAAK,CAAC,CAAA;AACtB,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,MAAM,IAAI,MAAM,eAAe,CAAA;AAAA,IACjC;AACA,IAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACrB,MAAA,IAAI,YAAY,OAAA,EAAS;AACvB,QAAA,IAAA,CAAK,IAAA,KAAS,IAAA;AAAA,MAChB,CAAA,MAAO;AACL,QAAA,IAAA,CAAK,SAAA,CAAU,MAAM,IAAI,CAAA;AAAA,MAC3B;AAAA,IACF,CAAA,MAAA,IAAW,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG;AAC1B,MAAA,MAAM,KAAA,GACJ,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,WAAA,KAAgB,OAAO,CAAA,IAAK,IAAA,CAAK,SAAA,CAAU,MAAM,MAAS,CAAA;AAExF,MAAA,MAAM,SAAA,GAAY,IAAA,CAAK,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA;AACzC,MAAA,MAAM,IAAA,GAAO,SAAA,CAAU,EAAA,CAAG,CAAC,KAAK,MAAM,CAAA;AACtC,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,KAAA,CAAM,eAAA,GAAkB,IAAA;AAAA,MAC1B;AACA,MAAA,KAAA,CAAM,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,CAAC,GAAG,IAAI,CAAA;AAAA,IAClC;AAAA,EACF;AAAA,EAEA,IAAI,IAAA,EAAe;AACjB,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,GAAG,GAAG,IAAI,CAAA;AAAA,EACxC;AAAA,EAEA,SAAS,IAAA,EAA6C;AACpD,IAAA,IAAI,IAAA,CAAK,WAAW,CAAA,IAAM,IAAA,CAAK,WAAW,CAAA,IAAK,IAAA,CAAK,CAAC,CAAA,KAAM,OAAA,EAAU;AACnE,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,OAAO,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,WAAA,KAAgB,IAAA,CAAK,CAAC,CAAC,CAAA,EAAG,QAAA,CAAS,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA;AAAA,EACrF;AAAA,EAEA,cAAc,IAAA,EAAoD;AAChE,IAAA,IAAI,IAAA,CAAK,WAAW,CAAA,IAAM,IAAA,CAAK,WAAW,CAAA,IAAK,IAAA,CAAK,CAAC,CAAA,KAAM,OAAA,EAAU;AACnE,MAAA,OAAO,CAAC,IAAI,CAAA;AAAA,IACd;AAEA,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,WAAA,KAAgB,IAAA,CAAK,CAAC,CAAC,CAAA;AACjE,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,MAAM,YAAY,KAAA,CAAM,aAAA,CAAc,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA;AACnD,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,OAAO,CAAC,IAAA,EAAM,GAAG,SAAS,CAAA;AAAA,EAC5B;AAAA,EAEA,OAAO,QAAA,EAAoD;AACzD,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA,CAAK,QAAA,CAAS,MAAA,CAAO,QAAQ,CAAA;AAC7C,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,CAAC,UAAU,KAAA,CAAM,MAAA,CAAO,QAAQ,CAAC,CAAA;AAAA,EACzD;AAAA,EAEA,IAAI,KAAA,EAA8C;AAChD,IAAA,KAAA,CAAM,IAAI,CAAA;AACV,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,CAAC,UAAU,KAAA,CAAM,GAAA,CAAI,KAAK,CAAC,CAAA;AAAA,EACnD;AAAA,EAEA,KAAK,MAAA,EAAkE;AACrE,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,MAAM,CAAA;AACzC,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,CAAC,MAAM,CAAA,CAAE,IAAA,CAAK,MAAM,CAAC,CAAA;AAAA,EAC7C;AACF,CAAA;AAEO,SAAS,iBACd,QAAA,EAKc;AACd,EAAA,MAAM,IAAA,GAAO,IAAI,YAAA,CAAa,EAAE,CAAA;AAChC,EAAA,QAAA,CAAS,OAAA,CAAQ,CAAC,IAAA,KAAS;AACzB,IAAA,IAAI,KAAK,WAAA,EAAa;AACpB,MAAA,IAAA,CAAK,GAAA,CAAI;AAAA,QACP,MAAM,IAAA,CAAK,IAAA;AAAA,QACX,KAAA,EAAO,IAAA,CAAK,WAAA,CAAY,KAAA,IAAS,EAAA;AAAA,QACjC,UAAU,IAAA,CAAK;AAAA,OAChB,CAAA;AAAA,IACH;AAAA,EACF,CAAC,CAAA;AAED,EAAA,OAAO,IAAA;AACT;;;AC1JA,IAAA,mBAAA,GAAA,kYAAA;AC0BA,IAAM,cAAA,GAAoC;AAAA,EACxC,YAAA,EAAc,QAAA;AAAA,EACd,QAAA,EAAU,MAAA;AAAA,EACV,uBAAA,EAAyB,IAAA;AAAA,EACzB,eAAA,EAAiB;AACnB,CAAA;AAEA,SAAS,WAAA,CAAY,WAAA,EAAqB,QAAA,EAAkB,WAAA,EAAgC;AAC1F,EAAA,OAAO;AAAA,IACL,WAAA;AAAA,IACA,IAAA,EAAM,eAAA,CAAgB,QAAA,EAAU,WAAW;AAAA,GAC7C;AACF;AAEA,IAAO,mBAAA,IAAS,CAAC,IAAA,KAAsC;AACrD,EAAA,MAAM,OAAA,GAA6B,EAAE,GAAG,cAAA,EAAgB,GAAG,IAAA,EAAK;AAChE,EAAA,MAAM,cAA+B,CAAC;AAAA,IACpC,QAAA;AAAA,IACA,QAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA,GACF,KAA4B;AAC1B,IAAA,MAAM,QAAA,GAAY,OAAO,EAAC;AAC1B,IAAA,QAAA,CAAS,IAAA,KAAS,gBAAA;AAAA,MAChB;AAAA,KAKF;AACA,IAAA,MAAM,OAAO,QAAA,CAAS,IAAA;AACtB,IAAA,MAAM,OAAO,QAAA,CAAS,IAAA;AACtB,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAChC,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,aAAA,CAAc,SAAS,CAAA;AAE9C,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,MAAM,MAAA,GAAsB,SAAA,CAAU,GAAA,CAAI,CAAC,MAAM,GAAA,KAAQ;AACvD,MAAA,MAAM,KAAA,GAAQ,YAAY,IAAA,CAAK,WAAA,EAAa,MAAM,YAAA,CAAa,IAAA,CAAK,IAAI,CAAC,CAAA;AACzE,MAAA,IAAI,QAAQ,CAAA,EAAG;AACb,QAAA,KAAA,CAAM,cAAc,OAAA,CAAQ,QAAA;AAAA,MAC9B;AAEA,MAAA,IAAI,GAAA,KAAQ,SAAA,CAAU,MAAA,GAAS,CAAA,EAAG;AAChC,QAAA,KAAA,CAAM,IAAA,GAAO,EAAA;AAAA,MACf;AAEA,MAAA,OAAO,KAAA;AAAA,IACT,CAAC,CAAA;AAED,IAAA,IAAI,CAAC,QAAQ,eAAA,EAAiB;AAC5B,MAAA,MAAA,CAAO,GAAA,EAAI;AAAA,IACb;AAEA,IAAA,2BACG,KAAA,EAAA,EAAI,KAAA,EAAO,UAAA,CAAW,YAAA,EAAc,sBAAsB,CAAA,EAAG,YAAA,EAAW,aAAA,EACtE,QAAA,EAAA,MAAA,CAAO,IAAI,CAAC,KAAA,EAAO,0BAClB,IAAA,CAAC,KAAA,EAAA,EAAI,OAAM,oBAAA,EACT,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,GAAA,EAAA,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAO,gBAAM,WAAA,EAAY,CAAA;AAAA,MACvC,KAAA,KAAU,OAAO,MAAA,GAAS,CAAA,wBAAM,GAAA,EAAA,EAAG,QAAA,EAAA,CAAA,CAAA,EAAI,OAAA,CAAQ,YAAY,CAAA,CAAA,CAAA,EAAI;AAAA,KAAA,EAClE,CACD,CAAA,EACH,CAAA;AAAA,EAEJ,CAAA;AACA,EAAA,WAAA,CAAY,GAAA,GAAM,mBAAA;AAElB,EAAA,OAAO,WAAA;AACT,CAAA","file":"index.js","sourcesContent":["export function classNames(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(\" \");\n}\n","import { simplifySlug as utilSimplifySlug, joinSegments } from \"@quartz-community/utils\";\n\nexport function simplifySlug(fp: string): string {\n return utilSimplifySlug(fp);\n}\n\nexport function resolveRelative(current: string, target: string): string {\n const simplified = simplifySlug(target);\n const rootPath = pathToRoot(current);\n return joinSegments(rootPath, simplified);\n}\n\nfunction pathToRoot(slug: string): string {\n let rootPath = slug\n .split(\"/\")\n .filter((x) => x !== \"\")\n .slice(0, -1)\n .map((_) => \"..\")\n .join(\"/\");\n\n if (rootPath.length === 0) {\n rootPath = \".\";\n }\n\n return rootPath;\n}\n","import { joinSegments } from \"@quartz-community/utils\";\n\ninterface FileTrieData {\n slug: string;\n title: string;\n filePath: string;\n}\n\nexport class FileTrieNode {\n isFolder: boolean;\n children: Array>;\n\n private slugSegments: string[];\n private fileSegmentHint?: string;\n private displayNameOverride?: string;\n data: T | null;\n\n constructor(segments: string[], data?: T) {\n this.children = [];\n this.slugSegments = segments;\n this.data = data ?? null;\n this.isFolder = false;\n this.displayNameOverride = undefined;\n }\n\n get displayName(): string {\n const nonIndexTitle = this.data?.title === \"index\" ? undefined : this.data?.title;\n return (\n this.displayNameOverride ?? nonIndexTitle ?? this.fileSegmentHint ?? this.slugSegment ?? \"\"\n );\n }\n\n set displayName(name: string) {\n this.displayNameOverride = name;\n }\n\n get slug(): string {\n const path = joinSegments(...this.slugSegments);\n if (this.isFolder) {\n return joinSegments(path, \"index\");\n }\n\n return path;\n }\n\n get slugSegment(): string {\n return this.slugSegments[this.slugSegments.length - 1] ?? \"\";\n }\n\n private makeChild(path: string[], file?: T): FileTrieNode {\n const nextSegment = path[0];\n if (!nextSegment) {\n throw new Error(\"path is empty\");\n }\n const fullPath = [...this.slugSegments, nextSegment];\n const child = new FileTrieNode(fullPath, file);\n this.children.push(child);\n return child;\n }\n\n private insert(path: string[], file: T): void {\n if (path.length === 0) {\n throw new Error(\"path is empty\");\n }\n\n this.isFolder = true;\n const segment = path[0];\n if (!segment) {\n throw new Error(\"path is empty\");\n }\n if (path.length === 1) {\n if (segment === \"index\") {\n this.data ??= file;\n } else {\n this.makeChild(path, file);\n }\n } else if (path.length > 1) {\n const child =\n this.children.find((c) => c.slugSegment === segment) ?? this.makeChild(path, undefined);\n\n const fileParts = file.filePath.split(\"/\");\n const hint = fileParts.at(-path.length);\n if (hint) {\n child.fileSegmentHint = hint;\n }\n child.insert(path.slice(1), file);\n }\n }\n\n add(file: T): void {\n this.insert(file.slug.split(\"/\"), file);\n }\n\n findNode(path: string[]): FileTrieNode | undefined {\n if (path.length === 0 || (path.length === 1 && path[0] === \"index\")) {\n return this;\n }\n\n return this.children.find((c) => c.slugSegment === path[0])?.findNode(path.slice(1));\n }\n\n ancestryChain(path: string[]): Array> | undefined {\n if (path.length === 0 || (path.length === 1 && path[0] === \"index\")) {\n return [this];\n }\n\n const child = this.children.find((c) => c.slugSegment === path[0]);\n if (!child) {\n return undefined;\n }\n\n const childPath = child.ancestryChain(path.slice(1));\n if (!childPath) {\n return undefined;\n }\n\n return [this, ...childPath];\n }\n\n filter(filterFn: (node: FileTrieNode) => boolean): void {\n this.children = this.children.filter(filterFn);\n this.children.forEach((child) => child.filter(filterFn));\n }\n\n map(mapFn: (node: FileTrieNode) => void): void {\n mapFn(this);\n this.children.forEach((child) => child.map(mapFn));\n }\n\n sort(sortFn: (a: FileTrieNode, b: FileTrieNode) => number): void {\n this.children = this.children.sort(sortFn);\n this.children.forEach((e) => e.sort(sortFn));\n }\n}\n\nexport function trieFromAllFiles(\n allFiles: Array<{\n slug?: string;\n filePath?: string;\n frontmatter?: { title?: string; [key: string]: unknown };\n }>,\n): FileTrieNode {\n const trie = new FileTrieNode([]);\n allFiles.forEach((file) => {\n if (file.frontmatter) {\n trie.add({\n slug: file.slug!,\n title: file.frontmatter.title ?? \"\",\n filePath: file.filePath!,\n });\n }\n });\n\n return trie;\n}\n",".breadcrumb-container {\n margin: 0;\n margin-top: 0.75rem;\n padding: 0;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.breadcrumb-element p {\n margin: 0;\n margin-left: 0.5rem;\n padding: 0;\n line-height: normal;\n}\n.breadcrumb-element {\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: center;\n}","import type { QuartzComponent, QuartzComponentProps } from \"@quartz-community/types\";\nimport { classNames } from \"../util/lang\";\nimport { resolveRelative, simplifySlug } from \"../util/path\";\nimport { FileTrieNode, trieFromAllFiles } from \"../util/fileTrie\";\nimport style from \"./styles/breadcrumbs.scss\";\n\ntype QuartzComponentConstructor = (\n opts: Options,\n) => QuartzComponent;\n\ntype CrumbData = {\n displayName: string;\n path: string;\n};\n\nexport interface BreadcrumbOptions {\n /** Symbol between crumbs */\n spacerSymbol: string;\n /** Name of first crumb */\n rootName: string;\n /** Whether to look up frontmatter title for folders */\n resolveFrontmatterTitle: boolean;\n /** Whether to display the current page in the breadcrumbs */\n showCurrentPage: boolean;\n}\n\nconst defaultOptions: BreadcrumbOptions = {\n spacerSymbol: \"❯\",\n rootName: \"Home\",\n resolveFrontmatterTitle: true,\n showCurrentPage: true,\n};\n\nfunction formatCrumb(displayName: string, baseSlug: string, currentSlug: string): CrumbData {\n return {\n displayName,\n path: resolveRelative(baseSlug, currentSlug),\n };\n}\n\nexport default ((opts?: Partial) => {\n const options: BreadcrumbOptions = { ...defaultOptions, ...opts };\n const Breadcrumbs: QuartzComponent = ({\n fileData,\n allFiles,\n displayClass,\n ctx,\n }: QuartzComponentProps) => {\n const typedCtx = (ctx ?? {}) as Record as { trie?: FileTrieNode };\n typedCtx.trie ??= trieFromAllFiles(\n allFiles as Array<{\n slug?: string;\n filePath?: string;\n frontmatter?: { title?: string; [key: string]: unknown };\n }>,\n );\n const trie = typedCtx.trie;\n const slug = fileData.slug as string;\n const slugParts = slug.split(\"/\");\n const pathNodes = trie.ancestryChain(slugParts);\n\n if (!pathNodes) {\n return null;\n }\n\n const crumbs: CrumbData[] = pathNodes.map((node, idx) => {\n const crumb = formatCrumb(node.displayName, slug, simplifySlug(node.slug));\n if (idx === 0) {\n crumb.displayName = options.rootName;\n }\n\n if (idx === pathNodes.length - 1) {\n crumb.path = \"\";\n }\n\n return crumb;\n });\n\n if (!options.showCurrentPage) {\n crumbs.pop();\n }\n\n return (\n \n );\n };\n Breadcrumbs.css = style;\n\n return Breadcrumbs;\n}) satisfies QuartzComponentConstructor;\n"]} \ No newline at end of file diff --git a/src/components/Breadcrumbs.tsx b/src/components/Breadcrumbs.tsx index 8d5ca84..31cfb74 100644 --- a/src/components/Breadcrumbs.tsx +++ b/src/components/Breadcrumbs.tsx @@ -33,7 +33,7 @@ const defaultOptions: BreadcrumbOptions = { function formatCrumb(displayName: string, baseSlug: string, currentSlug: string): CrumbData { return { - displayName: displayName.replaceAll("-", " "), + displayName, path: resolveRelative(baseSlug, currentSlug), }; }