diff --git a/PowerDocu.ClassicWorkflowDocumenter/ClassicWorkflowDocumentationContent.cs b/PowerDocu.ClassicWorkflowDocumenter/ClassicWorkflowDocumentationContent.cs
new file mode 100644
index 0000000..ec1671c
--- /dev/null
+++ b/PowerDocu.ClassicWorkflowDocumenter/ClassicWorkflowDocumentationContent.cs
@@ -0,0 +1,65 @@
+using System.IO;
+using System.Linq;
+using PowerDocu.Common;
+
+namespace PowerDocu.ClassicWorkflowDocumenter
+{
+ public class ClassicWorkflowDocumentationContent
+ {
+ public string folderPath, filename;
+ public ClassicWorkflowEntity workflow;
+ public DocumentationContext context;
+
+ public string headerOverview = "Overview";
+ public string headerSteps = "Steps";
+ public string headerStepDetails = "Step Details";
+ public string headerTableRelationships = "Table Relationships";
+ public string headerProperties = "Properties";
+ public string headerDocumentationGenerated = "Documentation generated at";
+
+ public ClassicWorkflowDocumentationContent(ClassicWorkflowEntity workflow, string path, DocumentationContext context)
+ {
+ NotificationHelper.SendNotification("Preparing documentation content for Classic Workflow: " + workflow.GetDisplayName());
+ this.workflow = workflow;
+ this.context = context;
+ folderPath = path + CharsetHelper.GetSafeName(@"\WorkflowDoc " + workflow.GetDisplayName() + @"\");
+ Directory.CreateDirectory(folderPath);
+ filename = CharsetHelper.GetSafeName(workflow.GetDisplayName());
+ }
+
+ public string GetTableDisplayName(string schemaName)
+ {
+ if (string.IsNullOrEmpty(schemaName)) return schemaName;
+ string displayName = context?.GetTableDisplayName(schemaName) ?? schemaName;
+ // If display name differs from logical name, show both: "Display Name (logical_name)"
+ if (!string.IsNullOrEmpty(displayName) && !displayName.Equals(schemaName, System.StringComparison.OrdinalIgnoreCase))
+ return displayName + " (" + schemaName + ")";
+ return schemaName;
+ }
+
+ ///
+ /// Resolves a field logical name to "Display Name (logical_name)" by looking up
+ /// the column in the primary entity's table definition.
+ ///
+ public string GetFieldDisplayName(string fieldLogicalName, string entityLogicalName = null)
+ {
+ if (string.IsNullOrEmpty(fieldLogicalName)) return fieldLogicalName;
+
+ string tableName = entityLogicalName ?? workflow.PrimaryEntity;
+ if (string.IsNullOrEmpty(tableName) || context?.Tables == null) return fieldLogicalName;
+
+ var table = context.Tables.FirstOrDefault(t =>
+ t.getName().Equals(tableName, System.StringComparison.OrdinalIgnoreCase));
+ if (table == null) return fieldLogicalName;
+
+ var column = table.GetColumns().FirstOrDefault(c =>
+ c.getLogicalName().Equals(fieldLogicalName, System.StringComparison.OrdinalIgnoreCase));
+ if (column == null) return fieldLogicalName;
+
+ string displayName = column.getDisplayName();
+ if (!string.IsNullOrEmpty(displayName) && !displayName.Equals(fieldLogicalName, System.StringComparison.OrdinalIgnoreCase))
+ return displayName + " (" + fieldLogicalName + ")";
+ return fieldLogicalName;
+ }
+ }
+}
diff --git a/PowerDocu.ClassicWorkflowDocumenter/ClassicWorkflowDocumentationGenerator.cs b/PowerDocu.ClassicWorkflowDocumenter/ClassicWorkflowDocumentationGenerator.cs
new file mode 100644
index 0000000..23b4a77
--- /dev/null
+++ b/PowerDocu.ClassicWorkflowDocumenter/ClassicWorkflowDocumentationGenerator.cs
@@ -0,0 +1,67 @@
+using System;
+using System.IO;
+using PowerDocu.Common;
+
+namespace PowerDocu.ClassicWorkflowDocumenter
+{
+ public static class ClassicWorkflowDocumentationGenerator
+ {
+ public static void GenerateOutput(DocumentationContext context, string path)
+ {
+ if (context.ClassicWorkflows == null || context.ClassicWorkflows.Count == 0 || !context.Config.documentClassicWorkflows) return;
+
+ DateTime startDocGeneration = DateTime.Now;
+ NotificationHelper.SendNotification($"Found {context.ClassicWorkflows.Count} Classic Workflow(s) in the solution.");
+
+ if (context.FullDocumentation)
+ {
+ foreach (ClassicWorkflowEntity workflow in context.ClassicWorkflows)
+ {
+ ClassicWorkflowDocumentationContent content = new ClassicWorkflowDocumentationContent(workflow, path, context);
+
+ // Generate workflow flow diagram
+ if (workflow.Steps.Count > 0)
+ {
+ try
+ {
+ GraphBuilder graphBuilder = new GraphBuilder(workflow, content.folderPath);
+ graphBuilder.BuildGraph();
+ }
+ catch (Exception ex)
+ {
+ NotificationHelper.SendNotification(" - Warning: Could not generate workflow diagram: " + ex.Message);
+ }
+ }
+
+ string wordTemplate = (!String.IsNullOrEmpty(context.Config.wordTemplate) && File.Exists(context.Config.wordTemplate))
+ ? context.Config.wordTemplate : null;
+ if (context.Config.outputFormat.Equals(OutputFormatHelper.Word) || context.Config.outputFormat.Equals(OutputFormatHelper.All))
+ {
+ NotificationHelper.SendNotification("Creating Word documentation for Classic Workflow: " + workflow.GetDisplayName());
+ ClassicWorkflowWordDocBuilder wordDoc = new ClassicWorkflowWordDocBuilder(content, wordTemplate);
+ }
+ if (context.Config.outputFormat.Equals(OutputFormatHelper.Markdown) || context.Config.outputFormat.Equals(OutputFormatHelper.All))
+ {
+ NotificationHelper.SendNotification("Creating Markdown documentation for Classic Workflow: " + workflow.GetDisplayName());
+ ClassicWorkflowMarkdownBuilder markdownDoc = new ClassicWorkflowMarkdownBuilder(content);
+ }
+ if (context.Config.outputFormat.Equals(OutputFormatHelper.Html) || context.Config.outputFormat.Equals(OutputFormatHelper.All))
+ {
+ NotificationHelper.SendNotification("Creating HTML documentation for Classic Workflow: " + workflow.GetDisplayName());
+ ClassicWorkflowHtmlBuilder htmlDoc = new ClassicWorkflowHtmlBuilder(content);
+ }
+ context.Progress?.Increment("Classic Workflows");
+ }
+ }
+ else
+ {
+ context.Progress?.Complete("ClassicWorkflows");
+ }
+
+ DateTime endDocGeneration = DateTime.Now;
+ NotificationHelper.SendNotification(
+ $"ClassicWorkflowDocumenter: Processed {context.ClassicWorkflows.Count} Classic Workflow(s) in {(endDocGeneration - startDocGeneration).TotalSeconds} seconds."
+ );
+ }
+ }
+}
diff --git a/PowerDocu.ClassicWorkflowDocumenter/ClassicWorkflowHtmlBuilder.cs b/PowerDocu.ClassicWorkflowDocumenter/ClassicWorkflowHtmlBuilder.cs
new file mode 100644
index 0000000..5a8725d
--- /dev/null
+++ b/PowerDocu.ClassicWorkflowDocumenter/ClassicWorkflowHtmlBuilder.cs
@@ -0,0 +1,587 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using PowerDocu.Common;
+
+namespace PowerDocu.ClassicWorkflowDocumenter
+{
+ class ClassicWorkflowHtmlBuilder : HtmlBuilder
+ {
+ private readonly ClassicWorkflowDocumentationContent content;
+ private readonly string mainFileName;
+ private readonly string triggerActionsFileName;
+ private string _navigationHtmlTop;
+ private string _navigationHtmlSub;
+ private string _metadataTableHtml;
+
+ public ClassicWorkflowHtmlBuilder(ClassicWorkflowDocumentationContent contentDocumentation)
+ {
+ content = contentDocumentation;
+ Directory.CreateDirectory(content.folderPath);
+ WriteDefaultStylesheet(content.folderPath);
+ Directory.CreateDirectory(Path.Combine(content.folderPath, "actions"));
+ WriteDefaultStylesheet(Path.Combine(content.folderPath, "actions"));
+
+ mainFileName = CollapseDashes(("workflow-" + content.filename + ".html").Replace(" ", "-"));
+ triggerActionsFileName = CollapseDashes(("triggersactions-" + content.filename + ".html").Replace(" ", "-"));
+
+ addMainPage();
+ addTriggersActionsPage();
+ addTriggerPage();
+ addActionPages();
+ NotificationHelper.SendNotification("Created HTML documentation for Classic Workflow: " + content.workflow.GetDisplayName());
+ }
+
+ // ── Navigation ──
+
+ private string getNavigationHtml(bool fromSubfolder = false)
+ => fromSubfolder
+ ? (_navigationHtmlSub ??= BuildNavigationHtmlCore(true))
+ : (_navigationHtmlTop ??= BuildNavigationHtmlCore(false));
+
+ private string BuildNavigationHtmlCore(bool fromSubfolder)
+ {
+ string prefix = fromSubfolder ? "../" : "";
+ var navItems = new List<(string label, string href)>();
+ if (content.context?.Solution != null)
+ {
+ string solutionPrefix = fromSubfolder ? "../../" : "../";
+ if (content.context?.Config?.documentSolution == true)
+ navItems.Add(("Solution", solutionPrefix + CrossDocLinkHelper.GetSolutionDocHtmlPath(content.context.Solution.UniqueName)));
+ else
+ navItems.Add((content.context.Solution.UniqueName, ""));
+ }
+ navItems.AddRange(new (string label, string href)[]
+ {
+ ("Overview", prefix + mainFileName),
+ ("Triggers & Actions", prefix + triggerActionsFileName),
+ ("Table Relationships", prefix + mainFileName + "#table-relationships")
+ });
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine($"
{Encode(content.workflow.GetDisplayName())}
");
+ sb.Append(NavigationList(navItems));
+ return sb.ToString();
+ }
+
+ private string buildMetadataTable()
+ {
+ if (_metadataTableHtml != null) return _metadataTableHtml;
+ StringBuilder sb = new StringBuilder();
+ sb.Append(TableStart("Property", "Value"));
+ sb.Append(TableRow("Name", content.workflow.GetDisplayName()));
+ sb.Append(TableRow("Primary Table", content.GetTableDisplayName(content.workflow.PrimaryEntity)));
+ sb.Append(TableRow("Category", content.workflow.GetCategoryLabel()));
+ sb.Append(TableRow("Mode", content.workflow.GetModeLabel()));
+ sb.Append(TableRow("Scope", content.workflow.GetScopeLabel()));
+ sb.Append(TableRow("Run As", content.workflow.GetRunAsLabel()));
+ sb.Append(TableRow("State", content.workflow.GetStateLabel()));
+ sb.Append(TableRow("Is Customizable", content.workflow.IsCustomizable ? "Yes" : "No"));
+ if (!string.IsNullOrEmpty(content.workflow.ID))
+ sb.Append(TableRow("ID", content.workflow.ID));
+ if (!string.IsNullOrEmpty(content.workflow.OwnerId))
+ sb.Append(TableRow("Owner", content.workflow.OwnerId));
+ if (!string.IsNullOrEmpty(content.workflow.Description))
+ sb.Append(TableRow("Description", content.workflow.Description));
+ if (!string.IsNullOrEmpty(content.workflow.IntroducedVersion))
+ sb.Append(TableRow("Version", content.workflow.IntroducedVersion));
+ sb.Append(TableRow("Number of Actions", CountAllSteps(content.workflow.Steps).ToString()));
+ sb.Append(TableRow("Number of Conditions", CountConditions(content.workflow.Steps).ToString()));
+ sb.Append(TableRow(content.headerDocumentationGenerated, PowerDocuReleaseHelper.GetTimestampWithVersion()));
+ sb.Append(TableEnd());
+ _metadataTableHtml = sb.ToString();
+ return _metadataTableHtml;
+ }
+
+ // ── Main Index Page ──
+
+ private void addMainPage()
+ {
+ StringBuilder body = new StringBuilder();
+
+ // Overview
+ body.AppendLine(HeadingWithId(1, content.workflow.GetDisplayName(), "overview"));
+ body.AppendLine(buildMetadataTable());
+
+ // Workflow diagram
+ addWorkflowDiagram(body);
+
+ // Table relationships
+ if (content.workflow.TableReferences.Count > 0)
+ {
+ body.AppendLine(HeadingWithId(2, content.headerTableRelationships, "table-relationships"));
+ body.AppendLine($"This workflow references {content.workflow.TableReferences.Count} table relationship(s).
");
+ body.Append(TableStart("Table Display Name", "Table Logical Name", "Reference Type"));
+ foreach (var tableRef in content.workflow.TableReferences)
+ {
+ string displayName = content.GetTableDisplayName(tableRef.TableLogicalName);
+ string displayCell;
+ if (content.context?.Solution != null && content.context?.Config?.documentSolution == true)
+ {
+ string anchor = CrossDocLinkHelper.GetSolutionTableHtmlAnchor(tableRef.TableLogicalName);
+ string solutionHtmlPath = CrossDocLinkHelper.GetSolutionDocHtmlPath(content.context.Solution.UniqueName);
+ displayCell = $"{Encode(displayName)}";
+ }
+ else
+ {
+ displayCell = Encode(displayName);
+ }
+ body.Append(TableRowRaw(displayCell, Encode(tableRef.TableLogicalName), Encode(tableRef.ReferenceType.ToString())));
+ }
+ body.AppendLine(TableEnd());
+ }
+
+ SaveHtmlFile(Path.Combine(content.folderPath, mainFileName),
+ WrapInHtmlPage($"Classic Workflow - {content.workflow.GetDisplayName()}", body.ToString(), getNavigationHtml(false)));
+ }
+
+ private void AddActionLinksRecursive(StringBuilder body, List steps, int depth)
+ {
+ foreach (var step in steps)
+ {
+ // ConditionBranch steps: show branch label in the list with children nested
+ if (step.StepType == ClassicWorkflowStepType.ConditionBranch)
+ {
+ string branchLabel = step.Name ?? "Condition Branch";
+ body.AppendLine($"{Encode(branchLabel)}");
+ if (step.ChildSteps.Count > 0)
+ {
+ body.AppendLine("");
+ AddActionLinksRecursive(body, step.ChildSteps, depth + 1);
+ body.AppendLine("
");
+ }
+ continue;
+ }
+
+ string safeName = CharsetHelper.GetSafeName(step.Name ?? step.GetStepTypeLabel());
+ string stepFileName = "actions/" + safeName + ".html";
+ body.AppendLine(BulletItemRaw(Link(step.Name ?? step.GetStepTypeLabel(), stepFileName)
+ + $" ({step.GetStepTypeLabel()})"));
+
+ if (step.ChildSteps.Count > 0)
+ {
+ body.AppendLine("");
+ AddActionLinksRecursive(body, step.ChildSteps, depth + 1);
+ body.AppendLine("
");
+ }
+ }
+ }
+
+ // ── Triggers & Actions Overview Page ──
+
+ private void addTriggersActionsPage()
+ {
+ StringBuilder body = new StringBuilder();
+ body.AppendLine(Heading(1, content.workflow.GetDisplayName()));
+ body.AppendLine(buildMetadataTable());
+
+ // Trigger section with link
+ body.AppendLine(Heading(2, "Trigger"));
+ body.AppendLine(BulletListStart());
+ body.AppendLine(BulletItemRaw(Link("Trigger", "actions/Trigger.html")));
+ body.AppendLine(BulletListEnd());
+
+ // Actions section with links
+ body.AppendLine(Heading(2, "Actions"));
+ if (content.workflow.Steps.Count > 0)
+ {
+ int totalSteps = CountAllSteps(content.workflow.Steps);
+ body.AppendLine($"There are a total of {totalSteps} action(s) in this workflow:
");
+ body.AppendLine(BulletListStart());
+ AddActionLinksRecursive(body, content.workflow.Steps, 0);
+ body.AppendLine(BulletListEnd());
+ }
+
+ SaveHtmlFile(Path.Combine(content.folderPath, triggerActionsFileName),
+ WrapInHtmlPage("Triggers & Actions - " + content.workflow.GetDisplayName(), body.ToString(), getNavigationHtml(false)));
+ }
+
+ // ── Trigger Page ──
+
+ private void addTriggerPage()
+ {
+ StringBuilder body = new StringBuilder();
+ body.AppendLine(Heading(1, content.workflow.GetDisplayName()));
+ body.AppendLine(buildMetadataTable());
+ body.AppendLine(Heading(2, "Trigger"));
+
+ // Trigger type as bulleted list
+ body.AppendLine("Trigger Type
");
+ body.AppendLine("");
+ if (content.workflow.OnDemand)
+ body.AppendLine("- On-Demand
");
+ if (content.workflow.TriggerOnCreate)
+ body.AppendLine("- Record Created
");
+ if (content.workflow.TriggerOnDelete)
+ body.AppendLine("- Record Deleted
");
+ if (!string.IsNullOrEmpty(content.workflow.TriggerOnUpdateAttributeList))
+ {
+ body.AppendLine("- Record Updated
");
+ foreach (string field in content.workflow.TriggerOnUpdateAttributeList.Split(','))
+ {
+ string trimmed = field.Trim();
+ if (!string.IsNullOrEmpty(trimmed))
+ body.AppendLine($"- {Encode(content.GetFieldDisplayName(trimmed))}
");
+ }
+ body.AppendLine("
");
+ }
+ if (!content.workflow.OnDemand && !content.workflow.TriggerOnCreate && !content.workflow.TriggerOnDelete && string.IsNullOrEmpty(content.workflow.TriggerOnUpdateAttributeList))
+ body.AppendLine("- None
");
+ body.AppendLine("
");
+
+ SaveHtmlFile(Path.Combine(content.folderPath, "actions", "Trigger.html"),
+ WrapInHtmlPage("Trigger - " + content.workflow.GetDisplayName(), body.ToString(), getNavigationHtml(true)));
+ }
+
+ // ── Per-Action Pages ──
+
+ private void addActionPages()
+ {
+ if (content.workflow.Steps.Count == 0) return;
+ AddActionPagesRecursive(content.workflow.Steps);
+ }
+
+ private void AddActionPagesRecursive(List steps)
+ {
+ // Build a filtered list of non-branch steps for prev/next navigation
+ var navigableSteps = new List();
+ for (int i = 0; i < steps.Count; i++)
+ {
+ if (steps[i].StepType != ClassicWorkflowStepType.ConditionBranch)
+ navigableSteps.Add(steps[i]);
+ }
+
+ for (int i = 0; i < steps.Count; i++)
+ {
+ var step = steps[i];
+
+ // ConditionBranch steps are rendered inline on the parent page;
+ // skip creating separate pages but still recurse into their children.
+ if (step.StepType == ClassicWorkflowStepType.ConditionBranch)
+ {
+ if (step.ChildSteps.Count > 0)
+ AddActionPagesRecursive(step.ChildSteps);
+ continue;
+ }
+
+ int navIndex = navigableSteps.IndexOf(step);
+ ClassicWorkflowStep prevStep = (navIndex > 0) ? navigableSteps[navIndex - 1] : null;
+ ClassicWorkflowStep nextStep = (navIndex >= 0 && navIndex + 1 < navigableSteps.Count) ? navigableSteps[navIndex + 1] : null;
+
+ {
+ string safeName = CharsetHelper.GetSafeName(step.Name ?? step.GetStepTypeLabel());
+ string fileName = safeName + ".html";
+
+ StringBuilder body = new StringBuilder();
+ body.AppendLine(Heading(1, content.workflow.GetDisplayName()));
+ body.AppendLine(buildMetadataTable());
+ body.AppendLine(Heading(2, step.Name ?? step.GetStepTypeLabel()));
+
+ // Action detail table
+ body.Append(TableStart("Property", "Value"));
+ body.Append(TableRow("Name", step.Name ?? ""));
+ body.Append(TableRow("Type", step.GetStepTypeLabel()));
+ if (!string.IsNullOrEmpty(step.StepDescription))
+ body.Append(TableRow("Description", step.StepDescription));
+ if (!string.IsNullOrEmpty(step.TargetEntity))
+ body.Append(TableRow("Target Table", content.GetTableDisplayName(step.TargetEntity)));
+ if (!string.IsNullOrEmpty(step.CustomActivityName))
+ body.Append(TableRow("Custom Activity", step.CustomActivityName));
+ if (!string.IsNullOrEmpty(step.CustomActivityClass))
+ body.Append(TableRow("Class", step.CustomActivityClass));
+ if (!string.IsNullOrEmpty(step.CustomActivityAssembly))
+ body.Append(TableRow("Assembly", step.CustomActivityAssembly));
+ if (!string.IsNullOrEmpty(step.CustomActivityFriendlyName))
+ body.Append(TableRow("Friendly Name", step.CustomActivityFriendlyName));
+ if (!string.IsNullOrEmpty(step.CustomActivityDescription))
+ body.Append(TableRow("Description", step.CustomActivityDescription));
+ if (!string.IsNullOrEmpty(step.CustomActivityGroupName))
+ body.Append(TableRow("Group", step.CustomActivityGroupName));
+ body.AppendLine(TableEnd());
+
+ // Condition tree
+ if (step.ConditionTree != null)
+ {
+ body.AppendLine(Heading(3, "Condition"));
+ RenderConditionSectionHtml(body, step.ConditionTree);
+ }
+ else if (!string.IsNullOrEmpty(step.ConditionDescription))
+ {
+ body.AppendLine(Heading(3, "Condition"));
+ body.AppendLine($"{Encode(step.ConditionDescription)}
");
+ }
+
+ // Field assignments / Inputs
+ if (step.Fields.Count > 0)
+ {
+ string inputsLabel = step.StepType switch
+ {
+ ClassicWorkflowStepType.Custom => "Inputs",
+ ClassicWorkflowStepType.SendEmail => "Email Properties",
+ _ => "Field Assignments"
+ };
+ body.AppendLine(Heading(3, inputsLabel));
+ body.Append(TableStart("Field", "Value"));
+ foreach (var field in step.Fields)
+ {
+ body.Append(TableRow(field.FieldName ?? "", field.Value ?? ""));
+ }
+ body.AppendLine(TableEnd());
+ }
+
+ // Child steps / Subactions
+ if (step.ChildSteps.Count > 0)
+ {
+ if (step.StepType == ClassicWorkflowStepType.CheckCondition ||
+ step.StepType == ClassicWorkflowStepType.Wait)
+ {
+ // Render condition branches inline on the CheckCondition/Wait page
+ RenderConditionBranchesInline(body, step.ChildSteps);
+ }
+ else
+ {
+ body.AppendLine(Heading(3, "Subactions"));
+ body.AppendLine(BulletListStart());
+ foreach (var child in step.ChildSteps)
+ {
+ string childSafeName = CharsetHelper.GetSafeName(child.Name ?? child.GetStepTypeLabel());
+ body.AppendLine(BulletItemRaw(
+ Link(child.Name ?? child.GetStepTypeLabel(), childSafeName + ".html")
+ + $" ({child.GetStepTypeLabel()})"));
+ }
+ body.AppendLine(BulletListEnd());
+ }
+ }
+
+ // Previous Action(s)
+ if (prevStep != null)
+ {
+ body.AppendLine(Heading(3, "Previous Action(s)"));
+ string prevSafeName = CharsetHelper.GetSafeName(prevStep.Name ?? prevStep.GetStepTypeLabel());
+ body.Append(TableStart("Previous Action"));
+ body.Append(TableRowRaw(
+ Link(prevStep.Name ?? prevStep.GetStepTypeLabel(), prevSafeName + ".html")
+ + $" ({prevStep.GetStepTypeLabel()})"));
+ body.AppendLine(TableEnd());
+ }
+
+ // Next Action(s)
+ if (nextStep != null)
+ {
+ body.AppendLine(Heading(3, "Next Action(s)"));
+ string nextSafeName = CharsetHelper.GetSafeName(nextStep.Name ?? nextStep.GetStepTypeLabel());
+ body.Append(TableStart("Next Action"));
+ body.Append(TableRowRaw(
+ Link(nextStep.Name ?? nextStep.GetStepTypeLabel(), nextSafeName + ".html")
+ + $" ({nextStep.GetStepTypeLabel()})"));
+ body.AppendLine(TableEnd());
+ }
+
+ SaveHtmlFile(Path.Combine(content.folderPath, "actions", fileName),
+ WrapInHtmlPage(step.GetStepTypeLabel() + " - " + (step.Name ?? ""), body.ToString(), getNavigationHtml(true)));
+
+ // Recurse into child steps
+ if (step.ChildSteps.Count > 0)
+ AddActionPagesRecursive(step.ChildSteps);
+ }
+ }
+ }
+
+ // ── Inline Condition Branch Rendering ──
+
+ private void RenderConditionBranchesInline(StringBuilder body, List branches)
+ {
+ foreach (var branch in branches)
+ {
+ if (branch.StepType != ClassicWorkflowStepType.ConditionBranch) continue;
+
+ string branchLabel = branch.Name ?? "Condition Branch";
+ string borderColor = branchLabel == "Otherwise (Default)" ? "#888" : "#0078d4";
+
+ body.AppendLine($"");
+ body.AppendLine($"
{Encode(branchLabel)}
");
+
+ // Show condition tree if present
+ if (branch.ConditionTree != null)
+ {
+ RenderConditionSectionHtml(body, branch.ConditionTree);
+ }
+ else if (!string.IsNullOrEmpty(branch.ConditionDescription) && branchLabel != "Otherwise (Default)")
+ {
+ body.AppendLine($"
{Encode(branch.ConditionDescription)}
");
+ }
+
+ // Show the branch's child actions as links
+ if (branch.ChildSteps.Count > 0)
+ {
+ body.AppendLine("
Actions:
");
+ body.AppendLine(BulletListStart());
+ foreach (var child in branch.ChildSteps)
+ {
+ string childSafeName = CharsetHelper.GetSafeName(child.Name ?? child.GetStepTypeLabel());
+ body.AppendLine(BulletItemRaw(
+ Link(child.Name ?? child.GetStepTypeLabel(), childSafeName + ".html")
+ + $"
({child.GetStepTypeLabel()})"));
+ }
+ body.AppendLine(BulletListEnd());
+ }
+
+ body.AppendLine("
");
+ }
+ }
+
+ // ── Helpers ──
+
+ private void addWorkflowDiagram(StringBuilder body)
+ {
+ string svgFile = "workflow.svg";
+ string pngFile = "workflow.png";
+
+ if (!File.Exists(Path.Combine(content.folderPath, pngFile))) return;
+
+ body.AppendLine(HeadingWithId(2, "Workflow Diagram", "workflow-diagram"));
+ body.AppendLine("The following diagram shows the flow of the workflow including condition branches.
");
+
+ if (File.Exists(Path.Combine(content.folderPath, svgFile)))
+ {
+ try
+ {
+ string svgContent = File.ReadAllText(Path.Combine(content.folderPath, svgFile));
+ int svgStart = svgContent.IndexOf("