From f3983d01f52d6b26df91a5757bb5fd66156112d3 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Tue, 16 Sep 2025 22:01:12 +0200 Subject: [PATCH 1/9] Fix appearance of multiple API calls in console codeblock --- docs/syntax/code.md | 52 ++++- docs/testing/index.md | 31 +++ .../Assets/markdown/code.css | 12 +- .../Myst/CodeBlocks/Code.cshtml | 18 +- .../Myst/CodeBlocks/CodeViewModel.cs | 2 +- .../Myst/CodeBlocks/EnhancedCodeBlock.cs | 8 +- .../EnhancedCodeBlockHtmlRenderer.cs | 2 +- .../CodeBlocks/EnhancedCodeBlockParser.cs | 212 +++++++++++++----- .../Myst/Components/ApplicableToViewModel.cs | 1 + .../Myst/Directives/DirectiveHtmlRenderer.cs | 2 +- 10 files changed, 272 insertions(+), 68 deletions(-) diff --git a/docs/syntax/code.md b/docs/syntax/code.md index 4cb8e8503..82d7f7bb3 100644 --- a/docs/syntax/code.md +++ b/docs/syntax/code.md @@ -267,10 +267,6 @@ project: ### Console code blocks -:::{note} -This feature is still being developed. -::: - We document a lot of API endpoints at Elastic. For these endpoints, we support `console` as a language. The term console relates to the dev console in kibana which users can link to directly from these code snippets. In a console code block, the first line is highlighted as a dev console string and the remainder as json: @@ -309,6 +305,54 @@ GET /mydocuments/_search :::: +Console code blocks now support multiple API calls within a single code block. When you have multiple console commands, they are displayed as separate sections within the same block with proper visual separation: + +::::{tab-set} + +:::{tab-item} Output + +```console +GET /mydocuments/_search +{ + "from": 1, + "query": { + "match_all" {} + } +} + +POST /mydocuments/_doc +{ + "title": "New Document", + "content": "This is a sample document" +} +``` + +::: + +:::{tab-item} Markdown + +````markdown +```console +GET /mydocuments/_search +{ + "from": 1, + "query": { + "match_all" {} + } +} + +POST /mydocuments/_doc +{ + "title": "New Document", + "content": "This is a sample document" +} +``` +```` + +::: + +:::: + ### Code block substitutions You can use substitutions to insert reusable values into your code block examples. diff --git a/docs/testing/index.md b/docs/testing/index.md index d17fecb60..491ed74f6 100644 --- a/docs/testing/index.md +++ b/docs/testing/index.md @@ -31,3 +31,34 @@ The files in this directory are used for testing purposes. Do not edit these fil "key": "value" } ``` + +```console +PUT metricbeat-2016.05.30/_doc/1?refresh +{"system.cpu.idle.pct": 0.908} +PUT metricbeat-2016.05.31/_doc/1?refresh +{"system.cpu.idle.pct": 0.105} +``` + +```console +POST _reindex +{ + "max_docs": 10, + "source": { + "index": "my-index-000001", + "query": { + "function_score" : { + "random_score" : {}, + "min_score" : 0.9 + } + } + }, + "dest": { + "index": "my-new-index-000001" + } +} +``` + +```console +GET metricbeat-2016.05.30-1/_doc/1 +GET metricbeat-2016.05.31-1/_doc/1 +``` diff --git a/src/Elastic.Documentation.Site/Assets/markdown/code.css b/src/Elastic.Documentation.Site/Assets/markdown/code.css index 62652d91b..7fcea1a4f 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/code.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/code.css @@ -19,8 +19,16 @@ code:last-child { @apply rounded-b-sm; } - code.language-apiheader { - @apply border-b-grey-80 border-b-1; + code.language-apiheader + code.language-json { + @apply pt-0! -mt-3; + } + + code.language-json + code.language-apiheader { + @apply border-t-grey-90 border-t-1; + } + + code.language-apiheader + code.language-apiheader { + @apply border-t-grey-90 border-t-1; } } diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml b/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml index 5c826fbe8..114d1f975 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml +++ b/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml @@ -8,6 +8,22 @@ ΒΆ } -
@if (!string.IsNullOrEmpty(Model.ApiCallHeader)) { @Model.ApiCallHeader }@(Model.RenderBlock())
+
+		@if (Model.ApiSegments.Count > 0)
+		{
+			@foreach (var segment in Model.ApiSegments)
+			{
+				@segment.Header
+				@if (segment.ContentLines.Count > 0)
+				{
+					@string.Join("\n", segment.ContentLines)
+				}
+			}
+		}
+		else
+		{
+			@(Model.RenderBlock())
+		}
+		
diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs b/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs index d0393529c..03b10f325 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs @@ -9,7 +9,7 @@ namespace Elastic.Markdown.Myst.CodeBlocks; public class CodeViewModel { - public required string? ApiCallHeader { get; init; } + public required List ApiSegments { get; init; } public required string? Caption { get; init; } public required string Language { get; init; } public required string? CrossReferenceName { get; init; } diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs index 4abd653d7..72df611f4 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs @@ -10,6 +10,12 @@ namespace Elastic.Markdown.Myst.CodeBlocks; +public class ApiSegment +{ + public string Header { get; set; } = ""; + public List ContentLines { get; set; } = []; +} + public class EnhancedCodeBlock(BlockParser parser, ParserContext context) : FencedCodeBlock(parser), IBlockExtension { @@ -31,5 +37,5 @@ public class EnhancedCodeBlock(BlockParser parser, ParserContext context) public string? Caption { get; set; } - public string? ApiCallHeader { get; set; } + public List ApiSegments { get; set; } = []; } diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockHtmlRenderer.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockHtmlRenderer.cs index 731635ca4..0b6e893d5 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockHtmlRenderer.cs @@ -128,7 +128,7 @@ protected override void Write(HtmlRenderer renderer, EnhancedCodeBlock block) CrossReferenceName = string.Empty,// block.CrossReferenceName, Language = block.Language, Caption = block.Caption, - ApiCallHeader = block.ApiCallHeader, + ApiSegments = block.ApiSegments, EnhancedCodeBlock = block }); diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs index 8b34aca0f..673c63583 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs @@ -164,19 +164,19 @@ private static void ProcessCodeBlock( else codeBlockArgs = codeArgs; + // Process console blocks with multiple API segments + if (language == "console") + { + ProcessConsoleCodeBlock(lines, codeBlock, codeBlockArgs, context); + return; + } + var callOutIndex = 0; var originatingLine = 0; for (var index = 0; index < lines.Lines.Length; index++) { originatingLine++; var line = lines.Lines[index]; - if (index == 0 && language == "console") - { - codeBlock.ApiCallHeader = line.ToString(); - var s = new StringSlice(""); - lines.Lines[index] = new StringLine(ref s); - continue; - } var span = line.Slice.AsSpan(); if (codeBlockArgs.UseSubstitutions) @@ -200,58 +200,11 @@ private static void ProcessCodeBlock( continue; if (codeBlockArgs.UseCallouts) - { - List callOuts = []; - var hasClassicCallout = span.IndexOf("<") > 0 && span.LastIndexOf(">") == span.Length - 1; - if (hasClassicCallout) - { - var matchClassicCallout = CallOutParser.CallOutNumber().EnumerateMatches(span); - callOuts.AddRange( - EnumerateAnnotations(matchClassicCallout, ref span, ref callOutIndex, originatingLine, false) - ); - } - - // only support magic callouts for smaller line lengths - if (callOuts.Count == 0 && span.Length < 200) - { - var matchInline = CallOutParser.MathInlineAnnotation().EnumerateMatches(span); - callOuts.AddRange( - EnumerateAnnotations(matchInline, ref span, ref callOutIndex, originatingLine, true) - ); - } - - codeBlock.CallOuts.AddRange(callOuts); - } + ProcessCalloutsForLine(span, codeBlock, ref callOutIndex, originatingLine); } - //update string slices to ignore call outs - if (codeBlock.CallOuts.Count > 0) - { - var callouts = codeBlock.CallOuts.Aggregate(new Dictionary(), (acc, curr) => - { - if (acc.TryAdd(curr.Line, curr)) - return acc; - if (acc[curr.Line].SliceStart > curr.SliceStart) - acc[curr.Line] = curr; - return acc; - }); - - foreach (var callout in callouts.Values) - { - var line = lines.Lines[callout.Line - 1]; - var newSpan = line.Slice.AsSpan()[..callout.SliceStart]; - var s = new StringSlice(newSpan.ToString()); - lines.Lines[callout.Line - 1] = new StringLine(ref s); - } - } - - var inlineAnnotations = codeBlock.CallOuts.Count(c => c.InlineCodeAnnotation); - var classicAnnotations = codeBlock.CallOuts.Count - inlineAnnotations; - if (inlineAnnotations > 0 && classicAnnotations > 0) - codeBlock.EmitError("Both inline and classic callouts are not supported"); - - if (inlineAnnotations > 0) - codeBlock.InlineAnnotations = true; + ProcessCalloutPostProcessing(lines, codeBlock); + ProcessInlineAnnotations(codeBlock); } private static List EnumerateAnnotations(Regex.ValueMatchEnumerator matches, @@ -335,4 +288,149 @@ private static List ParseClassicCallOuts(ValueMatch match, ref ReadOnly return callOuts; } + + private static void ProcessConsoleCodeBlock( + StringLineGroup lines, + EnhancedCodeBlock codeBlock, + CodeBlockArguments codeBlockArgs, + ParserContext context) + { + var currentSegment = new ApiSegment(); + var callOutIndex = 0; + var originatingLine = 0; + + for (var index = 0; index < lines.Lines.Length; index++) + { + originatingLine++; + var line = lines.Lines[index]; + var lineText = line.ToString(); + var span = line.Slice.AsSpan(); + + // Apply substitutions if enabled + if (codeBlockArgs.UseSubstitutions) + { + if (span.ReplaceSubstitutions(context.YamlFrontMatter?.Properties, context.Build.Collector, out var frontMatterReplacement)) + { + var s = new StringSlice(frontMatterReplacement); + lines.Lines[index] = new StringLine(ref s); + span = lines.Lines[index].Slice.AsSpan(); + lineText = frontMatterReplacement; + } + + if (span.ReplaceSubstitutions(context.Substitutions, context.Build.Collector, out var globalReplacement)) + { + var s = new StringSlice(globalReplacement); + lines.Lines[index] = new StringLine(ref s); + span = lines.Lines[index].Slice.AsSpan(); + lineText = globalReplacement; + } + } + + // Check if this line is an HTTP verb (API call header) + if (IsHttpVerb(lineText)) + { + // If we have a current segment with content, save it + if (!string.IsNullOrEmpty(currentSegment.Header) || currentSegment.ContentLines.Count > 0) + codeBlock.ApiSegments.Add(currentSegment); + + // Start a new segment + currentSegment = new ApiSegment + { + Header = lineText + }; + + // Clear this line from the content since it's now a header + var s = new StringSlice(""); + lines.Lines[index] = new StringLine(ref s); + } + else + { + // This is content for the current segment + if (!string.IsNullOrEmpty(lineText.Trim())) + currentSegment.ContentLines.Add(lineText); + } + + // Process callouts if enabled + if (codeBlockArgs.UseCallouts && codeBlock.OpeningFencedCharCount <= 3) + ProcessCalloutsForLine(span, codeBlock, ref callOutIndex, originatingLine); + } + + // Add the last segment if it has content + if (!string.IsNullOrEmpty(currentSegment.Header) || currentSegment.ContentLines.Count > 0) + codeBlock.ApiSegments.Add(currentSegment); + + ProcessCalloutPostProcessing(lines, codeBlock); + ProcessInlineAnnotations(codeBlock); + } + + private static bool IsHttpVerb(string line) + { + var trimmed = line.Trim(); + return trimmed.StartsWith("GET ", StringComparison.OrdinalIgnoreCase) || + trimmed.StartsWith("POST ", StringComparison.OrdinalIgnoreCase) || + trimmed.StartsWith("PUT ", StringComparison.OrdinalIgnoreCase) || + trimmed.StartsWith("DELETE ", StringComparison.OrdinalIgnoreCase) || + trimmed.StartsWith("PATCH ", StringComparison.OrdinalIgnoreCase) || + trimmed.StartsWith("HEAD ", StringComparison.OrdinalIgnoreCase) || + trimmed.StartsWith("OPTIONS ", StringComparison.OrdinalIgnoreCase); + } + + private static void ProcessCalloutsForLine(ReadOnlySpan span, EnhancedCodeBlock codeBlock, ref int callOutIndex, int originatingLine) + { + List callOuts = []; + var hasClassicCallout = span.IndexOf("<") > 0 && span.LastIndexOf(">") == span.Length - 1; + if (hasClassicCallout) + { + var matchClassicCallout = CallOutParser.CallOutNumber().EnumerateMatches(span); + callOuts.AddRange( + EnumerateAnnotations(matchClassicCallout, ref span, ref callOutIndex, originatingLine, false) + ); + } + + // only support magic callouts for smaller line lengths + if (callOuts.Count == 0 && span.Length < 200) + { + var matchInline = CallOutParser.MathInlineAnnotation().EnumerateMatches(span); + callOuts.AddRange( + EnumerateAnnotations(matchInline, ref span, ref callOutIndex, originatingLine, true) + ); + } + + codeBlock.CallOuts.AddRange(callOuts); + } + + private static void ProcessCalloutPostProcessing(StringLineGroup lines, EnhancedCodeBlock codeBlock) + { + //update string slices to ignore call outs + if (codeBlock.CallOuts.Count > 0) + { + var callouts = codeBlock.CallOuts.Aggregate(new Dictionary(), (acc, curr) => + { + if (acc.TryAdd(curr.Line, curr)) + return acc; + if (acc[curr.Line].SliceStart > curr.SliceStart) + acc[curr.Line] = curr; + return acc; + }); + + foreach (var callout in callouts.Values) + { + var line = lines.Lines[callout.Line - 1]; + var newSpan = line.Slice.AsSpan()[..callout.SliceStart]; + var s = new StringSlice(newSpan.ToString()); + lines.Lines[callout.Line - 1] = new StringLine(ref s); + } + } + } + + private static void ProcessInlineAnnotations(EnhancedCodeBlock codeBlock) + { + var inlineAnnotations = codeBlock.CallOuts.Count(c => c.InlineCodeAnnotation); + var classicAnnotations = codeBlock.CallOuts.Count - inlineAnnotations; + if (inlineAnnotations > 0 && classicAnnotations > 0) + codeBlock.EmitError("Both inline and classic callouts are not supported"); + + if (inlineAnnotations > 0) + codeBlock.InlineAnnotations = true; + } } diff --git a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs index ef50735ab..773bf03aa 100644 --- a/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs +++ b/src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs @@ -133,3 +133,4 @@ private IEnumerable ProcessApplicabilityCollection( }); } + diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index 1e890eb6b..b17629f3e 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -283,7 +283,7 @@ private static void WriteLiteralIncludeBlock(HtmlRenderer renderer, IncludeBlock CrossReferenceName = null, Language = block.Language, Caption = null, - ApiCallHeader = null, + ApiSegments = [], RawIncludedFileContents = content }); RenderRazorSlice(slice, renderer); From 6a3abf6d2cc92ea1a1709eafb0dadf90b8ba0cf6 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Tue, 16 Sep 2025 22:02:38 +0200 Subject: [PATCH 2/9] Run prettier --- src/Elastic.Documentation.Site/Assets/markdown/code.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Elastic.Documentation.Site/Assets/markdown/code.css b/src/Elastic.Documentation.Site/Assets/markdown/code.css index 7fcea1a4f..340e16588 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/code.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/code.css @@ -20,13 +20,13 @@ @apply rounded-b-sm; } code.language-apiheader + code.language-json { - @apply pt-0! -mt-3; + @apply -mt-3 pt-0!; } - + code.language-json + code.language-apiheader { @apply border-t-grey-90 border-t-1; } - + code.language-apiheader + code.language-apiheader { @apply border-t-grey-90 border-t-1; } From 90aba8742c4c951cc29c6a06387c79787b3258c3 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Tue, 16 Sep 2025 22:23:09 +0200 Subject: [PATCH 3/9] Fix callouts on api headers --- docs/testing/index.md | 6 ++- .../Myst/CodeBlocks/Code.cshtml | 2 +- .../Myst/CodeBlocks/CodeViewModel.cs | 17 ++++++ .../Myst/CodeBlocks/EnhancedCodeBlock.cs | 1 + .../CodeBlocks/EnhancedCodeBlockParser.cs | 53 ++++++++++++++----- 5 files changed, 63 insertions(+), 16 deletions(-) diff --git a/docs/testing/index.md b/docs/testing/index.md index 491ed74f6..00d151241 100644 --- a/docs/testing/index.md +++ b/docs/testing/index.md @@ -33,11 +33,13 @@ The files in this directory are used for testing purposes. Do not edit these fil ``` ```console -PUT metricbeat-2016.05.30/_doc/1?refresh +PUT metricbeat-2016.05.30/_doc/1?refresh <1> {"system.cpu.idle.pct": 0.908} -PUT metricbeat-2016.05.31/_doc/1?refresh +PUT metricbeat-2016.05.31/_doc/1?refresh <2> {"system.cpu.idle.pct": 0.105} ``` +1. test 1 +2. test 2 ```console POST _reindex diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml b/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml index 114d1f975..828e13d41 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml +++ b/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml @@ -13,7 +13,7 @@ { @foreach (var segment in Model.ApiSegments) { - @segment.Header + @segment.Header@(Model.RenderConsoleCallouts(segment.LineNumber)) @if (segment.ContentLines.Count > 0) { @string.Join("\n", segment.ContentLines) diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs b/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs index 03b10f325..ba501411a 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs @@ -29,4 +29,21 @@ public HtmlString RenderBlock() DocumentationObjectPoolProvider.HtmlRendererPool.Return(subscription); return new HtmlString(result); } + + public HtmlString RenderConsoleCallouts(int lineNumber) + { + if (EnhancedCodeBlock?.CallOuts == null) + return HtmlString.Empty; + + var callouts = EnhancedCodeBlock.CallOuts.Where(c => c.Line == lineNumber); + if (!callouts.Any()) + return HtmlString.Empty; + + var html = new System.Text.StringBuilder(); + foreach (var callout in callouts) + { + _ = html.Append($""); + } + return new HtmlString(html.ToString()); + } } diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs index 72df611f4..8bbb5a3da 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs @@ -14,6 +14,7 @@ public class ApiSegment { public string Header { get; set; } = ""; public List ContentLines { get; set; } = []; + public int LineNumber { get; set; } } public class EnhancedCodeBlock(BlockParser parser, ParserContext context) diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs index 673c63583..75d4e84ca 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs @@ -329,14 +329,17 @@ private static void ProcessConsoleCodeBlock( // Check if this line is an HTTP verb (API call header) if (IsHttpVerb(lineText)) { - // If we have a current segment with content, save it if (!string.IsNullOrEmpty(currentSegment.Header) || currentSegment.ContentLines.Count > 0) codeBlock.ApiSegments.Add(currentSegment); - // Start a new segment + // Process callouts before creating the segment to capture them on the original line + if (codeBlockArgs.UseCallouts && codeBlock.OpeningFencedCharCount <= 3) + ProcessCalloutsForLine(span, codeBlock, ref callOutIndex, originatingLine); + currentSegment = new ApiSegment { - Header = lineText + Header = lineText, + LineNumber = originatingLine }; // Clear this line from the content since it's now a header @@ -345,14 +348,12 @@ private static void ProcessConsoleCodeBlock( } else { - // This is content for the current segment if (!string.IsNullOrEmpty(lineText.Trim())) currentSegment.ContentLines.Add(lineText); - } - // Process callouts if enabled - if (codeBlockArgs.UseCallouts && codeBlock.OpeningFencedCharCount <= 3) - ProcessCalloutsForLine(span, codeBlock, ref callOutIndex, originatingLine); + if (codeBlockArgs.UseCallouts && codeBlock.OpeningFencedCharCount <= 3) + ProcessCalloutsForLine(span, codeBlock, ref callOutIndex, originatingLine); + } } // Add the last segment if it has content @@ -413,12 +414,38 @@ private static void ProcessCalloutPostProcessing(StringLineGroup lines, Enhanced return acc; }); - foreach (var callout in callouts.Values) + // Console code blocks use ApiSegments for rendering, so we need to update headers directly + // Note: console language gets converted to "json" for syntax highlighting + if ((codeBlock.Language == "json" || codeBlock.Language == "console") && codeBlock.ApiSegments.Count > 0) { - var line = lines.Lines[callout.Line - 1]; - var newSpan = line.Slice.AsSpan()[..callout.SliceStart]; - var s = new StringSlice(newSpan.ToString()); - lines.Lines[callout.Line - 1] = new StringLine(ref s); + foreach (var callout in callouts.Values) + { + foreach (var segment in codeBlock.ApiSegments) + { + var calloutPattern = $"<{callout.Index}>"; + if (segment.Header.Contains(calloutPattern)) + { + segment.Header = segment.Header.Replace(calloutPattern, "").Trim(); + break; + } + } + } + } + else + { + foreach (var callout in callouts.Values) + { + var line = lines.Lines[callout.Line - 1]; + var span = line.Slice.AsSpan(); + + // Skip callouts on cleared lines to avoid ArgumentOutOfRangeException + if (span.Length == 0 || callout.SliceStart >= span.Length) + continue; + + var newSpan = span[..callout.SliceStart]; + var s = new StringSlice(newSpan.ToString()); + lines.Lines[callout.Line - 1] = new StringLine(ref s); + } } } } From 5489ab36565f7099bd27f00e98546d90d35903fb Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Tue, 16 Sep 2025 22:31:02 +0200 Subject: [PATCH 4/9] Fix callouts in console API payload --- docs/testing/index.md | 28 ++ .../Myst/CodeBlocks/Code.cshtml | 4 +- .../Myst/CodeBlocks/CodeViewModel.cs | 37 ++ .../Myst/CodeBlocks/EnhancedCodeBlock.cs | 1 + .../CodeBlocks/EnhancedCodeBlockParser.cs | 3 + .../CodeBlocks/ConsoleCodeBlockTests.cs | 347 ++++++++++++++++++ 6 files changed, 418 insertions(+), 2 deletions(-) create mode 100644 tests/Elastic.Markdown.Tests/CodeBlocks/ConsoleCodeBlockTests.cs diff --git a/docs/testing/index.md b/docs/testing/index.md index 00d151241..c69886d02 100644 --- a/docs/testing/index.md +++ b/docs/testing/index.md @@ -64,3 +64,31 @@ POST _reindex GET metricbeat-2016.05.30-1/_doc/1 GET metricbeat-2016.05.31-1/_doc/1 ``` + +```console +PUT my-index-000001 +{ + "mappings": { + "enabled": false <1> + } +} + +PUT my-index-000001/_doc/session_1 +{ + "user_id": "kimchy", + "session_data": { + "arbitrary_object": { + "some_array": [ "foo", "bar", { "baz": 2 } ] + } + }, + "last_updated": "2015-12-06T18:20:22" +} + +GET my-index-000001/_doc/session_1 <2> + +GET my-index-000001/_mapping <3> +``` + +1. The entire mapping is disabled. +2. The document can be retrieved. +3. Checking the mapping reveals that no fields have been added. diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml b/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml index 828e13d41..17c90fcac 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml +++ b/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml @@ -14,9 +14,9 @@ @foreach (var segment in Model.ApiSegments) { @segment.Header@(Model.RenderConsoleCallouts(segment.LineNumber)) - @if (segment.ContentLines.Count > 0) + @if (segment.ContentLinesWithNumbers.Count > 0) { - @string.Join("\n", segment.ContentLines) + @(Model.RenderContentLinesWithCallouts(segment.ContentLinesWithNumbers)) } } } diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs b/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs index ba501411a..975981afe 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs @@ -46,4 +46,41 @@ public HtmlString RenderConsoleCallouts(int lineNumber) } return new HtmlString(html.ToString()); } + + public HtmlString RenderContentLinesWithCallouts(List<(string Content, int LineNumber)> contentLinesWithNumbers) + { + if (EnhancedCodeBlock?.CallOuts == null || contentLinesWithNumbers.Count == 0) + return new HtmlString(string.Join("\n", contentLinesWithNumbers.Select(c => c.Content))); + + var html = new System.Text.StringBuilder(); + for (var i = 0; i < contentLinesWithNumbers.Count; i++) + { + var (content, lineNumber) = contentLinesWithNumbers[i]; + var line = content; + + // Find callouts for this line + var callouts = EnhancedCodeBlock.CallOuts.Where(c => c.Line == lineNumber); + if (callouts.Any()) + { + // Remove callout markers from the line + foreach (var callout in callouts) + { + var calloutPattern = $"<{callout.Index}>"; + line = line.Replace(calloutPattern, ""); + } + line = line.TrimEnd(); + } + + if (i > 0) + _ = html.Append('\n'); + _ = html.Append(line); + + // Add callout HTML after the line + foreach (var callout in callouts) + { + _ = html.Append($""); + } + } + return new HtmlString(html.ToString()); + } } diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs index 8bbb5a3da..ffc8aae5b 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlock.cs @@ -15,6 +15,7 @@ public class ApiSegment public string Header { get; set; } = ""; public List ContentLines { get; set; } = []; public int LineNumber { get; set; } + public List<(string Content, int LineNumber)> ContentLinesWithNumbers { get; set; } = []; } public class EnhancedCodeBlock(BlockParser parser, ParserContext context) diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs index 75d4e84ca..63e91b4ee 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/EnhancedCodeBlockParser.cs @@ -349,7 +349,10 @@ private static void ProcessConsoleCodeBlock( else { if (!string.IsNullOrEmpty(lineText.Trim())) + { currentSegment.ContentLines.Add(lineText); + currentSegment.ContentLinesWithNumbers.Add((lineText, originatingLine)); + } if (codeBlockArgs.UseCallouts && codeBlock.OpeningFencedCharCount <= 3) ProcessCalloutsForLine(span, codeBlock, ref callOutIndex, originatingLine); diff --git a/tests/Elastic.Markdown.Tests/CodeBlocks/ConsoleCodeBlockTests.cs b/tests/Elastic.Markdown.Tests/CodeBlocks/ConsoleCodeBlockTests.cs new file mode 100644 index 000000000..4aed3baa3 --- /dev/null +++ b/tests/Elastic.Markdown.Tests/CodeBlocks/ConsoleCodeBlockTests.cs @@ -0,0 +1,347 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using Elastic.Markdown.Myst.CodeBlocks; +using Elastic.Markdown.Tests.Inline; +using FluentAssertions; +using JetBrains.Annotations; + +namespace Elastic.Markdown.Tests.CodeBlocks; + +public abstract class ConsoleCodeBlockTests( + ITestOutputHelper output, + [LanguageInjection("markdown")] string markdown +) + : BlockTest(output, markdown) +{ + [Fact] + public void ParsesConsoleCodeBlock() => Block.Should().NotBeNull(); + + [Fact] + public void SetsLanguage() => Block!.Language.Should().Be("json"); +} + +public class SingleConsoleApiCallTests(ITestOutputHelper output) : ConsoleCodeBlockTests(output, +""" +```console +GET /mydocuments/_search +{ + "from": 1, + "query": { + "match_all" {} + } +} +``` +""" +) +{ + [Fact] + public void CreatesSingleApiSegment() + { + Block!.ApiSegments.Should().HaveCount(1); + var segment = Block.ApiSegments[0]; + segment.Header.Should().Be("GET /mydocuments/_search"); + segment.ContentLines.Should().HaveCount(6); + segment.ContentLines[0].Should().Be("{"); + segment.ContentLines[1].Should().Be(" \"from\": 1,"); + segment.ContentLines[2].Should().Be(" \"query\": {"); + segment.ContentLines[3].Should().Be(" \"match_all\" {}"); + segment.ContentLines[4].Should().Be(" }"); + segment.ContentLines[5].Should().Be("}"); + } + + [Fact] + public void HasNoErrors() => Collector.Diagnostics.Should().BeEmpty(); +} + +public class MultipleConsoleApiCallsTests(ITestOutputHelper output) : ConsoleCodeBlockTests(output, +""" +```console +GET /mydocuments/_search +{ + "from": 1, + "query": { + "match_all" {} + } +} + +POST /mydocuments/_doc +{ + "title": "New Document", + "content": "This is a sample document" +} +``` +""" +) +{ + [Fact] + public void CreatesMultipleApiSegments() + { + Block!.ApiSegments.Should().HaveCount(2); + + // First segment + var firstSegment = Block.ApiSegments[0]; + firstSegment.Header.Should().Be("GET /mydocuments/_search"); + firstSegment.ContentLines.Should().HaveCount(6); + firstSegment.ContentLines[0].Should().Be("{"); + firstSegment.ContentLines[1].Should().Be(" \"from\": 1,"); + firstSegment.ContentLines[2].Should().Be(" \"query\": {"); + firstSegment.ContentLines[3].Should().Be(" \"match_all\" {}"); + firstSegment.ContentLines[4].Should().Be(" }"); + firstSegment.ContentLines[5].Should().Be("}"); + + // Second segment + var secondSegment = Block.ApiSegments[1]; + secondSegment.Header.Should().Be("POST /mydocuments/_doc"); + secondSegment.ContentLines.Should().HaveCount(4); + secondSegment.ContentLines[0].Should().Be("{"); + secondSegment.ContentLines[1].Should().Be(" \"title\": \"New Document\","); + secondSegment.ContentLines[2].Should().Be(" \"content\": \"This is a sample document\""); + secondSegment.ContentLines[3].Should().Be("}"); + } + + [Fact] + public void HasNoErrors() => Collector.Diagnostics.Should().BeEmpty(); +} + +public class ConsoleWithDifferentHttpVerbsTests(ITestOutputHelper output) : ConsoleCodeBlockTests(output, +""" +```console +GET /api/users +{ + "size": 10 +} + +PUT /api/users/123 +{ + "name": "John Doe", + "email": "john@example.com" +} + +DELETE /api/users/123 +``` +""" +) +{ + [Fact] + public void HandlesDifferentHttpVerbs() + { + Block!.ApiSegments.Should().HaveCount(3); + + Block.ApiSegments[0].Header.Should().Be("GET /api/users"); + Block.ApiSegments[1].Header.Should().Be("PUT /api/users/123"); + Block.ApiSegments[2].Header.Should().Be("DELETE /api/users/123"); + } + + [Fact] + public void HasNoErrors() => Collector.Diagnostics.Should().BeEmpty(); +} + +public class ConsoleWithCalloutsTests(ITestOutputHelper output) : ConsoleCodeBlockTests(output, +""" +```console +GET /mydocuments/_search +{ + "from": 1, + "query": { + "match_all" {} <1> + } +} + +POST /mydocuments/_doc +{ + "title": "New Document" <2> +} +``` + +1. This query matches all documents +2. The document title +""" +) +{ + [Fact] + public void CreatesMultipleApiSegmentsWithCallouts() + { + Block!.ApiSegments.Should().HaveCount(2); + Block.CallOuts.Should().HaveCount(2); + } + + [Fact] + public void HasNoErrors() => Collector.Diagnostics.Should().BeEmpty(); +} + +public class ConsoleWithEmptyLinesTests(ITestOutputHelper output) : ConsoleCodeBlockTests(output, +""" +```console +GET /api/test +{ + "param": "value" +} + +POST /api/test +{ + "another": "value" +} +``` +""" +) +{ + [Fact] + public void HandlesEmptyLinesBetweenApiCalls() + { + Block!.ApiSegments.Should().HaveCount(2); + Block.ApiSegments[0].Header.Should().Be("GET /api/test"); + Block.ApiSegments[1].Header.Should().Be("POST /api/test"); + } + + [Fact] + public void HasNoErrors() => Collector.Diagnostics.Should().BeEmpty(); +} + +public class ConsoleWithOnlyHeadersTests(ITestOutputHelper output) : ConsoleCodeBlockTests(output, +""" +```console +GET /api/health +POST /api/status +DELETE /api/cleanup +``` +""" +) +{ + [Fact] + public void HandlesApiCallsWithoutBodies() + { + Block!.ApiSegments.Should().HaveCount(3); + Block.ApiSegments[0].Header.Should().Be("GET /api/health"); + Block.ApiSegments[1].Header.Should().Be("POST /api/status"); + Block.ApiSegments[2].Header.Should().Be("DELETE /api/cleanup"); + + Block.ApiSegments.Should().OnlyContain(s => s.ContentLines.Count == 0); + } + + [Fact] + public void HasNoErrors() => Collector.Diagnostics.Should().BeEmpty(); +} + +public class ConsoleWithCalloutsOnHttpVerbsTests(ITestOutputHelper output) : ConsoleCodeBlockTests(output, +""" +```console +GET /api/users <1> +{ + "size": 10 +} + +POST /api/users <2> +{ + "name": "John Doe" +} +``` + +1. Get all users +2. Create a new user +""" +) +{ + [Fact] + public void CreatesMultipleApiSegmentsWithCalloutsOnHttpVerbs() + { + Block!.ApiSegments.Should().HaveCount(2); + Block.CallOuts.Should().HaveCount(2); + } + + [Fact] + public void RendersCalloutsInHttpVerbHeaders() + { + Block!.ApiSegments.Should().HaveCount(2); + Block.ApiSegments[0].Header.Should().Be("GET /api/users"); + Block.ApiSegments[1].Header.Should().Be("POST /api/users"); + Block.CallOuts.Should().HaveCount(2); + } + + [Fact] + public void RendersCalloutHtmlInConsoleCodeBlocks() + { + var viewModel = new CodeViewModel + { + ApiSegments = Block!.ApiSegments, + Language = Block.Language, + Caption = null, + CrossReferenceName = null, + RawIncludedFileContents = null, + EnhancedCodeBlock = Block + }; + + var calloutHtml = viewModel.RenderConsoleCallouts(Block.ApiSegments[0].LineNumber); + calloutHtml.Value.Should().Contain("code-callout"); + calloutHtml.Value.Should().Contain("data-index=\"1\""); + } + + [Fact] + public void HasNoErrors() => Collector.Diagnostics.Should().BeEmpty(); +} + +public class ConsoleWithCalloutsInJsonContentTests(ITestOutputHelper output) : ConsoleCodeBlockTests(output, +""" +```console +PUT my-index-000001 +{ + "mappings": { + "enabled": false <1> + } +} + +PUT my-index-000001/_doc/session_1 +{ + "user_id": "kimchy", + "session_data": { + "arbitrary_object": { + "some_array": [ "foo", "bar", { "baz": 2 } ] + } + }, + "last_updated": "2015-12-06T18:20:22" +} + +GET my-index-000001/_doc/session_1 <2> + +GET my-index-000001/_mapping <3> +``` + +1. The entire mapping is disabled. +2. The document can be retrieved. +3. Checking the mapping reveals that no fields have been added. +""" +) +{ + [Fact] + public void CreatesMultipleApiSegmentsWithCalloutsInJsonContent() + { + Block!.ApiSegments.Should().HaveCount(4); + Block.CallOuts.Should().HaveCount(3); + } + + [Fact] + public void RendersCalloutsInJsonContent() + { + // Test that callouts in JSON content are properly rendered + var viewModel = new CodeViewModel + { + ApiSegments = Block!.ApiSegments, + Language = Block.Language, + Caption = null, + CrossReferenceName = null, + RawIncludedFileContents = null, + EnhancedCodeBlock = Block + }; + + // The first segment should have callouts in its JSON content + var firstSegment = Block.ApiSegments[0]; + var contentHtml = viewModel.RenderContentLinesWithCallouts(firstSegment.ContentLinesWithNumbers); + contentHtml.Value.Should().Contain("code-callout"); + contentHtml.Value.Should().Contain("data-index=\"1\""); + contentHtml.Value.Should().NotContain("<1>"); + } + + [Fact] + public void HasNoErrors() => Collector.Diagnostics.Should().BeEmpty(); +} From 9149434aae77254bf1a05b7513e8cc00f6ed3933 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Tue, 16 Sep 2025 22:35:34 +0200 Subject: [PATCH 5/9] Simplify --- .../Myst/CodeBlocks/Code.cshtml | 2 +- .../Myst/CodeBlocks/CodeViewModel.cs | 45 +++++++++---------- .../CodeBlocks/ConsoleCodeBlockTests.cs | 2 +- 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml b/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml index 17c90fcac..ddefcf943 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml +++ b/src/Elastic.Markdown/Myst/CodeBlocks/Code.cshtml @@ -13,7 +13,7 @@ { @foreach (var segment in Model.ApiSegments) { - @segment.Header@(Model.RenderConsoleCallouts(segment.LineNumber)) + @(Model.RenderLineWithCallouts(segment.Header, segment.LineNumber)) @if (segment.ContentLinesWithNumbers.Count > 0) { @(Model.RenderContentLinesWithCallouts(segment.ContentLinesWithNumbers)) diff --git a/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs b/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs index 975981afe..44323e3f5 100644 --- a/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs +++ b/src/Elastic.Markdown/Myst/CodeBlocks/CodeViewModel.cs @@ -30,56 +30,51 @@ public HtmlString RenderBlock() return new HtmlString(result); } - public HtmlString RenderConsoleCallouts(int lineNumber) + public HtmlString RenderLineWithCallouts(string content, int lineNumber) { if (EnhancedCodeBlock?.CallOuts == null) - return HtmlString.Empty; + return new HtmlString(content); var callouts = EnhancedCodeBlock.CallOuts.Where(c => c.Line == lineNumber); if (!callouts.Any()) - return HtmlString.Empty; + return new HtmlString(content); + var line = content; var html = new System.Text.StringBuilder(); + + // Remove callout markers from the line + foreach (var callout in callouts) + { + var calloutPattern = $"<{callout.Index}>"; + line = line.Replace(calloutPattern, ""); + } + line = line.TrimEnd(); + + _ = html.Append(line); + + // Add callout HTML after the line foreach (var callout in callouts) { _ = html.Append($""); } + return new HtmlString(html.ToString()); } public HtmlString RenderContentLinesWithCallouts(List<(string Content, int LineNumber)> contentLinesWithNumbers) { - if (EnhancedCodeBlock?.CallOuts == null || contentLinesWithNumbers.Count == 0) - return new HtmlString(string.Join("\n", contentLinesWithNumbers.Select(c => c.Content))); + if (contentLinesWithNumbers.Count == 0) + return HtmlString.Empty; var html = new System.Text.StringBuilder(); for (var i = 0; i < contentLinesWithNumbers.Count; i++) { var (content, lineNumber) = contentLinesWithNumbers[i]; - var line = content; - - // Find callouts for this line - var callouts = EnhancedCodeBlock.CallOuts.Where(c => c.Line == lineNumber); - if (callouts.Any()) - { - // Remove callout markers from the line - foreach (var callout in callouts) - { - var calloutPattern = $"<{callout.Index}>"; - line = line.Replace(calloutPattern, ""); - } - line = line.TrimEnd(); - } if (i > 0) _ = html.Append('\n'); - _ = html.Append(line); - // Add callout HTML after the line - foreach (var callout in callouts) - { - _ = html.Append($""); - } + _ = html.Append(RenderLineWithCallouts(content, lineNumber)); } return new HtmlString(html.ToString()); } diff --git a/tests/Elastic.Markdown.Tests/CodeBlocks/ConsoleCodeBlockTests.cs b/tests/Elastic.Markdown.Tests/CodeBlocks/ConsoleCodeBlockTests.cs index 4aed3baa3..feaa3cc51 100644 --- a/tests/Elastic.Markdown.Tests/CodeBlocks/ConsoleCodeBlockTests.cs +++ b/tests/Elastic.Markdown.Tests/CodeBlocks/ConsoleCodeBlockTests.cs @@ -272,7 +272,7 @@ public void RendersCalloutHtmlInConsoleCodeBlocks() EnhancedCodeBlock = Block }; - var calloutHtml = viewModel.RenderConsoleCallouts(Block.ApiSegments[0].LineNumber); + var calloutHtml = viewModel.RenderLineWithCallouts(Block.ApiSegments[0].Header, Block.ApiSegments[0].LineNumber); calloutHtml.Value.Should().Contain("code-callout"); calloutHtml.Value.Should().Contain("data-index=\"1\""); } From 17f8bacb870d557439669447c3980636113a24a5 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Wed, 17 Sep 2025 10:27:04 +0200 Subject: [PATCH 6/9] Darker separators --- NOTICE.txt | 2 -- docs/testing/index.md | 6 ++++++ src/Elastic.Documentation.Site/Assets/markdown/code.css | 6 +++--- .../CodeBlocks/ConsoleCodeBlockTests.cs | 1 + 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/NOTICE.txt b/NOTICE.txt index 729c8f46d..9547245cf 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -555,5 +555,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - diff --git a/docs/testing/index.md b/docs/testing/index.md index c69886d02..0319fce2d 100644 --- a/docs/testing/index.md +++ b/docs/testing/index.md @@ -92,3 +92,9 @@ GET my-index-000001/_mapping <3> 1. The entire mapping is disabled. 2. The document can be retrieved. 3. Checking the mapping reveals that no fields have been added. + +```javascript +const foo = "bar"; <1> +``` + +1. This is a JavaScript code block. diff --git a/src/Elastic.Documentation.Site/Assets/markdown/code.css b/src/Elastic.Documentation.Site/Assets/markdown/code.css index 340e16588..32630246d 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/code.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/code.css @@ -24,11 +24,11 @@ } code.language-json + code.language-apiheader { - @apply border-t-grey-90 border-t-1; + @apply border-dotted border-t-grey-100 border-t-1; } - + code.language-apiheader + code.language-apiheader { - @apply border-t-grey-90 border-t-1; + @apply border-dotted border-t-grey-100 border-t-1; } } diff --git a/tests/Elastic.Markdown.Tests/CodeBlocks/ConsoleCodeBlockTests.cs b/tests/Elastic.Markdown.Tests/CodeBlocks/ConsoleCodeBlockTests.cs index feaa3cc51..5b9c122b3 100644 --- a/tests/Elastic.Markdown.Tests/CodeBlocks/ConsoleCodeBlockTests.cs +++ b/tests/Elastic.Markdown.Tests/CodeBlocks/ConsoleCodeBlockTests.cs @@ -345,3 +345,4 @@ public void RendersCalloutsInJsonContent() [Fact] public void HasNoErrors() => Collector.Diagnostics.Should().BeEmpty(); } + From 1c1ff014a903700bd0f861adddac69d0b7fc8eee Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Wed, 17 Sep 2025 10:36:34 +0200 Subject: [PATCH 7/9] Run prettier --- src/Elastic.Documentation.Site/Assets/markdown/code.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Elastic.Documentation.Site/Assets/markdown/code.css b/src/Elastic.Documentation.Site/Assets/markdown/code.css index 32630246d..221751b08 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/code.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/code.css @@ -24,11 +24,11 @@ } code.language-json + code.language-apiheader { - @apply border-dotted border-t-grey-100 border-t-1; + @apply border-t-grey-100 border-t-1 border-dotted; } - + code.language-apiheader + code.language-apiheader { - @apply border-dotted border-t-grey-100 border-t-1; + @apply border-t-grey-100 border-t-1 border-dotted; } } From 6a17f255bcab226acdadaccd64ec6e7f02d1ad97 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Wed, 17 Sep 2025 11:03:52 +0200 Subject: [PATCH 8/9] Refactor css --- src/Elastic.Documentation.Site/Assets/markdown/code.css | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Elastic.Documentation.Site/Assets/markdown/code.css b/src/Elastic.Documentation.Site/Assets/markdown/code.css index 221751b08..29c7e74fd 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/code.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/code.css @@ -23,10 +23,7 @@ @apply -mt-3 pt-0!; } - code.language-json + code.language-apiheader { - @apply border-t-grey-100 border-t-1 border-dotted; - } - + code.language-json + code.language-apiheader, code.language-apiheader + code.language-apiheader { @apply border-t-grey-100 border-t-1 border-dotted; } From 605764f00a9d3ff4fe59a8845ea61d024f05e40e Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Wed, 17 Sep 2025 11:21:41 +0200 Subject: [PATCH 9/9] Less space between api header and payload --- src/Elastic.Documentation.Site/Assets/markdown/code.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elastic.Documentation.Site/Assets/markdown/code.css b/src/Elastic.Documentation.Site/Assets/markdown/code.css index 29c7e74fd..0871aa1b7 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/code.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/code.css @@ -20,7 +20,7 @@ @apply rounded-b-sm; } code.language-apiheader + code.language-json { - @apply -mt-3 pt-0!; + @apply -mt-6 pt-0!; } code.language-json + code.language-apiheader,