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 mirror the TS MermaidInitializeConfig
221// shape. Serde tolerates unknown keys so newer mermaid fields pass through.
222
223/// CSS dimension that mermaid accepts as either a numeric pixel value
224/// (e.g. `14`) or a string with units (e.g. `"14px"`, `"1.2em"`).
225#[derive(Debug, Clone, Serialize, Deserialize)]
226#[serde(untagged)]
227pub enum CssDimension {
228  Number(f64),
229  String(String),
230}
231
232/// CSS font-weight: numeric (`400`, `700`) or named string
233/// (`"normal"`, `"bold"`).
234#[derive(Debug, Clone, Serialize, Deserialize)]
235#[serde(untagged)]
236pub enum CssFontWeight {
237  Number(u32),
238  String(String),
239}
240
241#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
242#[serde(rename_all = "lowercase")]
243pub enum MermaidSecurityLevel {
244  Strict,
245  Loose,
246  Antiscript,
247  Sandbox,
248}
249
250#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
251#[serde(rename_all = "lowercase")]
252pub enum MermaidLogLevel {
253  Debug,
254  Info,
255  Warn,
256  Error,
257  Fatal,
258}
259
260#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
261#[serde(rename_all = "camelCase")]
262pub enum MermaidLook {
263  Classic,
264  Neo,
265  HandDrawn,
266}
267
268#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
269#[serde(rename_all = "lowercase")]
270pub enum MermaidLayout {
271  Dagre,
272  Elk,
273}
274
275#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
276#[serde(rename_all = "kebab-case")]
277pub enum MermaidFlowchartRenderer {
278  #[serde(rename = "dagre-d3")]
279  DagreD3,
280  #[serde(rename = "dagre-wrapper")]
281  DagreWrapper,
282  Elk,
283}
284
285#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
286#[serde(rename_all = "camelCase")]
287pub enum MermaidFlowchartCurve {
288  Basis,
289  Linear,
290  Cardinal,
291  StepBefore,
292  StepAfter,
293  Natural,
294  MonotoneX,
295  MonotoneY,
296}
297
298#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
299#[serde(rename_all = "lowercase")]
300pub enum MermaidAlign {
301  Left,
302  Center,
303  Right,
304}
305
306#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
307#[serde(rename_all = "lowercase")]
308pub enum MermaidErLayoutDirection {
309  Tb,
310  Bt,
311  Lr,
312  Rl,
313}
314
315#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
316#[serde(rename_all = "lowercase")]
317pub enum MermaidGanttDisplayMode {
318  #[serde(rename = "")]
319  Default,
320  Compact,
321}
322
323#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
324#[serde(rename_all = "lowercase")]
325pub enum MermaidGanttWeekday {
326  Monday,
327  Tuesday,
328  Wednesday,
329  Thursday,
330  Friday,
331  Saturday,
332  Sunday,
333}
334
335#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
336#[serde(rename_all = "lowercase")]
337pub enum MermaidSankeyAlignment {
338  Left,
339  Right,
340  Center,
341  Justify,
342}
343
344#[derive(Debug, Clone, Default, Serialize, Deserialize)]
345#[serde(default, rename_all = "camelCase")]
346pub struct MermaidThemeVariables {
347  pub background: Option<String>,
348  pub font_family: Option<String>,
349  pub font_size: Option<String>,
350  pub primary_color: Option<String>,
351  pub primary_text_color: Option<String>,
352  pub primary_border_color: Option<String>,
353  pub secondary_color: Option<String>,
354  pub secondary_text_color: Option<String>,
355  pub secondary_border_color: Option<String>,
356  pub tertiary_color: Option<String>,
357  pub tertiary_text_color: Option<String>,
358  pub tertiary_border_color: Option<String>,
359  pub note_bkg_color: Option<String>,
360  pub note_text_color: Option<String>,
361  pub note_border_color: Option<String>,
362  pub line_color: Option<String>,
363  pub text_color: Option<String>,
364  pub main_bkg: Option<String>,
365  pub error_bkg_color: Option<String>,
366  pub error_text_color: Option<String>,
367  pub node_bkg: Option<String>,
368  pub node_border: Option<String>,
369  pub cluster_bkg: Option<String>,
370  pub cluster_border: Option<String>,
371  pub default_link_color: Option<String>,
372  pub title_color: Option<String>,
373  pub edge_label_background: Option<String>,
374}
375
376#[derive(Debug, Clone, Default, Serialize, Deserialize)]
377#[serde(default, rename_all = "camelCase")]
378pub struct MermaidFlowchartConfig {
379  pub html_labels: Option<bool>,
380  pub use_max_width: Option<bool>,
381  pub default_renderer: Option<MermaidFlowchartRenderer>,
382  pub curve: Option<MermaidFlowchartCurve>,
383  pub diagram_padding: Option<u32>,
384  pub node_spacing: Option<u32>,
385  pub rank_spacing: Option<u32>,
386  pub padding: Option<u32>,
387  pub title_top_margin: Option<u32>,
388  pub wrapping_width: Option<u32>,
389  pub arrow_marker_absolute: Option<bool>,
390}
391
392#[derive(Debug, Clone, Default, Serialize, Deserialize)]
393#[serde(default, rename_all = "camelCase")]
394pub struct MermaidSequenceConfig {
395  pub use_max_width: Option<bool>,
396  pub hide_unused_participants: Option<bool>,
397  pub activation_width: Option<u32>,
398  pub diagram_margin_x: Option<u32>,
399  pub diagram_margin_y: Option<u32>,
400  pub actor_margin: Option<u32>,
401  pub width: Option<u32>,
402  pub height: Option<u32>,
403  pub box_margin: Option<u32>,
404  pub box_text_margin: Option<u32>,
405  pub note_margin: Option<u32>,
406  pub message_margin: Option<u32>,
407  pub message_align: Option<MermaidAlign>,
408  pub mirror_actors: Option<bool>,
409  pub force_menus: Option<bool>,
410  pub bottom_margin_adj: Option<i32>,
411  pub right_angles: Option<bool>,
412  pub show_sequence_numbers: Option<bool>,
413  pub actor_font_size: Option<CssDimension>,
414  pub actor_font_family: Option<String>,
415  pub actor_font_weight: Option<CssFontWeight>,
416  pub note_font_size: Option<CssDimension>,
417  pub note_font_family: Option<String>,
418  pub note_font_weight: Option<CssFontWeight>,
419  pub note_align: Option<MermaidAlign>,
420  pub message_font_size: Option<CssDimension>,
421  pub message_font_family: Option<String>,
422  pub message_font_weight: Option<CssFontWeight>,
423  pub wrap: Option<bool>,
424  pub wrap_padding: Option<u32>,
425  pub label_box_width: Option<u32>,
426  pub label_box_height: Option<u32>,
427}
428
429#[derive(Debug, Clone, Default, Serialize, Deserialize)]
430#[serde(default, rename_all = "camelCase")]
431pub struct MermaidGanttConfig {
432  pub use_max_width: Option<bool>,
433  pub title_top_margin: Option<u32>,
434  pub bar_height: Option<u32>,
435  pub bar_gap: Option<u32>,
436  pub top_padding: Option<u32>,
437  pub right_padding: Option<u32>,
438  pub left_padding: Option<u32>,
439  pub grid_line_start_padding: Option<u32>,
440  pub font_size: Option<u32>,
441  pub section_font_size: Option<CssDimension>,
442  pub number_section_styles: Option<u32>,
443  pub axis_format: Option<String>,
444  pub tick_interval: Option<String>,
445  pub top_axis: Option<bool>,
446  pub display_mode: Option<MermaidGanttDisplayMode>,
447  pub weekday: Option<MermaidGanttWeekday>,
448}
449
450#[derive(Debug, Clone, Default, Serialize, Deserialize)]
451#[serde(default, rename_all = "camelCase")]
452pub struct MermaidErConfig {
453  pub use_max_width: Option<bool>,
454  pub title_top_margin: Option<u32>,
455  pub diagram_padding: Option<u32>,
456  pub layout_direction: Option<MermaidErLayoutDirection>,
457  pub min_entity_width: Option<u32>,
458  pub min_entity_height: Option<u32>,
459  pub entity_padding: Option<u32>,
460  pub stroke: Option<String>,
461  pub fill: Option<String>,
462  pub font_size: Option<u32>,
463}
464
465#[derive(Debug, Clone, Default, Serialize, Deserialize)]
466#[serde(default, rename_all = "camelCase")]
467pub struct MermaidPieConfig {
468  pub use_max_width: Option<bool>,
469  pub text_position: Option<f32>,
470}
471
472#[derive(Debug, Clone, Default, Serialize, Deserialize)]
473#[serde(default, rename_all = "camelCase")]
474pub struct MermaidNodeRendererConfig {
475  pub use_max_width: Option<bool>,
476  pub title_top_margin: Option<u32>,
477  pub default_renderer: Option<MermaidFlowchartRenderer>,
478  pub arrow_marker_absolute: Option<bool>,
479  pub divider_margin: Option<u32>,
480  pub padding: Option<u32>,
481  pub text_height: Option<u32>,
482}
483
484#[derive(Debug, Clone, Default, Serialize, Deserialize)]
485#[serde(default, rename_all = "camelCase")]
486pub struct MermaidGitGraphConfig {
487  pub use_max_width: Option<bool>,
488  pub title_top_margin: Option<u32>,
489  pub diagram_padding: Option<u32>,
490  pub main_branch_name: Option<String>,
491  pub main_branch_order: Option<u32>,
492  pub show_commit_label: Option<bool>,
493  pub show_branches: Option<bool>,
494  pub rotate_commit_label: Option<bool>,
495  pub parallel_commits: Option<bool>,
496}
497
498#[derive(Debug, Clone, Default, Serialize, Deserialize)]
499#[serde(default, rename_all = "camelCase")]
500pub struct MermaidJourneyConfig {
501  pub use_max_width: Option<bool>,
502  pub diagram_margin_x: Option<u32>,
503  pub diagram_margin_y: Option<u32>,
504  pub left_margin: Option<u32>,
505  pub width: Option<u32>,
506  pub height: Option<u32>,
507  pub box_margin: Option<u32>,
508  pub box_text_margin: Option<u32>,
509  pub note_margin: Option<u32>,
510  pub message_margin: Option<u32>,
511  pub message_align: Option<MermaidAlign>,
512  pub bottom_margin_adj: Option<i32>,
513  pub right_angles: Option<bool>,
514  pub task_font_size: Option<CssDimension>,
515  pub task_font_family: Option<String>,
516  pub task_margin: Option<u32>,
517  pub activation_width: Option<u32>,
518  pub text_placement: Option<String>,
519}
520
521#[derive(Debug, Clone, Default, Serialize, Deserialize)]
522#[serde(default, rename_all = "camelCase")]
523pub struct MermaidMindmapConfig {
524  pub use_max_width: Option<bool>,
525  pub padding: Option<u32>,
526  pub max_node_width: Option<u32>,
527}
528
529#[derive(Debug, Clone, Default, Serialize, Deserialize)]
530#[serde(default, rename_all = "camelCase")]
531pub struct MermaidTimelineConfig {
532  pub use_max_width: Option<bool>,
533  pub disable_multicolor: Option<bool>,
534}
535
536#[derive(Debug, Clone, Default, Serialize, Deserialize)]
537#[serde(default, rename_all = "camelCase")]
538pub struct MermaidSankeyConfig {
539  pub use_max_width: Option<bool>,
540  pub node_alignment: Option<MermaidSankeyAlignment>,
541  pub show_values: Option<bool>,
542}
543
544#[derive(Debug, Clone, Default, Serialize, Deserialize)]
545#[serde(default, rename_all = "camelCase")]
546pub struct MermaidXyChartConfig {
547  pub use_max_width: Option<bool>,
548  pub width: Option<u32>,
549  pub height: Option<u32>,
550}
551
552#[derive(Debug, Clone, Default, Serialize, Deserialize)]
553#[serde(default, rename_all = "camelCase")]
554pub struct MermaidBlockConfig {
555  pub use_max_width: Option<bool>,
556  pub padding: Option<u32>,
557}
558
559#[derive(Debug, Clone, Default, Serialize, Deserialize)]
560#[serde(default, rename_all = "camelCase")]
561pub struct MermaidRequirementConfig {
562  pub use_max_width: Option<bool>,
563  pub rect_min_width: Option<u32>,
564  pub rect_min_height: Option<u32>,
565}
566
567#[derive(Debug, Clone, Default, Serialize, Deserialize)]
568#[serde(default, rename_all = "camelCase")]
569pub struct MermaidC4Config {
570  pub use_max_width: Option<bool>,
571  pub diagram_margin_x: Option<u32>,
572  pub diagram_margin_y: Option<u32>,
573}
574
575#[derive(Debug, Clone, Default, Serialize, Deserialize)]
576#[serde(default, rename_all = "camelCase")]
577pub struct MermaidArchitectureConfig {
578  pub use_max_width: Option<bool>,
579  pub padding: Option<u32>,
580  pub icon_size: Option<u32>,
581}
582
583#[derive(Debug, Clone, Default, Serialize, Deserialize)]
584#[serde(default, rename_all = "camelCase")]
585pub struct MermaidRadarConfig {
586  pub use_max_width: Option<bool>,
587}
588
589#[derive(Debug, Clone, Default, Serialize, Deserialize)]
590#[serde(default, rename_all = "camelCase")]
591pub struct MermaidTreemapConfig {
592  pub use_max_width: Option<bool>,
593  pub padding: Option<u32>,
594}
595
596/// Top-level mermaid configuration. **Single flat object** - every
597/// `mermaid.initialize()` knob (themeVariables, flowchart, sequence,
598/// gantt, look, layout, ...) lives at the same level as the dmc-side
599/// rendering knobs (responsiveSvg, centerLabels, outputDir, ...). All
600/// fields are typed end-to-end; no `serde_json::Value` catch-all.
601///
602/// `None` on `CompileConfig.mermaid` means "use built-in defaults"
603/// (light + dark themes, htmlLabels:false, flowchart spacing).
604#[derive(Debug, Clone, Default, Serialize, Deserialize)]
605#[serde(default, rename_all = "camelCase")]
606pub struct MermaidOptions {
607  // dmc render knobs
608  /// Theme spec. Single string (one render -> `chartSvg`) or
609  /// `mode -> theme` map (per-mode render -> `${mode}Svg` each).
610  /// Always stripped from the mermaid configFile in
611  /// `build_mermaid_config` - it's a dmc-side knob, not a mermaid one.
612  pub theme: MermaidThemeMode,
613  /// `mmdc --backgroundColor`. Default `"transparent"`.
614  #[serde(skip_serializing_if = "Option::is_none")]
615  pub background_color: Option<String>,
616  /// Apply the responsive-width post-process. Default `true`.
617  #[serde(skip_serializing_if = "Option::is_none")]
618  pub responsive_svg: Option<bool>,
619  /// Inject `text-anchor="middle"` on label `<text>` / `<tspan>` so
620  /// flowchart node labels center inside their `<rect>` when
621  /// `htmlLabels:false` is in effect. Default `true`.
622  #[serde(skip_serializing_if = "Option::is_none")]
623  pub center_labels: Option<bool>,
624  /// Disk cache directory.
625  #[serde(skip_serializing_if = "Option::is_none")]
626  pub output_dir: Option<PathBuf>,
627  /// Forwarded to `mmdc --puppeteerConfigFile`.
628  #[serde(skip_serializing_if = "Option::is_none")]
629  pub puppeteer_config_file: Option<PathBuf>,
630
631  // mermaid.initialize()
632  /// Override the bundled `htmlLabels: false` default. `true` switches
633  /// flowchart node labels back to HTML-in-`<foreignObject>`.
634  #[serde(skip_serializing_if = "Option::is_none")]
635  pub html_labels: Option<bool>,
636  #[serde(skip_serializing_if = "Option::is_none")]
637  pub theme_variables: Option<MermaidThemeVariables>,
638  #[serde(skip_serializing_if = "Option::is_none")]
639  pub font_family: Option<String>,
640  #[serde(skip_serializing_if = "Option::is_none")]
641  pub font_size: Option<u32>,
642  #[serde(skip_serializing_if = "Option::is_none")]
643  pub start_on_load: Option<bool>,
644  #[serde(skip_serializing_if = "Option::is_none")]
645  pub arrow_marker_absolute: Option<bool>,
646  #[serde(skip_serializing_if = "Option::is_none")]
647  pub deterministic_ids: Option<bool>,
648  #[serde(skip_serializing_if = "Option::is_none")]
649  pub deterministic_id_seed: Option<String>,
650  #[serde(skip_serializing_if = "Option::is_none")]
651  pub max_text_size: Option<u64>,
652  #[serde(skip_serializing_if = "Option::is_none")]
653  pub max_edges: Option<u64>,
654  #[serde(skip_serializing_if = "Option::is_none")]
655  pub security_level: Option<MermaidSecurityLevel>,
656  #[serde(skip_serializing_if = "Option::is_none")]
657  pub log_level: Option<MermaidLogLevel>,
658  #[serde(skip_serializing_if = "Option::is_none")]
659  pub look: Option<MermaidLook>,
660  #[serde(skip_serializing_if = "Option::is_none")]
661  pub layout: Option<MermaidLayout>,
662  #[serde(skip_serializing_if = "Option::is_none")]
663  pub hand_drawn_seed: Option<i64>,
664  #[serde(skip_serializing_if = "Option::is_none")]
665  pub wrap: Option<bool>,
666  #[serde(skip_serializing_if = "Option::is_none")]
667  pub flowchart: Option<MermaidFlowchartConfig>,
668  #[serde(skip_serializing_if = "Option::is_none")]
669  pub sequence: Option<MermaidSequenceConfig>,
670  #[serde(skip_serializing_if = "Option::is_none")]
671  pub gantt: Option<MermaidGanttConfig>,
672  #[serde(skip_serializing_if = "Option::is_none")]
673  pub er: Option<MermaidErConfig>,
674  #[serde(skip_serializing_if = "Option::is_none")]
675  pub pie: Option<MermaidPieConfig>,
676  /// `class` is a Rust keyword - exposed under `class` in JSON via
677  /// `serde(rename)`.
678  #[serde(rename = "class", alias = "classDiagram", skip_serializing_if = "Option::is_none")]
679  pub class_diagram: Option<MermaidNodeRendererConfig>,
680  #[serde(skip_serializing_if = "Option::is_none")]
681  pub state: Option<MermaidNodeRendererConfig>,
682  #[serde(skip_serializing_if = "Option::is_none")]
683  pub git_graph: Option<MermaidGitGraphConfig>,
684  #[serde(skip_serializing_if = "Option::is_none")]
685  pub journey: Option<MermaidJourneyConfig>,
686  #[serde(skip_serializing_if = "Option::is_none")]
687  pub mindmap: Option<MermaidMindmapConfig>,
688  #[serde(skip_serializing_if = "Option::is_none")]
689  pub timeline: Option<MermaidTimelineConfig>,
690  #[serde(skip_serializing_if = "Option::is_none")]
691  pub sankey: Option<MermaidSankeyConfig>,
692  #[serde(skip_serializing_if = "Option::is_none")]
693  pub xy_chart: Option<MermaidXyChartConfig>,
694  #[serde(skip_serializing_if = "Option::is_none")]
695  pub block: Option<MermaidBlockConfig>,
696  #[serde(skip_serializing_if = "Option::is_none")]
697  pub requirement: Option<MermaidRequirementConfig>,
698  #[serde(skip_serializing_if = "Option::is_none")]
699  pub c4: Option<MermaidC4Config>,
700  #[serde(skip_serializing_if = "Option::is_none")]
701  pub architecture: Option<MermaidArchitectureConfig>,
702  #[serde(skip_serializing_if = "Option::is_none")]
703  pub radar: Option<MermaidRadarConfig>,
704  #[serde(skip_serializing_if = "Option::is_none")]
705  pub treemap: Option<MermaidTreemapConfig>,
706}
707
708/// Paths consumed by the `copy-linked-files` transformer.
709#[derive(Debug, Clone, Serialize, Deserialize)]
710pub struct CopyLinkedFilesOptions {
711  /// Directory the source `.mdx` lives in -- used to resolve relative
712  /// `src` / `href` attrs.
713  pub source_dir: PathBuf,
714  /// Output asset directory (where copies are written).
715  pub assets_dir: PathBuf,
716  /// Public URL prefix prepended to rewritten asset paths.
717  pub public_base: String,
718}