@quartz-community/types 0.3.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -32,10 +32,25 @@ type CSSResource = {
32
32
  inline?: boolean;
33
33
  spaPreserve?: boolean;
34
34
  };
35
+ /**
36
+ * A file or directory a plugin ships with the site (Quartz 5.1+), copied to
37
+ * `<output>/static/plugins/<to>`. Client scripts get its URL from `window.quartzStaticUrl`:
38
+ * `new URL("plugins/<to>/…", window.quartzStaticUrl)`. Use it for large or optional client code
39
+ * (loaded with `import()` only where needed) instead of inline scripts, which every page
40
+ * downloads. Older Quartz versions ignore `assets` and leave `quartzStaticUrl` undefined.
41
+ */
42
+ interface StaticAsset {
43
+ /** Absolute path of a file or directory on disk (e.g. resolved from a dependency). */
44
+ from: string;
45
+ /** Destination below `static/plugins/`, e.g. "my-plugin/vendor". */
46
+ to: string;
47
+ }
35
48
  interface StaticResources {
36
49
  css: CSSResource[];
37
50
  js: JSResource[];
38
51
  additionalHead: any[];
52
+ /** Files copied into the site output (Quartz 5.1+). See StaticAsset. */
53
+ assets?: StaticAsset[];
39
54
  }
40
55
  /**
41
56
  * Flat configuration object that components receive as `cfg` prop.
@@ -53,6 +68,41 @@ interface GlobalConfiguration {
53
68
  baseUrl?: string;
54
69
  theme?: unknown;
55
70
  locale?: string;
71
+ /** Built-in frontmatter parsing (Quartz 5.1+). */
72
+ frontmatter?: BuiltinFrontmatterConfig | boolean;
73
+ /** Built-in link processing, formerly crawl-links (Quartz 5.1+). */
74
+ links?: BuiltinLinksConfig | boolean;
75
+ /** Built-in draft handling, formerly remove-draft (Quartz 5.1+). */
76
+ drafts?: BuiltinDraftsConfig | boolean;
77
+ /** Built-in content index / sitemap / RSS, formerly content-index (Quartz 5.1+). */
78
+ contentIndex?: BuiltinContentIndexConfig | boolean;
79
+ }
80
+ interface BuiltinToggle {
81
+ enabled?: boolean;
82
+ order?: number;
83
+ }
84
+ interface BuiltinFrontmatterConfig extends BuiltinToggle {
85
+ delimiters?: string | [string, string];
86
+ language?: "yaml" | "toml";
87
+ }
88
+ interface BuiltinLinksConfig extends BuiltinToggle {
89
+ markdownLinkResolution?: "absolute" | "relative" | "shortest";
90
+ prettyLinks?: boolean;
91
+ openLinksInNewTab?: boolean;
92
+ lazyLoad?: boolean;
93
+ externalLinkIcon?: boolean;
94
+ disableBrokenWikilinks?: boolean;
95
+ }
96
+ interface BuiltinDraftsConfig extends BuiltinToggle {
97
+ publish?: boolean;
98
+ }
99
+ interface BuiltinContentIndexConfig extends BuiltinToggle {
100
+ enableSiteMap?: boolean;
101
+ enableRSS?: boolean;
102
+ rssLimit?: number;
103
+ rssFullHtml?: boolean;
104
+ rssSlug?: string;
105
+ includeEmptyFiles?: boolean;
56
106
  }
57
107
  /**
58
108
  * Full Quartz config with nested `configuration` property.
@@ -82,6 +132,11 @@ interface BuildCtx {
82
132
  allSlugs: FullSlug[];
83
133
  allFiles: FilePath[];
84
134
  incremental: boolean;
135
+ /**
136
+ * The site's content index (Quartz 5.1+): one entry per published page, virtual pages
137
+ * included. Available to emitters (except ComponentResources / PageTypeDispatcher); read-only.
138
+ */
139
+ contentIndex?: ReadonlyMap<FullSlug, Readonly<ContentDetails>>;
85
140
  }
86
141
  type QuartzPluginData = Data;
87
142
  type MarkdownContent = [Root, VFile];
@@ -114,7 +169,20 @@ interface PluginTypes {
114
169
  pageTypes: QuartzPageTypePluginInstance[];
115
170
  }
116
171
  type QuartzTransformerPlugin<Options extends OptionType = undefined> = (opts?: Options) => QuartzTransformerPluginInstance;
