Skip to content

Add global diagram-theme directive with fence-level override precedence#130

Merged
jongalloway merged 5 commits intomainfrom
copilot/add-global-diagram-theme-support
Mar 14, 2026
Merged

Add global diagram-theme directive with fence-level override precedence#130
jongalloway merged 5 commits intomainfrom
copilot/add-global-diagram-theme-support

Conversation

Copy link
Contributor

Copilot AI commented Mar 14, 2026

Adds a deck-level diagram-theme front matter directive so authors can set a preferred DiagramForge theme for all mermaid and diagram fences without repeating it per-fence. Fence-level theme: always wins.

Precedence

  1. Fence-level theme: in embedded YAML front matter — unchanged, highest priority
  2. Deck-level diagram-theme — injected when fence has no theme
  3. Neither — DiagramForge falls back to default color mapping derived from the Marp slide theme

Example

---
diagram-theme: prism
---
---
theme: dracula   # overrides deck-level prism for this fence only
---
flowchart LR
  A --> B
Loading

Changes

Model / Parser

  • SlideDeck.DiagramTheme — new property stores the parsed directive value (whitespace-only → null)
  • MarpMarkdownParser — reads diagram-theme from front matter during deck parsing

Renderer

  • SlideRenderContext.GlobalDiagramTheme — carries the deck-level value through the render pipeline
  • InjectDiagramThemeIfNeeded — injects theme: X into the diagram source's YAML front matter before handing off to DiagramRenderer.Render(). Creates a minimal ---…--- block if the source has none; no-ops if a fence-level theme: is already present
  • FrontMatterBodyHasThemeKey — span-based line scanner (avoids string-split allocation) used by the above helper

Tests

  • 4 parser tests: directive parsed correctly, absent → null, whitespace-only → null, stored in FrontMatter dict
  • 5 renderer tests: deck-level applied to mermaid fence, deck-level applied to diagram fence, fence-level override produces different SVG than deck-level, no-regression without directive, front matter with other keys but no theme: receives injection while explicit theme: dracula is preserved

Docs / Sample

  • doc/marp-markdown.md — new table entry + "Diagram Theme Directive" section documenting precedence and syntax
  • samples/09-diagrams.md — adds diagram-theme: prism to front matter; labels slides showing deck-level inheritance vs. fence-level override

Copilot AI and others added 3 commits March 14, 2026 21:13
…edence

Co-authored-by: jongalloway <68539+jongalloway@users.noreply.github.com>
…eam variable

Co-authored-by: jongalloway <68539+jongalloway@users.noreply.github.com>
@jongalloway jongalloway marked this pull request as ready for review March 14, 2026 22:47
@jongalloway jongalloway requested a review from Copilot March 14, 2026 22:47
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a deck-level diagram-theme front-matter directive so authors can set a default DiagramForge theme for all mermaid / diagram fences, while keeping fence-level theme: as the highest-precedence override.

Changes:

  • Introduces SlideDeck.DiagramTheme and parses diagram-theme from front matter.
  • Threads the deck-level theme through the PPTX render pipeline and injects theme: ... into diagram fence YAML front matter when missing.
  • Adds parser/renderer tests plus updates documentation and the diagrams sample deck.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/MarpToPptx.Tests/PptxRendererTests.cs Adds renderer tests validating SVG output and precedence behavior for deck-level diagram theming.
tests/MarpToPptx.Tests/ParserTests.cs Adds parser tests ensuring diagram-theme is parsed, normalized, and stored correctly.
src/MarpToPptx.Pptx/Rendering/OpenXmlPptxRenderer.cs Carries deck-level diagram theme through SlideRenderContext and injects it into diagram source when needed.
src/MarpToPptx.Core/Parsing/MarpMarkdownParser.cs Reads diagram-theme from deck front matter and stores it on SlideDeck.
src/MarpToPptx.Core/Models/SlideDeck.cs Adds the DiagramTheme property to the deck model.
samples/09-diagrams.md Demonstrates deck-level default theme and per-fence overrides in the sample deck.
doc/marp-markdown.md Documents the new directive, syntax, and precedence.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +5739 to +5785
// ── diagram-theme directive tests ──────────────────────────────────────────

