Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions src/Elastic.Markdown/IO/MarkdownFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,27 @@ public static List<PageTocItem> GetAnchors(
.ToArray();

var includedTocs = includes
.SelectMany(i => i!.Anchors!.TableOfContentItems
.Select(item => new { TocItem = item, i.Block.Line }))
.SelectMany(i =>
{
// Calculate the heading level context at the include block position
var precedingLevel = GetPrecedingHeadingLevel(i!.Block);

return i.Anchors!.TableOfContentItems
.Select(item =>
{
// Only adjust stepper steps, not regular headings
// Stepper steps default to level 2 when parsed in isolation (no preceding heading in snippet),
// but should be relative to the preceding heading in the parent document
var adjustedItem = item;
if (item.IsStepperStep && precedingLevel.HasValue && item.Level == 2)
{
// The step was parsed without context (defaulted to h2)
// Adjust it to be one level deeper than the preceding heading
adjustedItem = item with { Level = Math.Min(precedingLevel.Value + 1, 6) };
}
return new { TocItem = adjustedItem, i.Block.Line };
});
})
.ToArray();

// Collect headings from standard markdown
Expand Down Expand Up @@ -255,7 +274,8 @@ public static List<PageTocItem> GetAnchors(
{
Heading = processedTitle,
Slug = step.Anchor,
Level = step.HeadingLevel // Use dynamic heading level
Level = step.HeadingLevel, // Use dynamic heading level
IsStepperStep = true
},
step.Line
};
Expand Down Expand Up @@ -300,6 +320,38 @@ private static bool IsNestedInOtherDirective(DirectiveBlock block)
return false;
}

/// <summary>
/// Finds the heading level that precedes the given block in the document.
/// Used to provide context for included snippets so stepper heading levels
/// can be adjusted relative to the parent document's structure.
/// </summary>
private static int? GetPrecedingHeadingLevel(MarkdownObject block)
{
// Find the document root
var current = block;
while (current is ContainerBlock container && container.Parent != null)
current = container.Parent;

if (current is not ContainerBlock root)
return null;

// Find all blocks and locate this one
var allBlocks = root.Descendants().ToList();
var thisIndex = allBlocks.IndexOf(block);

if (thisIndex == -1)
return null;

// Look backwards for the most recent heading
for (var i = thisIndex - 1; i >= 0; i--)
{
if (allBlocks[i] is HeadingBlock heading)
return heading.Level;
}

return null;
}

private YamlFrontMatter ProcessYamlFrontMatter(MarkdownDocument document)
{
if (document.FirstOrDefault() is not YamlFrontMatterBlock yaml)
Expand Down
7 changes: 7 additions & 0 deletions src/Elastic.Markdown/MarkdownLayoutViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,11 @@ public record PageTocItem
public required string Heading { get; init; }
public required string Slug { get; init; }
public required int Level { get; init; }

/// <summary>
/// Indicates if this ToC item came from a stepper step.
/// Used to distinguish stepper steps (which may need level adjustment in includes)
/// from regular headings (which should preserve their levels).
/// </summary>
public bool IsStepperStep { get; init; }
}
Loading
Loading