117
- type QuartzTransformerPluginInstance = {
172
+ /**
173
+ * Optional hook on every plugin instance (Quartz 5.1+): extra JSON-serialisable fields for a page's
174
+ * entry in `static/contentIndex.json`. Core fields cannot be overridden; on key collisions between
175
+ * plugins the first (in plugin order) wins.
176
+ */
177
+ type ContentIndexExtension = (args: {
178
+ ctx: BuildCtx;
179
+ fileData: QuartzPluginData;
180
+ entry: Readonly<ContentDetails>;
181
+ }) => Record<string, unknown> | undefined | void;
182
+ interface PluginHooks {
183
+ extendContentIndex?: ContentIndexExtension;
184
+ }
185
+ type QuartzTransformerPluginInstance = PluginHooks & {
118
186
  name: string;
119
187
  textTransform?: (ctx: BuildCtx, src: string) => string;
120
188
  markdownPlugins?: (ctx: BuildCtx) => PluggableList;
@@ -122,7 +190,7 @@ type QuartzTransformerPluginInstance = {
122
190
  externalResources?: ExternalResourcesFn;
123
191
  };
124
192
  type QuartzFilterPlugin<Options extends OptionType = undefined> = (opts?: Options) => QuartzFilterPluginInstance;
125
- type QuartzFilterPluginInstance = {
193
+ type QuartzFilterPluginInstance = PluginHooks & {
126
194
  name: string;
127
195
  shouldPublish(ctx: BuildCtx, content: ProcessedContent): boolean;
128
196
  };
@@ -132,7 +200,7 @@ type ChangeEvent = {
132
200
  file?: VFile;
133
201
  };
134
202
  type QuartzEmitterPlugin<Options extends OptionType = undefined> = (opts?: Options) => QuartzEmitterPluginInstance;
135
- type QuartzEmitterPluginInstance = {
203
+ type QuartzEmitterPluginInstance = PluginHooks & {
136
204
  name: string;
137
205
  emit: (ctx: BuildCtx, content: ProcessedContent[], resources: StaticResources) => Promise<FilePath[]> | AsyncGenerator<FilePath>;
138
206
  partialEmit?: (ctx: BuildCtx, content: ProcessedContent[], resources: StaticResources, changeEvents: ChangeEvent[]) => Promise<FilePath[]> | AsyncGenerator<FilePath> | null;
@@ -178,7 +246,7 @@ type TreeTransform = (root: Root$1, slug: FullSlug, componentData: QuartzCompone
178
246
  * and layout reference for rendering.
179
247
  */
180
248
  type QuartzPageTypePlugin<Options extends OptionType = undefined> = (opts?: Options) => QuartzPageTypePluginInstance;
181
- type QuartzPageTypePluginInstance = {
249
+ type QuartzPageTypePluginInstance = PluginHooks & {
182
250
  name: string;
183
251
  /** Higher priority wins when multiple page types match the same file. Default: 0. */
184
252
  priority?: number;
@@ -220,7 +288,21 @@ interface PageFrameProps {
220
288
  right: QuartzComponent[];
221
289
  /** Footer components */
222
290
  footer: QuartzComponent[];
291
+ /**
292
+ * Sidebar modes from `layout.sidebars` (Quartz 5.1+; undefined before). Frames with sidebars
293
+ * should put them on the sidebar elements as `data-sidebar-mode`; Quartz's stylesheet does the rest.
294
+ */
295
+ sidebars?: {
296
+ left: SidebarMode;
297
+ right: SidebarMode;
298
+ };
223
299
  }
300
+ /**
301
+ * How a sidebar handles content taller than the screen (`layout.sidebars`, Quartz 5.1+):
302
+ * `fit` (default) — one screen tall, components shrink and scroll; `scroll` — stays in view and
303
+ * scrolls itself; `static` — scrolls away with the page.
304
+ */
305
+ type SidebarMode = "fit" | "scroll" | "static";
224
306
  /**
225
307
  * A PageFrame defines the inner HTML structure of a page inside the
226
308
  * `<div id="quartz-root">` shell. Different frames can produce completely
@@ -253,6 +335,8 @@ type ContentDetails = {
253
335
  richContent?: string;
254
336
  date?: Date;
255
337
  description?: string;
338
+ /** Fields added by plugins through `extendContentIndex` (Quartz 5.1+). */
339
+ [extra: string]: unknown;
256
340
  };
257
341
  type ContentIndex = Record<FullSlug, ContentDetails>;
258
342
  type SortFn = (f1: QuartzPluginData, f2: QuartzPluginData) => number;
@@ -304,4 +388,4 @@ declare module "vfile" {
304
388
  }
305
389
  }
306
390
 
307
- export { type Argv, type BuildCtx, type CSSResource, type ChangeEvent, type ClassValue, type CleanupFunction, type ContentDetails, type ContentIndex, type EventHandler, type ExternalResourcesFn, type FilePath, type FullSlug, type GlobalConfiguration, type HTMLAttributes, type JSResource, type MarkdownContent, type PageFrame, type PageFrameProps, type PageGenerator, type PageMatcher, type PluginTypes, type ProcessedContent, type QuartzComponent, type QuartzComponentConstructor, type QuartzComponentProps, type QuartzConfig, type QuartzEmitterPlugin, type QuartzEmitterPluginInstance, type QuartzFilterPlugin, type QuartzFilterPluginInstance, type QuartzPageTypePlugin, type QuartzPageTypePluginInstance, type QuartzPluginData, type QuartzTransformerPlugin, type QuartzTransformerPluginInstance, type SimpleSlug, type SortFn, type StaticResources, type StringResource, type ThemeKey, type TreeTransform, type ValidDateType, type VirtualPage, joinSegments };
391
+ export { type Argv, type BuildCtx, type BuiltinContentIndexConfig, type BuiltinDraftsConfig, type BuiltinFrontmatterConfig, type BuiltinLinksConfig, type CSSResource, type ChangeEvent, type ClassValue, type CleanupFunction, type ContentDetails, type ContentIndex, type ContentIndexExtension, type EventHandler, type ExternalResourcesFn, type FilePath, type FullSlug, type GlobalConfiguration, type HTMLAttributes, type JSResource, type MarkdownContent, type PageFrame, type PageFrameProps, type PageGenerator, type PageMatcher, type PluginTypes, type ProcessedContent, type QuartzComponent, type QuartzComponentConstructor, type QuartzComponentProps, type QuartzConfig, type QuartzEmitterPlugin, type QuartzEmitterPluginInstance, type QuartzFilterPlugin, type QuartzFilterPluginInstance, type QuartzPageTypePlugin, type QuartzPageTypePluginInstance, type QuartzPluginData, type QuartzTransformerPlugin, type QuartzTransformerPluginInstance, type SidebarMode, type SimpleSlug, type SortFn, type StaticAsset, type StaticResources, type StringResource, type ThemeKey, type TreeTransform, type ValidDateType, type VirtualPage, joinSegments };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Root as HtmlRoot } from \"hast\";\nimport type { Root as MdRoot } from \"mdast\";\nimport type { Data, VFile } from \"vfile\";\nimport type { PluggableList } from \"unified\";\n\n// ============================================================================\n// Path Types - Branded string types for type-safe file paths\n// ============================================================================\n\nexport type FilePath = string & { _brand: \"FilePath\" };\nexport type FullSlug = string & { _brand: \"FullSlug\" };\nexport type SimpleSlug = string & { _brand: \"SimpleSlug\" };\n\nexport function joinSegments(...segments: string[]): FilePath {\n return segments\n .filter((segment) => segment.length > 0)\n .join(\"/\")\n .replace(/\\/+/g, \"/\") as FilePath;\n}\n\n// ============================================================================\n// Resource Types - CSS/JS resources that plugins can inject\n// ============================================================================\n\nexport type JSResource =\n | {\n loadTime: \"beforeDOMReady\" | \"afterDOMReady\";\n moduleType?: \"module\";\n spaPreserve?: boolean;\n src: string;\n contentType: \"external\";\n }\n | {\n loadTime: \"beforeDOMReady\" | \"afterDOMReady\";\n moduleType?: \"module\";\n spaPreserve?: boolean;\n script: string;\n contentType: \"inline\";\n };\n\nexport type CSSResource = {\n content: string;\n inline?: boolean;\n spaPreserve?: boolean;\n};\n\nexport interface StaticResources {\n css: CSSResource[];\n js: JSResource[];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n additionalHead: any[];\n}\n\n// ============================================================================\n// Config Types - Quartz configuration\n// ============================================================================\n\n/**\n * Flat configuration object that components receive as `cfg` prop.\n * This matches Quartz's `GlobalConfiguration` — the resolved config\n * passed to components at render time (i.e. `ctx.cfg.configuration`).\n */\nexport interface GlobalConfiguration {\n pageTitle?: string;\n pageTitleSuffix?: string;\n enableSPA?: boolean;\n enablePopovers?: boolean;\n analytics?: unknown;\n ignorePatterns?: string[];\n defaultDateType?: string;\n baseUrl?: string;\n theme?: unknown;\n locale?: string;\n}\n\n/**\n * Full Quartz config with nested `configuration` property.\n * This is what `BuildCtx.cfg` holds — used in plugin hooks\n * (transformers, filters, emitters), NOT in component props.\n */\nexport interface QuartzConfig {\n configuration: GlobalConfiguration;\n plugins?: unknown;\n externalPlugins?: unknown;\n}\n\n// ============================================================================\n// Context Types - Build context passed to plugin hooks\n// ============================================================================\n\nexport interface Argv {\n directory: string;\n verbose: boolean;\n output: string;\n serve: boolean;\n watch: boolean;\n port: number;\n wsPort: number;\n remoteDevHost?: string;\n concurrency?: number;\n}\n\nexport interface BuildCtx {\n buildId: string;\n argv: Argv;\n cfg: QuartzConfig;\n allSlugs: FullSlug[];\n allFiles: FilePath[];\n incremental: boolean;\n}\n\n// ============================================================================\n// VFile Types - Content data types\n// ============================================================================\n\nexport type QuartzPluginData = Data;\nexport type MarkdownContent = [MdRoot, VFile];\nexport type ProcessedContent = [HtmlRoot, VFile];\n\n// ============================================================================\n// Core Component Types - The contract between Quartz and plugins\n// ============================================================================\n\nexport type QuartzComponentProps = {\n ctx: unknown;\n externalResources: StaticResources;\n fileData: QuartzPluginData & Record<string, unknown>;\n cfg: GlobalConfiguration;\n children: unknown;\n tree: unknown;\n allFiles: (QuartzPluginData & Record<string, unknown>)[];\n displayClass?: \"mobile-only\" | \"desktop-only\";\n [key: string]: unknown;\n};\n\nexport type QuartzComponent = ((props: QuartzComponentProps) => unknown) & {\n css?: string | string[] | undefined;\n beforeDOMLoaded?: string | string[] | undefined;\n afterDOMLoaded?: string | string[] | undefined;\n};\n\nexport type QuartzComponentConstructor<\n Options extends object | undefined = undefined,\n> = (opts?: Options) => QuartzComponent;\n\nexport type StringResource = string | string[];\n\n// ============================================================================\n// Plugin Types - What Quartz expects from plugins\n// ============================================================================\n\ntype OptionType = object | undefined;\ntype ExternalResourcesFn = (\n ctx: BuildCtx,\n) => Partial<StaticResources> | undefined;\n\nexport type { ExternalResourcesFn };\n\nexport interface PluginTypes {\n transformers: QuartzTransformerPluginInstance[];\n filters: QuartzFilterPluginInstance[];\n emitters: QuartzEmitterPluginInstance[];\n pageTypes: QuartzPageTypePluginInstance[];\n}\n\nexport type QuartzTransformerPlugin<Options extends OptionType = undefined> = (\n opts?: Options,\n) => QuartzTransformerPluginInstance;\n\nexport type QuartzTransformerPluginInstance = {\n name: string;\n textTransform?: (ctx: BuildCtx, src: string) => string;\n markdownPlugins?: (ctx: BuildCtx) => PluggableList;\n htmlPlugins?: (ctx: BuildCtx) => PluggableList;\n externalResources?: ExternalResourcesFn;\n};\n\nexport type QuartzFilterPlugin<Options extends OptionType = undefined> = (\n opts?: Options,\n) => QuartzFilterPluginInstance;\n\nexport type QuartzFilterPluginInstance = {\n name: string;\n shouldPublish(ctx: BuildCtx, content: ProcessedContent): boolean;\n};\n\nexport type ChangeEvent = {\n type: \"add\" | \"change\" | \"delete\";\n path: FilePath;\n file?: VFile;\n};\n\nexport type QuartzEmitterPlugin<Options extends OptionType = undefined> = (\n opts?: Options,\n) => QuartzEmitterPluginInstance;\n\nexport type QuartzEmitterPluginInstance = {\n name: string;\n emit: (\n ctx: BuildCtx,\n content: ProcessedContent[],\n resources: StaticResources,\n ) => Promise<FilePath[]> | AsyncGenerator<FilePath>;\n partialEmit?: (\n ctx: BuildCtx,\n content: ProcessedContent[],\n resources: StaticResources,\n changeEvents: ChangeEvent[],\n ) => Promise<FilePath[]> | AsyncGenerator<FilePath> | null;\n getQuartzComponents?: (ctx: BuildCtx) => QuartzComponent[];\n externalResources?: ExternalResourcesFn;\n};\n\n// ============================================================================\n// PageType Plugin Types - Declarative page rendering plugins\n// ============================================================================\n\n/**\n * Matcher function: determines if a source file belongs to a page type.\n * Returns true if the page type should own this file.\n */\nexport type PageMatcher = (args: {\n slug: FullSlug;\n fileData: QuartzPluginData & Record<string, unknown>;\n cfg: GlobalConfiguration;\n}) => boolean;\n\n/**\n * Virtual page descriptor for page types that generate pages\n * from aggregated data (e.g., tag indexes, folder listings).\n */\nexport interface VirtualPage {\n slug: FullSlug;\n title: string;\n data: Partial<QuartzPluginData> & Record<string, unknown>;\n}\n\n/**\n * Generator function: produces virtual pages from all processed content.\n * Used by page types that don't match source files but instead create\n * synthetic pages (e.g., one page per tag, one page per folder).\n */\nexport type PageGenerator = (args: {\n content: ProcessedContent[];\n cfg: GlobalConfiguration;\n ctx: BuildCtx;\n}) => VirtualPage[];\n\n/** A function that mutates a HAST tree at render time, when allFiles is available. */\nexport type TreeTransform = (\n root: HtmlRoot,\n slug: FullSlug,\n componentData: QuartzComponentProps,\n) => void;\n\n/**\n * A PageType plugin definition.\n *\n * PageTypes are a declarative abstraction over page-rendering emitters.\n * Each PageType declares which files it owns (via `match`), optionally\n * generates virtual pages (via `generate`), and provides a body component\n * and layout reference for rendering.\n */\nexport type QuartzPageTypePlugin<Options extends OptionType = undefined> = (\n opts?: Options,\n) => QuartzPageTypePluginInstance;\n\nexport type QuartzPageTypePluginInstance = {\n name: string;\n /** Higher priority wins when multiple page types match the same file. Default: 0. */\n priority?: number;\n /** File extensions this page type handles (e.g. [\".canvas\"]). Files with these extensions are excluded from the default asset emitter and processed by generate() instead. */\n fileExtensions?: string[];\n /** Determines which source files this page type owns. */\n match: PageMatcher;\n /** Produces virtual pages from aggregated content data. */\n generate?: PageGenerator;\n /** Layout key — references a key in `layout.byPageType`. */\n layout: string;\n /** Optional page frame name (e.g. \"default\", \"full-width\", \"canvas\"). Defaults to \"default\". */\n frame?: string;\n /** The body component constructor for this page type. */\n body: QuartzComponentConstructor;\n /** Optional render-time HAST tree transforms (e.g. resolving inline codeblocks). */\n treeTransforms?: (ctx: BuildCtx) => TreeTransform[];\n};\n\n// ============================================================================\n// PageFrame Types — Custom page layout frames\n// ============================================================================\n\n/**\n * Props passed to a PageFrame's render function.\n * Contains the resolved layout components and the shared component data.\n */\nexport interface PageFrameProps {\n /** Component data shared across all components on the page */\n componentData: QuartzComponentProps;\n /** The Head component (rendered in <head>) — NOT used by frames, included for completeness */\n head: QuartzComponent;\n /** Header slot components (rendered inside <header>) */\n header: QuartzComponent[];\n /** Components rendered before the page body */\n beforeBody: QuartzComponent[];\n /** The page body component (Content) */\n pageBody: QuartzComponent;\n /** Components rendered after the page body */\n afterBody: QuartzComponent[];\n /** Left sidebar components */\n left: QuartzComponent[];\n /** Right sidebar components */\n right: QuartzComponent[];\n /** Footer components */\n footer: QuartzComponent[];\n}\n\n/**\n * A PageFrame defines the inner HTML structure of a page inside the\n * `<div id=\"quartz-root\">` shell. Different frames can produce completely\n * different layouts (e.g. with/without sidebars, horizontal scroll, etc.)\n * while the outer shell (html, head, body, quartz-root) remains stable\n * for SPA navigation.\n *\n * The `render` function returns `unknown` to avoid coupling to a specific\n * JSX runtime (Preact, React, etc.). Quartz core casts this to JSX.Element.\n */\nexport interface PageFrame {\n /** Unique name for this frame (e.g. \"default\", \"full-width\", \"canvas\") */\n name: string;\n /** Render the inner page structure. Returns a JSX tree to be placed inside Body > #quartz-body. */\n render: (props: PageFrameProps) => unknown;\n /** Optional CSS string to include when this frame is active */\n css?: string;\n}\n\nexport type HTMLAttributes = Record<\n string,\n string | number | boolean | undefined\n>;\n\nexport type EventHandler = (event: Event) => void;\n\nexport type CleanupFunction = () => void;\n\nexport type ClassValue =\n | string\n | number\n | boolean\n | undefined\n | null\n | ClassValue[];\n\n// ============================================================================\n// Content Index Types - Data shape for contentIndex.json\n// ============================================================================\n\nexport type ContentDetails = {\n slug: FullSlug;\n filePath: FilePath;\n title: string;\n links: string[];\n tags: string[];\n content: string;\n richContent?: string;\n date?: Date;\n description?: string;\n};\n\nexport type ContentIndex = Record<FullSlug, ContentDetails>;\n\n// ============================================================================\n// Sort & Theme Types\n// ============================================================================\n\nexport type SortFn = (f1: QuartzPluginData, f2: QuartzPluginData) => number;\n\nexport type ThemeKey = \"lightMode\" | \"darkMode\";\n\nexport type ValidDateType = \"created\" | \"modified\" | \"published\";\n\n// ============================================================================\n// VFile DataMap Augmentation — canonical vfile shape for Quartz\n// ============================================================================\n\ndeclare module \"vfile\" {\n interface DataMap {\n slug: FullSlug;\n filePath: FilePath;\n relativePath: FilePath;\n description: string;\n text: string;\n links: SimpleSlug[];\n toc: { depth: number; text: string; slug: string }[];\n collapseToc: boolean;\n blocks: Record<string, import(\"hast\").Element>;\n htmlAst: import(\"hast\").Root;\n hasMermaidDiagram: boolean | undefined;\n frontmatter: { [key: string]: unknown } & {\n title: string;\n } & Partial<{\n tags: string[];\n aliases: string[];\n modified: string;\n created: string;\n published: string;\n description: string;\n socialDescription: string;\n publish: boolean | string;\n draft: boolean | string;\n lang: string;\n enableToc: string;\n cssclasses: string[];\n socialImage: string;\n comments: boolean | string;\n }>;\n dates: {\n created: Date;\n modified: Date;\n published: Date;\n };\n defaultDateType?: ValidDateType;\n }\n}\n"],"mappings":";AAaO,SAAS,gBAAgB,UAA8B;AAC5D,SAAO,SACJ,OAAO,CAAC,YAAY,QAAQ,SAAS,CAAC,EACtC,KAAK,GAAG,EACR,QAAQ,QAAQ,GAAG;AACxB;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Root as HtmlRoot } from \"hast\";\nimport type { Root as MdRoot } from \"mdast\";\nimport type { Data, VFile } from \"vfile\";\nimport type { PluggableList } from \"unified\";\n\n// ============================================================================\n// Path Types - Branded string types for type-safe file paths\n// ============================================================================\n\nexport type FilePath = string & { _brand: \"FilePath\" };\nexport type FullSlug = string & { _brand: \"FullSlug\" };\nexport type SimpleSlug = string & { _brand: \"SimpleSlug\" };\n\nexport function joinSegments(...segments: string[]): FilePath {\n return segments\n .filter((segment) => segment.length > 0)\n .join(\"/\")\n .replace(/\\/+/g, \"/\") as FilePath;\n}\n\n// ============================================================================\n// Resource Types - CSS/JS resources that plugins can inject\n// ============================================================================\n\nexport type JSResource =\n | {\n loadTime: \"beforeDOMReady\" | \"afterDOMReady\";\n moduleType?: \"module\";\n spaPreserve?: boolean;\n src: string;\n contentType: \"external\";\n }\n | {\n loadTime: \"beforeDOMReady\" | \"afterDOMReady\";\n moduleType?: \"module\";\n spaPreserve?: boolean;\n script: string;\n contentType: \"inline\";\n };\n\nexport type CSSResource = {\n content: string;\n inline?: boolean;\n spaPreserve?: boolean;\n};\n\n/**\n * A file or directory a plugin ships with the site (Quartz 5.1+), copied to\n * `<output>/static/plugins/<to>`. Client scripts get its URL from `window.quartzStaticUrl`:\n * `new URL(\"plugins/<to>/…\", window.quartzStaticUrl)`. Use it for large or optional client code\n * (loaded with `import()` only where needed) instead of inline scripts, which every page\n * downloads. Older Quartz versions ignore `assets` and leave `quartzStaticUrl` undefined.\n */\nexport interface StaticAsset {\n /** Absolute path of a file or directory on disk (e.g. resolved from a dependency). */\n from: string;\n /** Destination below `static/plugins/`, e.g. \"my-plugin/vendor\". */\n to: string;\n}\n\nexport interface StaticResources {\n css: CSSResource[];\n js: JSResource[];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n additionalHead: any[];\n /** Files copied into the site output (Quartz 5.1+). See StaticAsset. */\n assets?: StaticAsset[];\n}\n\n// ============================================================================\n// Config Types - Quartz configuration\n// ============================================================================\n\n/**\n * Flat configuration object that components receive as `cfg` prop.\n * This matches Quartz's `GlobalConfiguration` — the resolved config\n * passed to components at render time (i.e. `ctx.cfg.configuration`).\n */\nexport interface GlobalConfiguration {\n pageTitle?: string;\n pageTitleSuffix?: string;\n enableSPA?: boolean;\n enablePopovers?: boolean;\n analytics?: unknown;\n ignorePatterns?: string[];\n defaultDateType?: string;\n baseUrl?: string;\n theme?: unknown;\n locale?: string;\n /** Built-in frontmatter parsing (Quartz 5.1+). */\n frontmatter?: BuiltinFrontmatterConfig | boolean;\n /** Built-in link processing, formerly crawl-links (Quartz 5.1+). */\n links?: BuiltinLinksConfig | boolean;\n /** Built-in draft handling, formerly remove-draft (Quartz 5.1+). */\n drafts?: BuiltinDraftsConfig | boolean;\n /** Built-in content index / sitemap / RSS, formerly content-index (Quartz 5.1+). */\n contentIndex?: BuiltinContentIndexConfig | boolean;\n}\n\ninterface BuiltinToggle {\n enabled?: boolean;\n order?: number;\n}\n\nexport interface BuiltinFrontmatterConfig extends BuiltinToggle {\n delimiters?: string | [string, string];\n language?: \"yaml\" | \"toml\";\n}\n\nexport interface BuiltinLinksConfig extends BuiltinToggle {\n markdownLinkResolution?: \"absolute\" | \"relative\" | \"shortest\";\n prettyLinks?: boolean;\n openLinksInNewTab?: boolean;\n lazyLoad?: boolean;\n externalLinkIcon?: boolean;\n disableBrokenWikilinks?: boolean;\n}\n\nexport interface BuiltinDraftsConfig extends BuiltinToggle {\n publish?: boolean;\n}\n\nexport interface BuiltinContentIndexConfig extends BuiltinToggle {\n enableSiteMap?: boolean;\n enableRSS?: boolean;\n rssLimit?: number;\n rssFullHtml?: boolean;\n rssSlug?: string;\n includeEmptyFiles?: boolean;\n}\n\n/**\n * Full Quartz config with nested `configuration` property.\n * This is what `BuildCtx.cfg` holds — used in plugin hooks\n * (transformers, filters, emitters), NOT in component props.\n */\nexport interface QuartzConfig {\n configuration: GlobalConfiguration;\n plugins?: unknown;\n externalPlugins?: unknown;\n}\n\n// ============================================================================\n// Context Types - Build context passed to plugin hooks\n// ============================================================================\n\nexport interface Argv {\n directory: string;\n verbose: boolean;\n output: string;\n serve: boolean;\n watch: boolean;\n port: number;\n wsPort: number;\n remoteDevHost?: string;\n concurrency?: number;\n}\n\nexport interface BuildCtx {\n buildId: string;\n argv: Argv;\n cfg: QuartzConfig;\n allSlugs: FullSlug[];\n allFiles: FilePath[];\n incremental: boolean;\n /**\n * The site's content index (Quartz 5.1+): one entry per published page, virtual pages\n * included. Available to emitters (except ComponentResources / PageTypeDispatcher); read-only.\n */\n contentIndex?: ReadonlyMap<FullSlug, Readonly<ContentDetails>>;\n}\n\n// ============================================================================\n// VFile Types - Content data types\n// ============================================================================\n\nexport type QuartzPluginData = Data;\nexport type MarkdownContent = [MdRoot, VFile];\nexport type ProcessedContent = [HtmlRoot, VFile];\n\n// ============================================================================\n// Core Component Types - The contract between Quartz and plugins\n// ============================================================================\n\nexport type QuartzComponentProps = {\n ctx: unknown;\n externalResources: StaticResources;\n fileData: QuartzPluginData & Record<string, unknown>;\n cfg: GlobalConfiguration;\n children: unknown;\n tree: unknown;\n allFiles: (QuartzPluginData & Record<string, unknown>)[];\n displayClass?: \"mobile-only\" | \"desktop-only\";\n [key: string]: unknown;\n};\n\nexport type QuartzComponent = ((props: QuartzComponentProps) => unknown) & {\n css?: string | string[] | undefined;\n beforeDOMLoaded?: string | string[] | undefined;\n afterDOMLoaded?: string | string[] | undefined;\n};\n\nexport type QuartzComponentConstructor<\n Options extends object | undefined = undefined,\n> = (opts?: Options) => QuartzComponent;\n\nexport type StringResource = string | string[];\n\n// ============================================================================\n// Plugin Types - What Quartz expects from plugins\n// ============================================================================\n\ntype OptionType = object | undefined;\ntype ExternalResourcesFn = (\n ctx: BuildCtx,\n) => Partial<StaticResources> | undefined;\n\nexport type { ExternalResourcesFn };\n\nexport interface PluginTypes {\n transformers: QuartzTransformerPluginInstance[];\n filters: QuartzFilterPluginInstance[];\n emitters: QuartzEmitterPluginInstance[];\n pageTypes: QuartzPageTypePluginInstance[];\n}\n\nexport type QuartzTransformerPlugin<Options extends OptionType = undefined> = (\n opts?: Options,\n) => QuartzTransformerPluginInstance;\n\n/**\n * Optional hook on every plugin instance (Quartz 5.1+): extra JSON-serialisable fields for a page's\n * entry in `static/contentIndex.json`. Core fields cannot be overridden; on key collisions between\n * plugins the first (in plugin order) wins.\n */\nexport type ContentIndexExtension = (args: {\n ctx: BuildCtx;\n fileData: QuartzPluginData;\n entry: Readonly<ContentDetails>;\n}) => Record<string, unknown> | undefined | void;\n\ninterface PluginHooks {\n extendContentIndex?: ContentIndexExtension;\n}\n\nexport type QuartzTransformerPluginInstance = PluginHooks & {\n name: string;\n textTransform?: (ctx: BuildCtx, src: string) => string;\n markdownPlugins?: (ctx: BuildCtx) => PluggableList;\n htmlPlugins?: (ctx: BuildCtx) => PluggableList;\n externalResources?: ExternalResourcesFn;\n};\n\nexport type QuartzFilterPlugin<Options extends OptionType = undefined> = (\n opts?: Options,\n) => QuartzFilterPluginInstance;\n\nexport type QuartzFilterPluginInstance = PluginHooks & {\n name: string;\n shouldPublish(ctx: BuildCtx, content: ProcessedContent): boolean;\n};\n\nexport type ChangeEvent = {\n type: \"add\" | \"change\" | \"delete\";\n path: FilePath;\n file?: VFile;\n};\n\nexport type QuartzEmitterPlugin<Options extends OptionType = undefined> = (\n opts?: Options,\n) => QuartzEmitterPluginInstance;\n\nexport type QuartzEmitterPluginInstance = PluginHooks & {\n name: string;\n emit: (\n ctx: BuildCtx,\n content: ProcessedContent[],\n resources: StaticResources,\n ) => Promise<FilePath[]> | AsyncGenerator<FilePath>;\n partialEmit?: (\n ctx: BuildCtx,\n content: ProcessedContent[],\n resources: StaticResources,\n changeEvents: ChangeEvent[],\n ) => Promise<FilePath[]> | AsyncGenerator<FilePath> | null;\n getQuartzComponents?: (ctx: BuildCtx) => QuartzComponent[];\n externalResources?: ExternalResourcesFn;\n};\n\n// ============================================================================\n// PageType Plugin Types - Declarative page rendering plugins\n// ============================================================================\n\n/**\n * Matcher function: determines if a source file belongs to a page type.\n * Returns true if the page type should own this file.\n */\nexport type PageMatcher = (args: {\n slug: FullSlug;\n fileData: QuartzPluginData & Record<string, unknown>;\n cfg: GlobalConfiguration;\n}) => boolean;\n\n/**\n * Virtual page descriptor for page types that generate pages\n * from aggregated data (e.g., tag indexes, folder listings).\n */\nexport interface VirtualPage {\n slug: FullSlug;\n title: string;\n data: Partial<QuartzPluginData> & Record<string, unknown>;\n}\n\n/**\n * Generator function: produces virtual pages from all processed content.\n * Used by page types that don't match source files but instead create\n * synthetic pages (e.g., one page per tag, one page per folder).\n */\nexport type PageGenerator = (args: {\n content: ProcessedContent[];\n cfg: GlobalConfiguration;\n ctx: BuildCtx;\n}) => VirtualPage[];\n\n/** A function that mutates a HAST tree at render time, when allFiles is available. */\nexport type TreeTransform = (\n root: HtmlRoot,\n slug: FullSlug,\n componentData: QuartzComponentProps,\n) => void;\n\n/**\n * A PageType plugin definition.\n *\n * PageTypes are a declarative abstraction over page-rendering emitters.\n * Each PageType declares which files it owns (via `match`), optionally\n * generates virtual pages (via `generate`), and provides a body component\n * and layout reference for rendering.\n */\nexport type QuartzPageTypePlugin<Options extends OptionType = undefined> = (\n opts?: Options,\n) => QuartzPageTypePluginInstance;\n\nexport type QuartzPageTypePluginInstance = PluginHooks & {\n name: string;\n /** Higher priority wins when multiple page types match the same file. Default: 0. */\n priority?: number;\n /** File extensions this page type handles (e.g. [\".canvas\"]). Files with these extensions are excluded from the default asset emitter and processed by generate() instead. */\n fileExtensions?: string[];\n /** Determines which source files this page type owns. */\n match: PageMatcher;\n /** Produces virtual pages from aggregated content data. */\n generate?: PageGenerator;\n /** Layout key — references a key in `layout.byPageType`. */\n layout: string;\n /** Optional page frame name (e.g. \"default\", \"full-width\", \"canvas\"). Defaults to \"default\". */\n frame?: string;\n /** The body component constructor for this page type. */\n body: QuartzComponentConstructor;\n /** Optional render-time HAST tree transforms (e.g. resolving inline codeblocks). */\n treeTransforms?: (ctx: BuildCtx) => TreeTransform[];\n};\n\n// ============================================================================\n// PageFrame Types — Custom page layout frames\n// ============================================================================\n\n/**\n * Props passed to a PageFrame's render function.\n * Contains the resolved layout components and the shared component data.\n */\nexport interface PageFrameProps {\n /** Component data shared across all components on the page */\n componentData: QuartzComponentProps;\n /** The Head component (rendered in <head>) — NOT used by frames, included for completeness */\n head: QuartzComponent;\n /** Header slot components (rendered inside <header>) */\n header: QuartzComponent[];\n /** Components rendered before the page body */\n beforeBody: QuartzComponent[];\n /** The page body component (Content) */\n pageBody: QuartzComponent;\n /** Components rendered after the page body */\n afterBody: QuartzComponent[];\n /** Left sidebar components */\n left: QuartzComponent[];\n /** Right sidebar components */\n right: QuartzComponent[];\n /** Footer components */\n footer: QuartzComponent[];\n /**\n * Sidebar modes from `layout.sidebars` (Quartz 5.1+; undefined before). Frames with sidebars\n * should put them on the sidebar elements as `data-sidebar-mode`; Quartz's stylesheet does the rest.\n */\n sidebars?: { left: SidebarMode; right: SidebarMode };\n}\n\n/**\n * How a sidebar handles content taller than the screen (`layout.sidebars`, Quartz 5.1+):\n * `fit` (default) — one screen tall, components shrink and scroll; `scroll` — stays in view and\n * scrolls itself; `static` — scrolls away with the page.\n */\nexport type SidebarMode = \"fit\" | \"scroll\" | \"static\";\n\n/**\n * A PageFrame defines the inner HTML structure of a page inside the\n * `<div id=\"quartz-root\">` shell. Different frames can produce completely\n * different layouts (e.g. with/without sidebars, horizontal scroll, etc.)\n * while the outer shell (html, head, body, quartz-root) remains stable\n * for SPA navigation.\n *\n * The `render` function returns `unknown` to avoid coupling to a specific\n * JSX runtime (Preact, React, etc.). Quartz core casts this to JSX.Element.\n */\nexport interface PageFrame {\n /** Unique name for this frame (e.g. \"default\", \"full-width\", \"canvas\") */\n name: string;\n /** Render the inner page structure. Returns a JSX tree to be placed inside Body > #quartz-body. */\n render: (props: PageFrameProps) => unknown;\n /** Optional CSS string to include when this frame is active */\n css?: string;\n}\n\nexport type HTMLAttributes = Record<\n string,\n string | number | boolean | undefined\n>;\n\nexport type EventHandler = (event: Event) => void;\n\nexport type CleanupFunction = () => void;\n\nexport type ClassValue =\n string | number | boolean | undefined | null | ClassValue[];\n\n// ============================================================================\n// Content Index Types - Data shape for contentIndex.json\n// ============================================================================\n\nexport type ContentDetails = {\n slug: FullSlug;\n filePath: FilePath;\n title: string;\n links: string[];\n tags: string[];\n content: string;\n richContent?: string;\n date?: Date;\n description?: string;\n /** Fields added by plugins through `extendContentIndex` (Quartz 5.1+). */\n [extra: string]: unknown;\n};\n\nexport type ContentIndex = Record<FullSlug, ContentDetails>;\n\n// ============================================================================\n// Sort & Theme Types\n// ============================================================================\n\nexport type SortFn = (f1: QuartzPluginData, f2: QuartzPluginData) => number;\n\nexport type ThemeKey = \"lightMode\" | \"darkMode\";\n\nexport type ValidDateType = \"created\" | \"modified\" | \"published\";\n\n// ============================================================================\n// VFile DataMap Augmentation — canonical vfile shape for Quartz\n// ============================================================================\n\ndeclare module \"vfile\" {\n interface DataMap {\n slug: FullSlug;\n filePath: FilePath;\n relativePath: FilePath;\n description: string;\n text: string;\n links: SimpleSlug[];\n toc: { depth: number; text: string; slug: string }[];\n collapseToc: boolean;\n blocks: Record<string, import(\"hast\").Element>;\n htmlAst: import(\"hast\").Root;\n hasMermaidDiagram: boolean | undefined;\n frontmatter: { [key: string]: unknown } & {\n title: string;\n } & Partial<{\n tags: string[];\n aliases: string[];\n modified: string;\n created: string;\n published: string;\n description: string;\n socialDescription: string;\n publish: boolean | string;\n draft: boolean | string;\n lang: string;\n enableToc: string;\n cssclasses: string[];\n socialImage: string;\n comments: boolean | string;\n }>;\n dates: {\n created: Date;\n modified: Date;\n published: Date;\n };\n defaultDateType?: ValidDateType;\n }\n}\n"],"mappings":";AAaO,SAAS,gBAAgB,UAA8B;AAC5D,SAAO,SACJ,OAAO,CAAC,YAAY,QAAQ,SAAS,CAAC,EACtC,KAAK,GAAG,EACR,QAAQ,QAAQ,GAAG;AACxB;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quartz-community/types",
3
- "version": "0.3.0",
3
+ "version": "1.1.0",
4
4
  "description": "Type definitions for Quartz community plugins",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -41,7 +41,8 @@
41
41
  "dev": "tsup --watch",
42
42
  "typecheck": "tsc --noEmit",
43
43
  "prepublishOnly": "npm run build",
44
- "release": "npm run build && changeset publish"
44
+ "release": "npm run build && changeset publish",
45
+ "version-packages": "changeset version && npm install --package-lock-only --ignore-scripts --no-audit --no-fund"
45
46
  },
46
47
  "dependencies": {
47
48
  "unified": "^11.0.5",
package/src/index.ts CHANGED
@@ -44,11 +44,27 @@ export type CSSResource = {
44
44
  spaPreserve?: boolean;
45
45
  };
46
46
 
47
+ /**
48
+ * A file or directory a plugin ships with the site (Quartz 5.1+), copied to
49
+ * `<output>/static/plugins/<to>`. Client scripts get its URL from `window.quartzStaticUrl`:
50
+ * `new URL("plugins/<to>/…", window.quartzStaticUrl)`. Use it for large or optional client code
51
+ * (loaded with `import()` only where needed) instead of inline scripts, which every page
52
+ * downloads. Older Quartz versions ignore `assets` and leave `quartzStaticUrl` undefined.
53
+ */
54
+ export interface StaticAsset {
55
+ /** Absolute path of a file or directory on disk (e.g. resolved from a dependency). */
56
+ from: string;
57
+ /** Destination below `static/plugins/`, e.g. "my-plugin/vendor". */
58
+ to: string;
59
+ }
60
+
47
61
  export interface StaticResources {
48
62
  css: CSSResource[];
49
63
  js: JSResource[];
50
64
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
65
  additionalHead: any[];
66
+ /** Files copied into the site output (Quartz 5.1+). See StaticAsset. */
67
+ assets?: StaticAsset[];
52
68
  }
53
69
 
54
70
  // ============================================================================
@@ -71,6 +87,46 @@ export interface GlobalConfiguration {
71
87
  baseUrl?: string;
72
88
  theme?: unknown;
73
89
  locale?: string;
90
+ /** Built-in frontmatter parsing (Quartz 5.1+). */
91
+ frontmatter?: BuiltinFrontmatterConfig | boolean;
92
+ /** Built-in link processing, formerly crawl-links (Quartz 5.1+). */
93
+ links?: BuiltinLinksConfig | boolean;
94
+ /** Built-in draft handling, formerly remove-draft (Quartz 5.1+). */
95
+ drafts?: BuiltinDraftsConfig | boolean;
96
+ /** Built-in content index / sitemap / RSS, formerly content-index (Quartz 5.1+). */
97
+ contentIndex?: BuiltinContentIndexConfig | boolean;
98
+ }
99
+
100
+ interface BuiltinToggle {
101
+ enabled?: boolean;
102
+ order?: number;
103
+ }
104
+
105
+ export interface BuiltinFrontmatterConfig extends BuiltinToggle {
106
+ delimiters?: string | [string, string];
107
+ language?: "yaml" | "toml";
108
+ }
109
+
110
+ export interface BuiltinLinksConfig extends BuiltinToggle {
111
+ markdownLinkResolution?: "absolute" | "relative" | "shortest";
112
+ prettyLinks?: boolean;
113
+ openLinksInNewTab?: boolean;
114
+ lazyLoad?: boolean;
115
+ externalLinkIcon?: boolean;
116
+ disableBrokenWikilinks?: boolean;
117
+ }
118
+
119
+ export interface BuiltinDraftsConfig extends BuiltinToggle {
120
+ publish?: boolean;
121
+ }
122
+
123
+ export interface BuiltinContentIndexConfig extends BuiltinToggle {
124
+ enableSiteMap?: boolean;
125
+ enableRSS?: boolean;
126
+ rssLimit?: number;
127
+ rssFullHtml?: boolean;
128
+ rssSlug?: string;
129
+ includeEmptyFiles?: boolean;
74
130
  }
75
131
 
76
132
  /**
@@ -107,6 +163,11 @@ export interface BuildCtx {
107
163
  allSlugs: FullSlug[];
108
164
  allFiles: FilePath[];
109
165
  incremental: boolean;
166
+ /**
167
+ * The site's content index (Quartz 5.1+): one entry per published page, virtual pages
168
+ * included. Available to emitters (except ComponentResources / PageTypeDispatcher); read-only.
169
+ */
170
+ contentIndex?: ReadonlyMap<FullSlug, Readonly<ContentDetails>>;
110
171
  }
111
172
 
112
173
  // ============================================================================
@@ -167,7 +228,22 @@ export type QuartzTransformerPlugin<Options extends OptionType = undefined> = (
167
228
  opts?: Options,
168
229
  ) => QuartzTransformerPluginInstance;
169
230
 
170
- export type QuartzTransformerPluginInstance = {
231
+ /**
232
+ * Optional hook on every plugin instance (Quartz 5.1+): extra JSON-serialisable fields for a page's
233
+ * entry in `static/contentIndex.json`. Core fields cannot be overridden; on key collisions between
234
+ * plugins the first (in plugin order) wins.
235
+ */
236
+ export type ContentIndexExtension = (args: {
237
+ ctx: BuildCtx;
238
+ fileData: QuartzPluginData;
239
+ entry: Readonly<ContentDetails>;
240
+ }) => Record<string, unknown> | undefined | void;
241
+
242
+ interface PluginHooks {
243
+ extendContentIndex?: ContentIndexExtension;
244
+ }
245
+
246
+ export type QuartzTransformerPluginInstance = PluginHooks & {
171
247
  name: string;
172
248
  textTransform?: (ctx: BuildCtx, src: string) => string;
173
249
  markdownPlugins?: (ctx: BuildCtx) => PluggableList;
@@ -179,7 +255,7 @@ export type QuartzFilterPlugin<Options extends OptionType = undefined> = (
179
255
  opts?: Options,
180
256
  ) => QuartzFilterPluginInstance;
181
257
 
182
- export type QuartzFilterPluginInstance = {
258
+ export type QuartzFilterPluginInstance = PluginHooks & {
183
259
  name: string;
184
260
  shouldPublish(ctx: BuildCtx, content: ProcessedContent): boolean;
185
261
  };
@@ -194,7 +270,7 @@ export type QuartzEmitterPlugin<Options extends OptionType = undefined> = (
194
270
  opts?: Options,
195
271
  ) => QuartzEmitterPluginInstance;
196
272
 
197
- export type QuartzEmitterPluginInstance = {
273
+ export type QuartzEmitterPluginInstance = PluginHooks & {
198
274
  name: string;
199
275
  emit: (
200
276
  ctx: BuildCtx,
@@ -265,7 +341,7 @@ export type QuartzPageTypePlugin<Options extends OptionType = undefined> = (
265
341
  opts?: Options,
266
342
  ) => QuartzPageTypePluginInstance;
267
343
 
268
- export type QuartzPageTypePluginInstance = {
344
+ export type QuartzPageTypePluginInstance = PluginHooks & {
269
345
  name: string;
270
346
  /** Higher priority wins when multiple page types match the same file. Default: 0. */
271
347
  priority?: number;
@@ -312,8 +388,20 @@ export interface PageFrameProps {
312
388
  right: QuartzComponent[];
313
389
  /** Footer components */
314
390
  footer: QuartzComponent[];
391
+ /**
392
+ * Sidebar modes from `layout.sidebars` (Quartz 5.1+; undefined before). Frames with sidebars
393
+ * should put them on the sidebar elements as `data-sidebar-mode`; Quartz's stylesheet does the rest.
394
+ */
395
+ sidebars?: { left: SidebarMode; right: SidebarMode };
315
396
  }
316
397
 
398
+ /**
399
+ * How a sidebar handles content taller than the screen (`layout.sidebars`, Quartz 5.1+):
400
+ * `fit` (default) — one screen tall, components shrink and scroll; `scroll` — stays in view and
401
+ * scrolls itself; `static` — scrolls away with the page.
402
+ */
403
+ export type SidebarMode = "fit" | "scroll" | "static";
404
+
317
405
  /**
318
406
  * A PageFrame defines the inner HTML structure of a page inside the
319
407
  * `<div id="quartz-root">` shell. Different frames can produce completely
@@ -343,12 +431,7 @@ export type EventHandler = (event: Event) => void;
343
431
  export type CleanupFunction = () => void;
344
432
 
345
433
  export type ClassValue =
346
- | string
347
- | number
348
- | boolean
349
- | undefined
350
- | null
351
- | ClassValue[];
434
+ string | number | boolean | undefined | null | ClassValue[];
352
435
 
353
436
  // ============================================================================
354
437
  // Content Index Types - Data shape for contentIndex.json
@@ -364,6 +447,8 @@ export type ContentDetails = {
364
447
  richContent?: string;
365
448
  date?: Date;
366
449
  description?: string;
450
+ /** Fields added by plugins through `extendContentIndex` (Quartz 5.1+). */
451
+ [extra: string]: unknown;
367
452
  };
368
453
 
369
454
  export type ContentIndex = Record<FullSlug, ContentDetails>;