[Fact]
public void Renderer_GlobalDiagramTheme_MermaidFence_WithoutFenceLevelTheme_RendersSvg()
{
// A deck with diagram-theme: prism should apply the theme to mermaid fences without
// their own fence-level theme, and the diagram must render as an SVG picture shape.
using var workspace = TestWorkspace.Create();

var markdownPath = workspace.WriteMarkdown(
"deck.md",
"""
---
diagram-theme: prism
---

# Mermaid Diagram

```mermaid
flowchart LR
A --> B
```
""");

var outputPath = workspace.GetPath("deck.pptx");
RenderDeck(markdownPath, outputPath, workspace.RootPath);

using var document = PresentationDocument.Open(outputPath, false);
var slidePart = document.PresentationPart!.SlideParts.Single();

var pictures = slidePart.Slide!.Descendants<P.Picture>().ToArray();
Assert.NotEmpty(pictures);

var svgPart = slidePart.ImageParts
.FirstOrDefault(p => string.Equals(p.ContentType, "image/svg+xml", StringComparison.OrdinalIgnoreCase));
Assert.NotNull(svgPart);

using var stream = svgPart!.GetStream();
var svg = new System.IO.StreamReader(stream).ReadToEnd();
Assert.Contains("<svg", svg, StringComparison.OrdinalIgnoreCase);

var textRuns = slidePart.Slide.Descendants<A.Text>().Select(t => t.Text).ToArray();
Assert.DoesNotContain(textRuns, t => t.StartsWith("Mermaid parse error:", StringComparison.Ordinal));

var validationErrors = new OpenXmlPackageValidator().Validate(document);
Assert.Empty(validationErrors);
}
Comment on lines +5779 to +5782

var textRuns = slidePart.Slide.Descendants<A.Text>().Select(t => t.Text).ToArray();
Assert.DoesNotContain(textRuns, t => t.StartsWith("Mermaid parse error:", StringComparison.Ordinal));

Comment on lines +1914 to +1941
if (source.StartsWith("---", StringComparison.Ordinal) &&
(source.Length == 3 || source[3] is '\n' or '\r'))
{
var openEnd = source.IndexOf('\n', 3);
if (openEnd < 0)
return source; // Opening delimiter with no newline — return unchanged.

var bodyStart = openEnd + 1;

// Search for the closing --- delimiter. Start from openEnd so the \n that
// terminates the opening delimiter line is included in the search window,
// which correctly handles an empty front matter block (--- immediately after ---\n).
var closeLineIdx = source.IndexOf("\n---", openEnd, StringComparison.Ordinal);
if (closeLineIdx >= 0)
{
var frontMatterBody = closeLineIdx >= bodyStart
? source[bodyStart..closeLineIdx]
: string.Empty;

// If the fence already specifies a theme, it takes precedence — return unchanged.
// Use a span-based line scan to avoid allocating a split array.
if (FrontMatterBodyHasThemeKey(frontMatterBody))
{
return source;
}

// Inject the global theme as the first key inside the existing front matter block.
return source[..bodyStart] + $"theme: {globalDiagramTheme}\n" + source[bodyStart..];
Comment on lines +204 to +211
````markdown
```mermaid
---
theme: dracula
---
flowchart LR
A --> B
````
## Mermaid With Dracula Theme

This Mermaid diagram uses DiagramForge frontmatter to switch to the built-in Dracula theme and apply additional styling overrides.
This Mermaid diagram uses DiagramForge frontmatter to override the deck-level `prism` theme with `theme: dracula`, plus additional styling options.
@jongalloway
Copy link
Owner

@copilot review comments

…oc closing fence, front matter spelling

Co-authored-by: jongalloway <68539+jongalloway@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 14, 2026

@copilot review comments

Addressed all five review comments in commit 6e95e41:

  1. Test indentation — all 5 new renderer tests now use 4-space indent matching the rest of the file.
  2. Nullable flow — all slidePart.Slide / sp.Slide accesses in the new tests now use the null-forgiving ! operator, consistent with the rest of the file.
  3. InjectDiagramThemeIfNeeded trailing whitespace — replaced the literal position-based --- check with a trim-based first-line check (firstLine.Trim() == "---"). Added a FindClosingFrontMatterDelimiter helper that similarly trims each candidate closing line, so delimiters with trailing whitespace are correctly recognized.
  4. Doc missing closing fence — added the missing closing ``` for the inner mermaid fence in the ````markdown` example block.
  5. "frontmatter" → "front matter" — fixed all four occurrences in samples/09-diagrams.md.

Copilot AI requested a review from jongalloway March 14, 2026 22:58
@jongalloway jongalloway merged commit f1ce870 into main Mar 14, 2026
5 checks passed
@jongalloway jongalloway deleted the copilot/add-global-diagram-theme-support branch March 14, 2026 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add global diagram-theme support with fence-level theme override precedence

3 participants