Skip to main content

dmc_transform/
config.rs

1//! Pipeline-level configuration. Compiled unconditionally so callers can
2//! describe the pipeline (themes, copy-linked-files paths, gfm switch, ...)
3//! whether or not the matching feature flags are enabled.
4//!
5//! When a config field describes a transformer whose feature is off,
6//! [`Pipeline::with_defaults_for`](crate::Pipeline::with_defaults_for)
7//! silently skips it -- the field is still allowed in the config so user
8//! settings round-trip cleanly across builds with different feature sets.
9
10use serde::{Deserialize, Serialize};
11use std::collections::BTreeMap;
12use std::path::PathBuf;
13
14/// Top-level config consumed by [`crate::Pipeline::with_defaults_for`].
15/// All fields are optional; the empty config (`PipelineConfig::default()`)
16/// reproduces the historical `Pipeline::with_defaults()` behavior.
17#[derive(Debug, Clone, Default, Serialize, Deserialize)]
18#[serde(default)]
19pub struct PipelineConfig {
20  /// When `false`, append the `disable-gfm` transformer that strips GFM
21  /// extensions (tables, strikethrough, autolinks, task lists).
22  pub markdown_gfm: Option<bool>,
23  /// Pretty-code theme + multi-mode settings. `None` keeps the bundled
24  /// defaults (Catppuccin Latte/Mocha pair, dark primary).
25  pub pretty_code: Option<PrettyCodeOptions>,
26  /// LaTeX rendering engine. `None` -> [`MathEngine::Katex`].
27  pub math_engine: Option<MathEngine>,
28  /// When `Some`, append the `copy-linked-files` transformer with the
29  /// supplied paths.
30  pub copy_linked_files: Option<CopyLinkedFilesOptions>,
31  /// When `Some(false)`, do not push the `Emoji` transformer. `None`
32  /// or `Some(true)` keeps the default behaviour (transformer added
33  /// when the `emoji` feature is on). Used by the plugin gate to drop
34  /// the native transformer when the user prefers `remark-emoji`.
35  pub emoji: Option<bool>,
36  /// Same shape for the `AutolinkHeadings` transformer. Set to
37  /// `Some(false)` when the user prefers `rehype-slug` /
38  /// `rehype-autolink-headings`.
39  pub autolink_headings: Option<bool>,
40  /// Same shape for the `Math` transformer. Set to `Some(false)` when
41  /// the user prefers `remark-math` / `rehype-katex` / `rehype-mathjax`.
42  pub math: Option<bool>,
43  /// Same shape for the `PrettyCode` transformer. Set to `Some(false)`
44  /// when the user prefers `rehype-pretty-code` / `shiki`.
45  pub pretty_code_enabled: Option<bool>,
46  /// Mermaid render config. `None` keeps the bundled defaults
47  /// (light + dark theme pair, `htmlLabels:false`, responsive SVG,
48  /// centered labels).
49  pub mermaid: Option<MermaidOptions>,
50  /// Same shape for the `Mermaid` transformer. `Some(false)` drops the
51  /// transformer (mermaid blocks left as code fences).
52  pub mermaid_enabled: Option<bool>,
53}
54
55/// Which engine renders `$...$` / `$$...$$` math.
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
57#[serde(rename_all = "lowercase")]
58pub enum MathEngine {
59  /// Embedded KaTeX via `quick-js`. Output matches `rehype-katex` byte
60  /// for byte. Slow per-expression (1-5 ms each).
61  #[default]
62  Katex,
63  /// `pulldown-latex` -> MathML. Fast (microseconds). Browser MathML
64  /// rendering is functional but visually plainer than KaTeX HTML.
65  Mathml,
66}
67
68/// How multi-theme pretty-code output is laid out in the DOM.
69///
70/// - `CssVars` (default, fast): single `<pre><code>` tree. Each styled
71///   token carries `style="--dmc-light:#XXX;--dmc-dark:#YYY"`, the
72///   `<pre>` carries default `color`/`background-color` from the
73///   primary mode. Consumer CSS swaps themes by overriding `color` to
74///   the matching `--dmc-*` variable inside whichever class /
75///   media-query controls the theme. ~25% faster than `Split`.
76/// - `Split` (velite parity): one full
77///   `<pre data-theme="<mode>"><code>...</code></pre>` subtree per
78///   theme, each with solid `color:#XXX` per token. Matches velite +
79///   rehype-pretty-code byte-for-byte; consumer CSS shows/hides whole
80///   panes by `[data-theme]`.
81#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
82#[serde(rename_all = "kebab-case")]
83pub enum MultiThemeStrategy {
84  /// One `<pre data-theme="<mode>">...</pre>` subtree per theme. Each
85  /// subtree carries solid `color:#XXX` styles, no CSS custom
86  /// properties. Default because the per-token style strings stay
87  /// shorter (no `--dmc-{mode}` pairs), the consumer flips themes by
88  /// toggling a single `[data-theme]` CSS rule, and it matches the
89  /// velite + rehype-pretty-code byte shape that consumers were
90  /// already styled for. The phase-6 flamegraph confirms PrettyCode
91  /// dominates compile time, so cutting the per-token work - even
92  /// at the cost of duplicated pre subtrees - is the right default.
93  #[default]
94  Split,
95  /// One `<pre>` subtree carrying `--dmc-{mode}` / `--dmc-{mode}-bg`
96  /// custom properties per token, plus solid fallbacks for the
97  /// `default_mode`. Consumer CSS toggles via `var(--dmc-active)`.
98  /// Slightly larger per-token style strings, but only one DOM
99  /// subtree regardless of theme count - pick this when you have
100  /// `>2` themes or need media-query / class-toggle theme switching
101  /// without re-rendering the code surface.
102  #[serde(alias = "cssVars")]
103  CssVars,
104}
105
106/// Pretty-code theme spec.
107///
108/// Deserialised untagged: pass a JSON string for single-theme, or an
109/// object `{ light: "...", dark: "...", ... }` for multi-mode output.
110#[derive(Debug, Clone, Serialize, Deserialize)]
111#[serde(untagged)]
112pub enum PrettyCodeTheme {
113  /// Single bundled theme name. Emits per-token `style="color:#xxx"`.
114  Single(String),
115  /// Map of `mode -> bundled theme name`. Mode keys are arbitrary
116  /// (`light`, `dark`, `dim`, ...); they appear in the emitted CSS as
117  /// `--dmc-{mode}` / `--dmc-{mode}-bg` CSS custom properties.
118  Multi(BTreeMap<String, String>),
119}
120
121impl Default for PrettyCodeTheme {
122  fn default() -> Self {
123    Self::Multi(
124      [("light".to_string(), "Catppuccin Latte".to_string()), ("dark".to_string(), "Catppuccin Mocha".to_string())]
125        .into_iter()
126        .collect(),
127    )
128  }
129}
130
131/// Top-level pretty-code configuration. Stored on `CompileConfig` as
132/// `Option<PrettyCodeOptions>`; `None` means "use built-in defaults".
133///
134/// Every field below has a sensible default; callers only set the knobs
135/// they care about. Field names match the TS-side `PrettyCodeOptions`
136/// shape exported from `@gentleduck/md`.
137#[derive(Debug, Clone, Default, Serialize, Deserialize)]
138#[serde(default, rename_all = "camelCase")]
139pub struct PrettyCodeOptions {
140  /// Theme spec. String for single-theme, object for multi-mode.
141  pub theme: PrettyCodeTheme,
142  /// Mode key whose colors fill the unprefixed `color` /
143  /// `background-color` attrs. Only meaningful for [`PrettyCodeTheme::Multi`].
144  /// When unset, resolves to `"dark"` if present, else the first key.
145  pub default_mode: Option<String>,
146  /// Multi-theme DOM strategy. Default `CssVars` (single tree, faster).
147  /// Set to `Split` for one `<pre data-theme="...">` subtree per theme
148  /// (velite parity, ~2x the AST nodes). Single-theme mode ignores this
149  /// - there's only one tree either way.
150  pub multi_theme_strategy: Option<MultiThemeStrategy>,
151  /// Keep the `__dmcRaw__` attribute on each `<pre>` so consumer
152  /// `<PreBlock>` can offer a Copy button without re-parsing the tree.
153  /// Default `true`.
154  pub keep_raw_string: Option<bool>,
155  /// Wrap the per-theme `<pre>` blocks in a
156  /// `<div data-dmc-fragment="">` envelope. Default
157  /// `true`. Set `false` to emit just the `<pre>` siblings (compatible
158  /// with consumers that wrap themselves).
159  pub fragment_wrapper: Option<bool>,
160  /// Class on each line `<span>`. Default `"line"`.
161  pub line_class: Option<String>,
162  /// Attribute name set on highlighted lines (from `{1,3-5}` meta).
163  /// Default `"data-dmc-line-highlighted"`.
164  pub highlighted_line_attr: Option<String>,
165  /// Language used when a fence has no `lang` and for unknown langs
166  /// when [`Self::fallback_to_plaintext`] is on. Default `"plaintext"`.
167  pub default_language: Option<String>,
168  /// When `true` (default), unknown fence languages render as plain
169  /// text. When `false`, the code block is left as a `CodeBlock` node
170  /// for downstream tooling.
171  pub fallback_to_plaintext: Option<bool>,
172  /// Render a `<figcaption data-dmc-title>` from the
173  /// fence's `title="..."` meta. Default `true`.
174  pub render_title: Option<bool>,
175  /// Include `data-language` on every emitted `<pre>` and `<code>`.
176  /// Default `true`.
177  pub include_data_language: Option<bool>,
178  /// Emit a solid `background-color` on `<pre>` from the primary theme.
179  /// Default `true`. Set to `false` to skip the inline bg so the outer
180  /// `[data-dmc-fragment]` wrapper (or consumer chrome) owns the
181  /// surface color. Per-mode `--dmc-{mode}-bg` custom properties are
182  /// always emitted regardless of this flag, so consumer CSS can still
183  /// opt back in via `var(--dmc-{mode}-bg)` if it wants.
184  pub include_pre_background: Option<bool>,
185  /// Languages to skip - these blocks are passed through unchanged.
186  /// `mermaid` is always skipped (owned by the mermaid transformer);
187  /// add other langs to keep them as raw `CodeBlock` nodes.
188  pub skip_languages: Vec<String>,
189  /// Expand tab characters to N spaces before highlighting. `None`
190  /// preserves original tabs.
191  pub tab_size: Option<u32>,
192}
193
194/// Mermaid theme spec. Either a single theme name (renders once,
195/// attaches one `chartSvg` attr) or a map of `mode -> theme name` that
196/// renders per-mode and attaches one `${mode}Svg` attr per entry.
197///
198/// Recognised theme names (passed through to `mmdc --theme`):
199/// `default`, `dark`, `forest`, `neutral`, `base`. Anything else is
200/// forwarded verbatim - mermaid-cli will validate.
201#[derive(Debug, Clone, Serialize, Deserialize)]
202#[serde(untagged)]
203pub enum MermaidThemeMode {
204  /// One render. Output JSX gets a single `chartSvg` attr.
205  Single(String),
206  /// Per-mode render. Output JSX gets `${key}Svg` per entry. The default
207  /// `{ "light": "default", "dark": "dark" }` reproduces the historical
208  /// `lightSvg` + `darkSvg` shape.
209  Multi(BTreeMap<String, String>),
210}
211
212impl Default for MermaidThemeMode {
213  fn default() -> Self {
214    Self::Multi(
215      [("light".to_string(), "default".to_string()), ("dark".to_string(), "dark".to_string())].into_iter().collect(),
216    )
217  }
218}
219
220// Mermaid initialize sub-config types
221// Each diagram block + the top-level enums get a typed Rust struct so
222// the user-facing config is fully type-checked end-to-end. Mirror the
223// TS-side MermaidInitializeConfig shape; new mermaid releases extend
224// these structs (serde tolerates unknown keys so missing fields just
225// pass through unchecked).
226
227/// CSS dimension that mermaid accepts as either a numeric pixel value
228/// (e.g. `14`) or a string with units (e.g. `"14px"`, `"1.2em"`).
229#[derive(Debug, Clone, Serialize, Deserialize)]
230#[serde(untagged)]
231pub enum CssDimension {
232  Number(f64),
233  String(String),
234}
235
236/// CSS font-weight: numeric (`400`, `700`) or named string
237/// (`"normal"`, `"bold"`).
238#[derive(Debug, Clone, Serialize, Deserialize)]
239#[serde(untagged)]
240pub enum CssFontWeight {
241  Number(u32),
242  String(String),
243}
244
245#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
246#[serde(rename_all = "lowercase")]
247pub enum MermaidSecurityLevel {
248  Strict,
249  Loose,
250  Antiscript,
251  Sandbox,
252}
253
254#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
255#[serde(rename_all = "lowercase")]
256pub enum MermaidLogLevel {
257  Debug,
258  Info,
259  Warn,
260  Error,
261  Fatal,
262}
263
264#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
265#[serde(rename_all = "camelCase")]
266pub enum MermaidLook {
267  Classic,
268  Neo,
269  HandDrawn,
270}
271
272#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
273#[serde(rename_all = "lowercase")]
274pub enum MermaidLayout {
275  Dagre,
276  Elk,
277}
278
279#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
280#[serde(rename_all = "kebab-case")]
281pub enum MermaidFlowchartRenderer {
282  #[serde(rename = "dagre-d3")]
283  DagreD3,
284  #[serde(rename = "dagre-wrapper")]
285  DagreWrapper,
286  Elk,
287}
288
289#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
290#[serde(rename_all = "camelCase")]
291pub enum MermaidFlowchartCurve {
292  Basis,
293  Linear,
294  Cardinal,
295  StepBefore,
296  StepAfter,
297  Natural,
298  MonotoneX,
299  MonotoneY,
300}
301
302#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
303#[serde(rename_all = "lowercase")]
304pub enum MermaidAlign {
305  Left,
306  Center,
307  Right,
308}
309
310#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
311#[serde(rename_all = "lowercase")]
312pub enum MermaidErLayoutDirection {
313  Tb,
314  Bt,
315  Lr,
316  Rl,
317}
318
319#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
320#[serde(rename_all = "lowercase")]
321pub enum MermaidGanttDisplayMode {
322  #[serde(rename = "")]
323  Default,
324  Compact,
325}
326
327#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
328#[serde(rename_all = "lowercase")]
329pub enum MermaidGanttWeekday {
330  Monday,
331  Tuesday,
332  Wednesday,
333  Thursday,
334  Friday,
335  Saturday,
336  Sunday,
337}
338
339#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
340#[serde(rename_all = "lowercase")]
341pub enum MermaidSankeyAlignment {
342  Left,
343  Right,
344  Center,
345  Justify,
346}
347
348#[derive(Debug, Clone, Default, Serialize, Deserialize)]
349#[serde(default, rename_all = "camelCase")]
350pub struct MermaidThemeVariables {
351  pub background: Option<String>,
352  pub font_family: Option<String>,
353  pub font_size: Option<String>,
354  pub primary_color: Option<String>,
355  pub primary_text_color: Option<String>,
356  pub primary_border_color: Option<String>,
357  pub secondary_color: Option<String>,
358  pub secondary_text_color: Option<String>,
359  pub secondary_border_color: Option<String>,
360  pub tertiary_color: Option<String>,
361  pub tertiary_text_color: Option<String>,
362  pub tertiary_border_color: Option<String>,
363  pub note_bkg_color: Option<String>,
364  pub note_text_color: Option<String>,
365  pub note_border_color: Option<String>,
366  pub line_color: Option<String>,
367  pub text_color: Option<String>,
368  pub main_bkg: Option<String>,
369  pub error_bkg_color: Option<String>,
370  pub error_text_color: Option<String>,
371  pub node_bkg: Option<String>,
372  pub node_border: Option<String>,
373  pub cluster_bkg: Option<String>,
374  pub cluster_border: Option<String>,
375  pub default_link_color: Option<String>,
376  pub title_color: Option<String>,
377  pub edge_label_background: Option<String>,
378}
379
380#[derive(Debug, Clone, Default, Serialize, Deserialize)]
381#[serde(default, rename_all = "camelCase")]
382pub struct MermaidFlowchartConfig {
383  pub html_labels: Option<bool>,
384  pub use_max_width: Option<bool>,
385  pub default_renderer: Option<MermaidFlowchartRenderer>,
386  pub curve: Option<MermaidFlowchartCurve>,
387  pub diagram_padding: Option<u32>,
388  pub node_spacing: Option<u32>,
389  pub rank_spacing: Option<u32>,
390  pub padding: Option<u32>,
391  pub title_top_margin: Option<u32>,
392  pub wrapping_width: Option<u32>,
393  pub arrow_marker_absolute: Option<bool>,
394}
395
396#[derive(Debug, Clone, Default, Serialize, Deserialize)]
397#[serde(default, rename_all = "camelCase")]
398pub struct MermaidSequenceConfig {
399  pub use_max_width: Option<bool>,
400  pub hide_unused_participants: Option<bool>,
401  pub activation_width: Option<u32>,
402  pub diagram_margin_x: Option<u32>,
403  pub diagram_margin_y: Option<u32>,
404  pub actor_margin: Option<u32>,
405  pub width: Option<u32>,
406  pub height: Option<u32>,
407  pub box_margin: Option<u32>,
408  pub box_text_margin: Option<u32>,
409  pub note_margin: Option<u32>,
410  pub message_margin: Option<u32>,
411  pub message_align: Option<MermaidAlign>,
412  pub mirror_actors: Option<bool>,
413  pub force_menus: Option<bool>,
414  pub bottom_margin_adj: Option<i32>,
415  pub right_angles: Option<bool>,
416  pub show_sequence_numbers: Option<bool>,
417  pub actor_font_size: Option<CssDimension>,
418  pub actor_font_family: Option<String>,
419  pub actor_font_weight: Option<CssFontWeight>,
420  pub note_font_size: Option<CssDimension>,
421  pub note_font_family: Option<String>,
422  pub note_font_weight: Option<CssFontWeight>,
423  pub note_align: Option<MermaidAlign>,
424  pub message_font_size: Option<CssDimension>,
425  pub message_font_family: Option<String>,
426  pub message_font_weight: Option<CssFontWeight>,
427  pub wrap: Option<bool>,
428  pub wrap_padding: Option<u32>,
429  pub label_box_width: Option<u32>,
430  pub label_box_height: Option<u32>,
431}
432
433#[derive(Debug, Clone, Default, Serialize, Deserialize)]
434#[serde(default, rename_all = "camelCase")]
435pub struct MermaidGanttConfig {
436  pub use_max_width: Option<bool>,
437  pub title_top_margin: Option<u32>,
438  pub bar_height: Option<u32>,
439  pub bar_gap: Option<u32>,
440  pub top_padding: Option<u32>,
441  pub right_padding: Option<u32>,
442  pub left_padding: Option<u32>,
443  pub grid_line_start_padding: Option<u32>,
444  pub font_size: Option<u32>,
445  pub section_font_size: Option<CssDimension>,
446  pub number_section_styles: Option<u32>,
447  pub axis_format: Option<String>,
448  pub tick_interval: Option<String>,
449  pub top_axis: Option<bool>,
450  pub display_mode: Option<MermaidGanttDisplayMode>,
451  pub weekday: Option<MermaidGanttWeekday>,
452}
453
454#[derive(Debug, Clone, Default, Serialize, Deserialize)]
455#[serde(default, rename_all = "camelCase")]
456pub struct MermaidErConfig {
457  pub use_max_width: Option<bool>,
458  pub title_top_margin: Option<u32>,
459  pub diagram_padding: Option<u32>,
460  pub layout_direction: Option<MermaidErLayoutDirection>,
461  pub min_entity_width: Option<u32>,
462  pub min_entity_height: Option<u32>,
463  pub entity_padding: Option<u32>,
464  pub stroke: Option<String>,
465  pub fill: Option<String>,
466  pub font_size: Option<u32>,
467}
468
469#[derive(Debug, Clone, Default, Serialize, Deserialize)]
470#[serde(default, rename_all = "camelCase")]
471pub struct MermaidPieConfig {
472  pub use_max_width: Option<bool>,
473  pub text_position: Option<f32>,
474}
475
476#[derive(Debug, Clone, Default, Serialize, Deserialize)]
477#[serde(default, rename_all = "camelCase")]
478pub struct MermaidNodeRendererConfig {
479  pub use_max_width: Option<bool>,
480  pub title_top_margin: Option<u32>,
481  pub default_renderer: Option<MermaidFlowchartRenderer>,
482  pub arrow_marker_absolute: Option<bool>,
483  pub divider_margin: Option<u32>,
484  pub padding: Option<u32>,
485  pub text_height: Option<u32>,
486}
487
488#[derive(Debug, Clone, Default, Serialize, Deserialize)]
489#[serde(default, rename_all = "camelCase")]
490pub struct MermaidGitGraphConfig {
491  pub use_max_width: Option<bool>,
492  pub title_top_margin: Option<u32>,
493  pub diagram_padding: Option<u32>,
494  pub main_branch_name: Option<String>,
495  pub main_branch_order: Option<u32>,
496  pub show_commit_label: Option<bool>,
497  pub show_branches: Option<bool>,
498  pub rotate_commit_label: Option<bool>,
499  pub parallel_commits: Option<bool>,
500}
501
502#[derive(Debug, Clone, Default, Serialize, Deserialize)]
503#[serde(default, rename_all = "camelCase")]
504pub struct MermaidJourneyConfig {
505  pub use_max_width: Option<bool>,
506  pub diagram_margin_x: Option<u32>,
507  pub diagram_margin_y: Option<u32>,
508  pub left_margin: Option<u32>,
509  pub width: Option<u32>,
510  pub height: Option<u32>,
511  pub box_margin: Option<u32>,
512  pub box_text_margin: Option<u32>,
513  pub note_margin: Option<u32>,
514  pub message_margin: Option<u32>,
515  pub message_align: Option<MermaidAlign>,
516  pub bottom_margin_adj: Option<i32>,
517  pub right_angles: Option<bool>,
518  pub task_font_size: Option<CssDimension>,
519  pub task_font_family: Option<String>,
520  pub task_margin: Option<u32>,
521  pub activation_width: Option<u32>,
522  pub text_placement: Option<String>,
523}
524
525#[derive(Debug, Clone, Default, Serialize, Deserialize)]
526#[serde(default, rename_all = "camelCase")]
527pub struct MermaidMindmapConfig {
528  pub use_max_width: Option<bool>,
529  pub padding: Option<u32>,
530  pub max_node_width: Option<u32>,
531}
532
533#[derive(Debug, Clone, Default, Serialize, Deserialize)]
534#[serde(default, rename_all = "camelCase")]
535pub struct MermaidTimelineConfig {
536  pub use_max_width: Option<bool>,
537  pub disable_multicolor: Option<bool>,
538}
539
540#[derive(Debug, Clone, Default, Serialize, Deserialize)]
541#[serde(default, rename_all = "camelCase")]
542pub struct MermaidSankeyConfig {
543  pub use_max_width: Option<bool>,
544  pub node_alignment: Option<MermaidSankeyAlignment>,
545  pub show_values: Option<bool>,
546}
547
548#[derive(Debug, Clone, Default, Serialize, Deserialize)]
549#[serde(default, rename_all = "camelCase")]
550pub struct MermaidXyChartConfig {
551  pub use_max_width: Option<bool>,
552  pub width: Option<u32>,
553  pub height: Option<u32>,
554}
555
556#[derive(Debug, Clone, Default, Serialize, Deserialize)]
557#[serde(default, rename_all = "camelCase")]
558pub struct MermaidBlockConfig {
559  pub use_max_width: Option<bool>,
560  pub padding: Option<u32>,
561}
562
563#[derive(Debug, Clone, Default, Serialize, Deserialize)]
564#[serde(default, rename_all = "camelCase")]
565pub struct MermaidRequirementConfig {
566  pub use_max_width: Option<bool>,
567  pub rect_min_width: Option<u32>,
568  pub rect_min_height: Option<u32>,
569}
570
571#[derive(Debug, Clone, Default, Serialize, Deserialize)]
572#[serde(default, rename_all = "camelCase")]
573pub struct MermaidC4Config {
574  pub use_max_width: Option<bool>,
575  pub diagram_margin_x: Option<u32>,
576  pub diagram_margin_y: Option<u32>,
577}
578
579#[derive(Debug, Clone, Default, Serialize, Deserialize)]
580#[serde(default, rename_all = "camelCase")]
581pub struct MermaidArchitectureConfig {
582  pub use_max_width: Option<bool>,
583  pub padding: Option<u32>,
584  pub icon_size: Option<u32>,
585}
586
587#[derive(Debug, Clone, Default, Serialize, Deserialize)]
588#[serde(default, rename_all = "camelCase")]
589pub struct MermaidRadarConfig {
590  pub use_max_width: Option<bool>,
591}
592
593#[derive(Debug, Clone, Default, Serialize, Deserialize)]
594#[serde(default, rename_all = "camelCase")]
595pub struct MermaidTreemapConfig {
596  pub use_max_width: Option<bool>,
597  pub padding: Option<u32>,
598}
599
600/// Top-level mermaid configuration. **Single flat object** - every
601/// `mermaid.initialize()` knob (themeVariables, flowchart, sequence,
602/// gantt, look, layout, ...) lives at the same level as the dmc-side
603/// rendering knobs (responsiveSvg, centerLabels, outputDir, ...). All
604/// fields are typed end-to-end; no `serde_json::Value` catch-all.
605///
606/// `None` on `CompileConfig.mermaid` means "use built-in defaults"
607/// (light + dark themes, htmlLabels:false, flowchart spacing).
608#[derive(Debug, Clone, Default, Serialize, Deserialize)]
609#[serde(default, rename_all = "camelCase")]
610pub struct MermaidOptions {
611  // dmc render knobs
612  /// Theme spec. Single string (one render -> `chartSvg`) or
613  /// `mode -> theme` map (per-mode render -> `${mode}Svg` each).
614  /// Always stripped from the mermaid configFile in
615  /// `build_mermaid_config` - it's a dmc-side knob, not a mermaid one.
616  pub theme: MermaidThemeMode,
617  /// `mmdc --backgroundColor`. Default `"transparent"`.
618  #[serde(skip_serializing_if = "Option::is_none")]
619  pub background_color: Option<String>,
620  /// Apply the responsive-width post-process. Default `true`.
621  #[serde(skip_serializing_if = "Option::is_none")]
622  pub responsive_svg: Option<bool>,
623  /// Inject `text-anchor="middle"` on label `<text>` / `<tspan>` so
624  /// flowchart node labels center inside their `<rect>` when
625  /// `htmlLabels:false` is in effect. Default `true`.
626  #[serde(skip_serializing_if = "Option::is_none")]
627  pub center_labels: Option<bool>,
628  /// Disk cache directory.
629  #[serde(skip_serializing_if = "Option::is_none")]
630  pub output_dir: Option<PathBuf>,
631  /// Forwarded to `mmdc --puppeteerConfigFile`.
632  #[serde(skip_serializing_if = "Option::is_none")]
633  pub puppeteer_config_file: Option<PathBuf>,
634
635  // mermaid.initialize()
636  /// Override the bundled `htmlLabels: false` default. `true` switches
637  /// flowchart node labels back to HTML-in-`<foreignObject>`.
638  #[serde(skip_serializing_if = "Option::is_none")]
639  pub html_labels: Option<bool>,
640  #[serde(skip_serializing_if = "Option::is_none")]
641  pub theme_variables: Option<MermaidThemeVariables>,
642  #[serde(skip_serializing_if = "Option::is_none")]
643  pub font_family: Option<String>,
644  #[serde(skip_serializing_if = "Option::is_none")]
645  pub font_size: Option<u32>,
646  #[serde(skip_serializing_if = "Option::is_none")]
647  pub start_on_load: Option<bool>,
648  #[serde(skip_serializing_if = "Option::is_none")]
649  pub arrow_marker_absolute: Option<bool>,
650  #[serde(skip_serializing_if = "Option::is_none")]
651  pub deterministic_ids: Option<bool>,
652  #[serde(skip_serializing_if = "Option::is_none")]
653  pub deterministic_id_seed: Option<String>,
654  #[serde(skip_serializing_if = "Option::is_none")]
655  pub max_text_size: Option<u64>,
656  #[serde(skip_serializing_if = "Option::is_none")]
657  pub max_edges: Option<u64>,
658  #[serde(skip_serializing_if = "Option::is_none")]
659  pub security_level: Option<MermaidSecurityLevel>,
660  #[serde(skip_serializing_if = "Option::is_none")]
661  pub log_level: Option<MermaidLogLevel>,
662  #[serde(skip_serializing_if = "Option::is_none")]
663  pub look: Option<MermaidLook>,
664  #[serde(skip_serializing_if = "Option::is_none")]
665  pub layout: Option<MermaidLayout>,
666  #[serde(skip_serializing_if = "Option::is_none")]
667  pub hand_drawn_seed: Option<i64>,
668  #[serde(skip_serializing_if = "Option::is_none")]
669  pub wrap: Option<bool>,
670  #[serde(skip_serializing_if = "Option::is_none")]
671  pub flowchart: Option<MermaidFlowchartConfig>,
672  #[serde(skip_serializing_if = "Option::is_none")]
673  pub sequence: Option<MermaidSequenceConfig>,
674  #[serde(skip_serializing_if = "Option::is_none")]
675  pub gantt: Option<MermaidGanttConfig>,
676  #[serde(skip_serializing_if = "Option::is_none")]
677  pub er: Option<MermaidErConfig>,
678  #[serde(skip_serializing_if = "Option::is_none")]
679  pub pie: Option<MermaidPieConfig>,
680  /// `class` is a Rust keyword - exposed under `class` in JSON via
681  /// `serde(rename)`.
682  #[serde(rename = "class", alias = "classDiagram", skip_serializing_if = "Option::is_none")]
683  pub class_diagram: Option<MermaidNodeRendererConfig>,
684  #[serde(skip_serializing_if = "Option::is_none")]
685  pub state: Option<MermaidNodeRendererConfig>,
686  #[serde(skip_serializing_if = "Option::is_none")]
687  pub git_graph: Option<MermaidGitGraphConfig>,
688  #[serde(skip_serializing_if = "Option::is_none")]
689  pub journey: Option<MermaidJourneyConfig>,
690  #[serde(skip_serializing_if = "Option::is_none")]
691  pub mindmap: Option<MermaidMindmapConfig>,
692  #[serde(skip_serializing_if = "Option::is_none")]
693  pub timeline: Option<MermaidTimelineConfig>,
694  #[serde(skip_serializing_if = "Option::is_none")]
695  pub sankey: Option<MermaidSankeyConfig>,
696  #[serde(skip_serializing_if = "Option::is_none")]
697  pub xy_chart: Option<MermaidXyChartConfig>,
698  #[serde(skip_serializing_if = "Option::is_none")]
699  pub block: Option<MermaidBlockConfig>,
700  #[serde(skip_serializing_if = "Option::is_none")]
701  pub requirement: Option<MermaidRequirementConfig>,
702  #[serde(skip_serializing_if = "Option::is_none")]
703  pub c4: Option<MermaidC4Config>,
704  #[serde(skip_serializing_if = "Option::is_none")]
705  pub architecture: Option<MermaidArchitectureConfig>,
706  #[serde(skip_serializing_if = "Option::is_none")]
707  pub radar: Option<MermaidRadarConfig>,
708  #[serde(skip_serializing_if = "Option::is_none")]
709  pub treemap: Option<MermaidTreemapConfig>,
710}
711
712/// Paths consumed by the `copy-linked-files` transformer.
713#[derive(Debug, Clone, Serialize, Deserialize)]
714pub struct CopyLinkedFilesOptions {
715  /// Directory the source `.mdx` lives in -- used to resolve relative
716  /// `src` / `href` attrs.
717  pub source_dir: PathBuf,
718  /// Output asset directory (where copies are written).
719  pub assets_dir: PathBuf,
720  /// Public URL prefix prepended to rewritten asset paths.
721  pub public_base: String,
722}