From 3ae8c2fb1a4afff598bb02da910a4077f154db56 Mon Sep 17 00:00:00 2001 From: saberzero1 Date: Sat, 14 Mar 2026 21:57:12 +0100 Subject: [PATCH] chore: commit dist/ and remove prepare script Pre-built output is now committed to the repository so that Quartz can skip the build step during plugin installation. The prepare script is removed to prevent redundant builds when installing from npm/git. --- .gitignore | 1 - dist/components/index.d.ts | 2 + dist/components/index.js | 196 +++++++++++++++++++++++++++++++++++ dist/components/index.js.map | 1 + dist/index.d.ts | 16 +++ dist/index.js | 196 +++++++++++++++++++++++++++++++++++ dist/index.js.map | 1 + package-lock.json | 7 +- package.json | 1 - 9 files changed, 415 insertions(+), 6 deletions(-) create mode 100644 dist/components/index.d.ts create mode 100644 dist/components/index.js create mode 100644 dist/components/index.js.map create mode 100644 dist/index.d.ts create mode 100644 dist/index.js create mode 100644 dist/index.js.map diff --git a/.gitignore b/.gitignore index 657842b..ef88793 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ node_modules/ .pnp.js # Build output -dist/ build/ *.tsbuildinfo diff --git a/dist/components/index.d.ts b/dist/components/index.d.ts new file mode 100644 index 0000000..d1f4911 --- /dev/null +++ b/dist/components/index.d.ts @@ -0,0 +1,2 @@ +export { BreadcrumbOptions, Breadcrumbs } from '../index.js'; +import '@quartz-community/types'; diff --git a/dist/components/index.js b/dist/components/index.js new file mode 100644 index 0000000..f0d6c6d --- /dev/null +++ b/dist/components/index.js @@ -0,0 +1,196 @@ +import { simplifySlug as simplifySlug$1, joinSegments } from '@quartz-community/utils'; +import { jsx, jsxs } from 'preact/jsx-runtime'; + +// src/util/lang.ts +function classNames(...classes) { + return classes.filter(Boolean).join(" "); +} +function simplifySlug(fp) { + return simplifySlug$1(fp); +} +function resolveRelative(current, target) { + const simplified = simplifySlug(target); + const rootPath = pathToRoot(current); + return joinSegments(rootPath, simplified); +} +function pathToRoot(slug) { + let rootPath = slug.split("/").filter((x) => x !== "").slice(0, -1).map((_) => "..").join("/"); + if (rootPath.length === 0) { + rootPath = "."; + } + return rootPath; +} +var FileTrieNode = class _FileTrieNode { + isFolder; + children; + slugSegments; + fileSegmentHint; + displayNameOverride; + data; + constructor(segments, data) { + this.children = []; + this.slugSegments = segments; + this.data = data ?? null; + this.isFolder = false; + this.displayNameOverride = void 0; + } + get displayName() { + const nonIndexTitle = this.data?.title === "index" ? void 0 : this.data?.title; + return this.displayNameOverride ?? nonIndexTitle ?? this.fileSegmentHint ?? this.slugSegment ?? ""; + } + set displayName(name) { + this.displayNameOverride = name; + } + get slug() { + const path = joinSegments(...this.slugSegments); + if (this.isFolder) { + return joinSegments(path, "index"); + } + return path; + } + get slugSegment() { + return this.slugSegments[this.slugSegments.length - 1] ?? ""; + } + makeChild(path, file) { + const nextSegment = path[0]; + if (!nextSegment) { + throw new Error("path is empty"); + } + const fullPath = [...this.slugSegments, nextSegment]; + const child = new _FileTrieNode(fullPath, file); + this.children.push(child); + return child; + } + insert(path, file) { + if (path.length === 0) { + throw new Error("path is empty"); + } + this.isFolder = true; + const segment = path[0]; + if (!segment) { + throw new Error("path is empty"); + } + if (path.length === 1) { + if (segment === "index") { + this.data ??= file; + } else { + this.makeChild(path, file); + } + } else if (path.length > 1) { + const child = this.children.find((c) => c.slugSegment === segment) ?? this.makeChild(path, void 0); + const fileParts = file.filePath.split("/"); + const hint = fileParts.at(-path.length); + if (hint) { + child.fileSegmentHint = hint; + } + child.insert(path.slice(1), file); + } + } + add(file) { + this.insert(file.slug.split("/"), file); + } + findNode(path) { + if (path.length === 0 || path.length === 1 && path[0] === "index") { + return this; + } + return this.children.find((c) => c.slugSegment === path[0])?.findNode(path.slice(1)); + } + ancestryChain(path) { + if (path.length === 0 || path.length === 1 && path[0] === "index") { + return [this]; + } + const child = this.children.find((c) => c.slugSegment === path[0]); + if (!child) { + return void 0; + } + const childPath = child.ancestryChain(path.slice(1)); + if (!childPath) { + return void 0; + } + return [this, ...childPath]; + } + filter(filterFn) { + this.children = this.children.filter(filterFn); + this.children.forEach((child) => child.filter(filterFn)); + } + map(mapFn) { + mapFn(this); + this.children.forEach((child) => child.map(mapFn)); + } + sort(sortFn) { + this.children = this.children.sort(sortFn); + this.children.forEach((e) => e.sort(sortFn)); + } +}; +function trieFromAllFiles(allFiles) { + const trie = new FileTrieNode([]); + allFiles.forEach((file) => { + if (file.frontmatter) { + trie.add({ + slug: file.slug, + title: file.frontmatter.title ?? "", + filePath: file.filePath + }); + } + }); + return trie; +} + +// src/components/styles/breadcrumbs.scss +var breadcrumbs_default = ".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}"; +var defaultOptions = { + spacerSymbol: "\u276F", + rootName: "Home", + resolveFrontmatterTitle: true, + showCurrentPage: true +}; +function formatCrumb(displayName, baseSlug, currentSlug) { + return { + displayName: displayName.replaceAll("-", " "), + path: resolveRelative(baseSlug, currentSlug) + }; +} +var Breadcrumbs_default = ((opts) => { + const options = { ...defaultOptions, ...opts }; + const Breadcrumbs = ({ + fileData, + allFiles, + displayClass, + ctx + }) => { + const typedCtx = ctx ?? {}; + typedCtx.trie ??= trieFromAllFiles( + allFiles + ); + const trie = typedCtx.trie; + const slug = fileData.slug; + const slugParts = slug.split("/"); + const pathNodes = trie.ancestryChain(slugParts); + if (!pathNodes) { + return null; + } + const crumbs = pathNodes.map((node, idx) => { + const crumb = formatCrumb(node.displayName, slug, simplifySlug(node.slug)); + if (idx === 0) { + crumb.displayName = options.rootName; + } + if (idx === pathNodes.length - 1) { + crumb.path = ""; + } + return crumb; + }); + if (!options.showCurrentPage) { + crumbs.pop(); + } + return /* @__PURE__ */ jsx("nav", { class: classNames(displayClass, "breadcrumb-container"), "aria-label": "breadcrumbs", children: crumbs.map((crumb, index) => /* @__PURE__ */ jsxs("div", { class: "breadcrumb-element", children: [ + /* @__PURE__ */ jsx("a", { href: crumb.path, children: crumb.displayName }), + index !== crumbs.length - 1 && /* @__PURE__ */ jsx("p", { children: ` ${options.spacerSymbol} ` }) + ] })) }); + }; + Breadcrumbs.css = breadcrumbs_default; + return Breadcrumbs; +}); + +export { Breadcrumbs_default as Breadcrumbs }; +//# sourceMappingURL=index.js.map +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/components/index.js.map b/dist/components/index.js.map new file mode 100644 index 0000000..71e3cce --- /dev/null +++ b/dist/components/index.js.map @@ -0,0 +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 diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..e8be288 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,16 @@ +import { QuartzComponent } from '@quartz-community/types'; +export { QuartzComponent, QuartzComponentProps, StringResource } from '@quartz-community/types'; + +interface BreadcrumbOptions { + /** Symbol between crumbs */ + spacerSymbol: string; + /** Name of first crumb */ + rootName: string; + /** Whether to look up frontmatter title for folders */ + resolveFrontmatterTitle: boolean; + /** Whether to display the current page in the breadcrumbs */ + showCurrentPage: boolean; +} +declare const _default: (opts?: Partial) => QuartzComponent; + +export { type BreadcrumbOptions, _default as Breadcrumbs }; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..f0d6c6d --- /dev/null +++ b/dist/index.js @@ -0,0 +1,196 @@ +import { simplifySlug as simplifySlug$1, joinSegments } from '@quartz-community/utils'; +import { jsx, jsxs } from 'preact/jsx-runtime'; + +// src/util/lang.ts +function classNames(...classes) { + return classes.filter(Boolean).join(" "); +} +function simplifySlug(fp) { + return simplifySlug$1(fp); +} +function resolveRelative(current, target) { + const simplified = simplifySlug(target); + const rootPath = pathToRoot(current); + return joinSegments(rootPath, simplified); +} +function pathToRoot(slug) { + let rootPath = slug.split("/").filter((x) => x !== "").slice(0, -1).map((_) => "..").join("/"); + if (rootPath.length === 0) { + rootPath = "."; + } + return rootPath; +} +var FileTrieNode = class _FileTrieNode { + isFolder; + children; + slugSegments; + fileSegmentHint; + displayNameOverride; + data; + constructor(segments, data) { + this.children = []; + this.slugSegments = segments; + this.data = data ?? null; + this.isFolder = false; + this.displayNameOverride = void 0; + } + get displayName() { + const nonIndexTitle = this.data?.title === "index" ? void 0 : this.data?.title; + return this.displayNameOverride ?? nonIndexTitle ?? this.fileSegmentHint ?? this.slugSegment ?? ""; + } + set displayName(name) { + this.displayNameOverride = name; + } + get slug() { + const path = joinSegments(...this.slugSegments); + if (this.isFolder) { + return joinSegments(path, "index"); + } + return path; + } + get slugSegment() { + return this.slugSegments[this.slugSegments.length - 1] ?? ""; + } + makeChild(path, file) { + const nextSegment = path[0]; + if (!nextSegment) { + throw new Error("path is empty"); + } + const fullPath = [...this.slugSegments, nextSegment]; + const child = new _FileTrieNode(fullPath, file); + this.children.push(child); + return child; + } + insert(path, file) { + if (path.length === 0) { + throw new Error("path is empty"); + } + this.isFolder = true; + const segment = path[0]; + if (!segment) { + throw new Error("path is empty"); + } + if (path.length === 1) { + if (segment === "index") { + this.data ??= file; + } else { + this.makeChild(path, file); + } + } else if (path.length > 1) { + const child = this.children.find((c) => c.slugSegment === segment) ?? this.makeChild(path, void 0); + const fileParts = file.filePath.split("/"); + const hint = fileParts.at(-path.length); + if (hint) { + child.fileSegmentHint = hint; + } + child.insert(path.slice(1), file); + } + } + add(file) { + this.insert(file.slug.split("/"), file); + } + findNode(path) { + if (path.length === 0 || path.length === 1 && path[0] === "index") { + return this; + } + return this.children.find((c) => c.slugSegment === path[0])?.findNode(path.slice(1)); + } + ancestryChain(path) { + if (path.length === 0 || path.length === 1 && path[0] === "index") { + return [this]; + } + const child = this.children.find((c) => c.slugSegment === path[0]); + if (!child) { + return void 0; + } + const childPath = child.ancestryChain(path.slice(1)); + if (!childPath) { + return void 0; + } + return [this, ...childPath]; + } + filter(filterFn) { + this.children = this.children.filter(filterFn); + this.children.forEach((child) => child.filter(filterFn)); + } + map(mapFn) { + mapFn(this); + this.children.forEach((child) => child.map(mapFn)); + } + sort(sortFn) { + this.children = this.children.sort(sortFn); + this.children.forEach((e) => e.sort(sortFn)); + } +}; +function trieFromAllFiles(allFiles) { + const trie = new FileTrieNode([]); + allFiles.forEach((file) => { + if (file.frontmatter) { + trie.add({ + slug: file.slug, + title: file.frontmatter.title ?? "", + filePath: file.filePath + }); + } + }); + return trie; +} + +// src/components/styles/breadcrumbs.scss +var breadcrumbs_default = ".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}"; +var defaultOptions = { + spacerSymbol: "\u276F", + rootName: "Home", + resolveFrontmatterTitle: true, + showCurrentPage: true +}; +function formatCrumb(displayName, baseSlug, currentSlug) { + return { + displayName: displayName.replaceAll("-", " "), + path: resolveRelative(baseSlug, currentSlug) + }; +} +var Breadcrumbs_default = ((opts) => { + const options = { ...defaultOptions, ...opts }; + const Breadcrumbs = ({ + fileData, + allFiles, + displayClass, + ctx + }) => { + const typedCtx = ctx ?? {}; + typedCtx.trie ??= trieFromAllFiles( + allFiles + ); + const trie = typedCtx.trie; + const slug = fileData.slug; + const slugParts = slug.split("/"); + const pathNodes = trie.ancestryChain(slugParts); + if (!pathNodes) { + return null; + } + const crumbs = pathNodes.map((node, idx) => { + const crumb = formatCrumb(node.displayName, slug, simplifySlug(node.slug)); + if (idx === 0) { + crumb.displayName = options.rootName; + } + if (idx === pathNodes.length - 1) { + crumb.path = ""; + } + return crumb; + }); + if (!options.showCurrentPage) { + crumbs.pop(); + } + return /* @__PURE__ */ jsx("nav", { class: classNames(displayClass, "breadcrumb-container"), "aria-label": "breadcrumbs", children: crumbs.map((crumb, index) => /* @__PURE__ */ jsxs("div", { class: "breadcrumb-element", children: [ + /* @__PURE__ */ jsx("a", { href: crumb.path, children: crumb.displayName }), + index !== crumbs.length - 1 && /* @__PURE__ */ jsx("p", { children: ` ${options.spacerSymbol} ` }) + ] })) }); + }; + Breadcrumbs.css = breadcrumbs_default; + return Breadcrumbs; +}); + +export { Breadcrumbs_default as Breadcrumbs }; +//# sourceMappingURL=index.js.map +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map new file mode 100644 index 0000000..89064a9 --- /dev/null +++ b/dist/index.js.map @@ -0,0 +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 diff --git a/package-lock.json b/package-lock.json index 26f80f4..33edf43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,7 @@ "license": "MIT", "dependencies": { "@quartz-community/types": "github:quartz-community/types", - "@quartz-community/utils": "github:quartz-community/utils", - "tsup": "^8.5.0", - "typescript": "^5.9.3" + "@quartz-community/utils": "github:quartz-community/utils" }, "devDependencies": { "@types/hast": "^3.0.4", @@ -26,6 +24,8 @@ "prettier": "^3.6.2", "sass": "^1.97.3", "sass-embedded": "^1.79.5", + "tsup": "^8.5.0", + "typescript": "^5.9.3", "vfile": "^6.0.3", "vitest": "^2.1.9" }, @@ -4061,7 +4061,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", diff --git a/package.json b/package.json index 3d3c75f..9c3e53a 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "sideEffects": false, "scripts": { "build": "tsup", - "prepare": "npm run build", "dev": "tsup --watch", "lint": "eslint . --max-warnings=0", "format": "prettier . --check",