diff --git a/src/Whipstaff.Statiq/Mermaid/MermaidDiagramModule.cs b/src/Whipstaff.Statiq/Mermaid/MermaidDiagramModule.cs
new file mode 100644
index 000000000..d138de1a1
--- /dev/null
+++ b/src/Whipstaff.Statiq/Mermaid/MermaidDiagramModule.cs
@@ -0,0 +1,111 @@
+// Copyright (c) 2022 DHGMS Solutions and Contributors. All rights reserved.
+// This file is licensed to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Statiq.Common;
+using Whipstaff.Mermaid.HttpServer;
+using Whipstaff.Mermaid.Playwright;
+using Whipstaff.Playwright;
+
+namespace Whipstaff.Statiq.Mermaid
+{
+ ///
+ /// Statiq module for producing mermaid diagrams.
+ ///
+ public sealed class MermaidDiagramModule : Module
+ {
+ private readonly System.IO.Abstractions.IFileSystem _fileSystem;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// File system abstraction.
+ public MermaidDiagramModule(System.IO.Abstractions.IFileSystem fileSystem)
+ {
+ ArgumentNullException.ThrowIfNull(fileSystem);
+ _fileSystem = fileSystem;
+ }
+
+ ///
+ protected override async Task> ExecuteInputAsync(IDocument input, IExecutionContext context)
+ {
+ ArgumentNullException.ThrowIfNull(input);
+ ArgumentNullException.ThrowIfNull(context);
+
+ context.LogInformation(input, "Starting Mermaid Diagram CLI Module");
+
+ var path = _fileSystem.Path;
+ var inputFilename = path.GetFullPath(input.Source.FullPath);
+
+ var rootPath = path.GetFullPath(context.FileSystem.RootPath.FullPath);
+
+ var destination = input.Destination.FullPath;
+
+ // this is taking the input folder, the output folder
+ var outputFilename = path.Combine(
+ rootPath,
+ "output",
+ destination);
+
+ // and reversing the path separator on the output
+ outputFilename = path.GetFullPath(outputFilename);
+
+ // finally replace the file extension
+ // could be a setting for now assume svg
+ // might be a better way of doing all the output steps
+ // within the Statiq Context would need to dig.
+ outputFilename = path.ChangeExtension(
+ outputFilename,
+ ".svg");
+
+ var targetDir = path.GetDirectoryName(outputFilename);
+
+ if (targetDir == null)
+ {
+ throw new InvalidOperationException("failed to work out target directory");
+ }
+
+ var directory = _fileSystem.Directory;
+ if (!directory.Exists(targetDir))
+ {
+ _ = directory.CreateDirectory(targetDir);
+ }
+
+ var logMessageActions = new PlaywrightRendererLogMessageActions();
+ var loggerFactory = context.GetRequiredService();
+ var logger = loggerFactory.CreateLogger();
+ var logMessageActionsWrapper = new PlaywrightRendererLogMessageActionsWrapper(logMessageActions, logger);
+ var mermaidHttpServer = MermaidHttpServerFactory.GetTestServer(loggerFactory);
+
+ var playwrightRenderer = new Whipstaff.Mermaid.Playwright.PlaywrightRenderer(
+ mermaidHttpServer,
+ logMessageActionsWrapper);
+
+ var markdown = await _fileSystem.File.ReadAllTextAsync(inputFilename);
+
+ var diagramResponse = await playwrightRenderer.GetDiagram(
+ markdown,
+ PlaywrightBrowserTypeAndChannel.ChromiumDefault())
+ .ConfigureAwait(false);
+
+ if (diagramResponse == null)
+ {
+ throw new InvalidOperationException("Failed to generate diagram");
+ }
+
+ await _fileSystem.File.WriteAllTextAsync(
+ outputFilename,
+ diagramResponse.Svg)
+ .ConfigureAwait(false);
+
+ // keeping the working the same as the previous cli version
+ // could return the response object in future.
+ return Array.Empty();
+ }
+ }
+}
diff --git a/src/Whipstaff.Statiq/Mermaid/MermaidDiagramPipeline.cs b/src/Whipstaff.Statiq/Mermaid/MermaidDiagramPipeline.cs
new file mode 100644
index 000000000..5eb23eae1
--- /dev/null
+++ b/src/Whipstaff.Statiq/Mermaid/MermaidDiagramPipeline.cs
@@ -0,0 +1,29 @@
+// Copyright (c) 2022 DHGMS Solutions and Contributors. All rights reserved.
+// This file is licensed to you under the MIT license.
+// See the LICENSE file in the project root for full license information.
+
+using Statiq.Common;
+using Statiq.Core;
+
+namespace Whipstaff.Statiq.Mermaid
+{
+ ///
+ /// Statiq pipeline for processing mermaid diagram files.
+ ///
+ public sealed class MermaidDiagramPipeline : Pipeline
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MermaidDiagramPipeline()
+ {
+ // we need to check for mermaidjs-cli
+ // we need to look for *.mmd files
+ var patterns = new[] { "./**/*.mmd" };
+ var readFiles = new ReadFiles(patterns);
+ InputModules = new ModuleList(readFiles);
+
+ ProcessModules = new ModuleList(new MermaidDiagramModule(new System.IO.Abstractions.FileSystem()));
+ }
+ }
+}
diff --git a/src/Whipstaff.Statiq/Whipstaff.Statiq.csproj b/src/Whipstaff.Statiq/Whipstaff.Statiq.csproj
new file mode 100644
index 000000000..0c30c8572
--- /dev/null
+++ b/src/Whipstaff.Statiq/Whipstaff.Statiq.csproj
@@ -0,0 +1,24 @@
+
+
+
+ net8.0
+ enable
+ Re-usable logic for working with WPF.
+
+
+
+
+ $(PackageVersion)-beta
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Whipstaff.sln b/src/Whipstaff.sln
index f4569787b..0d6d7fed4 100644
--- a/src/Whipstaff.sln
+++ b/src/Whipstaff.sln
@@ -85,7 +85,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Whipstaff.Mermaid", "Whipst
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Whipstaff.Wpf.MaterialDesign", "Whipstaff.Wpf.MaterialDesign\Whipstaff.Wpf.MaterialDesign.csproj", "{F23F9BFF-DF01-43A3-85E8-DF87CDA02A58}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Whipstaff.Wpf.AvalonEdit", "Whipstaff.Wpf.AvalonEdit\Whipstaff.Wpf.AvalonEdit.csproj", "{4E69353A-56B5-4EAE-8BE4-8975EFDB7C4D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Whipstaff.Wpf.AvalonEdit", "Whipstaff.Wpf.AvalonEdit\Whipstaff.Wpf.AvalonEdit.csproj", "{4E69353A-56B5-4EAE-8BE4-8975EFDB7C4D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Whipstaff.Statiq", "Whipstaff.Statiq\Whipstaff.Statiq.csproj", "{AE602A3A-6E12-4881-8EF6-A078C11BDF0E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -257,6 +259,10 @@ Global
{4E69353A-56B5-4EAE-8BE4-8975EFDB7C4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E69353A-56B5-4EAE-8BE4-8975EFDB7C4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E69353A-56B5-4EAE-8BE4-8975EFDB7C4D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AE602A3A-6E12-4881-8EF6-A078C11BDF0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AE602A3A-6E12-4881-8EF6-A078C11BDF0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AE602A3A-6E12-4881-8EF6-A078C11BDF0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AE602A3A-6E12-4881-8EF6-A078C11BDF0E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE