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
2 changes: 2 additions & 0 deletions src/Elastic.Markdown/Assets/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// @ts-nocheck
import "htmx.org"
import "htmx-ext-preload"
import "htmx-ext-head-support"

import {initTocNav} from "./toc-nav";
import {initHighlight} from "./hljs";
import {initTabs} from "./tabs";
Expand Down
2 changes: 1 addition & 1 deletion src/Elastic.Markdown/DocumentationGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public DocumentationGenerator(
DocumentationSet = docSet;
Context = docSet.Build;
Resolver = docSet.LinkResolver;
HtmlWriter = new HtmlWriter(DocumentationSet, _writeFileSystem);
HtmlWriter = new HtmlWriter(DocumentationSet, _writeFileSystem, new DescriptionGenerator());
_documentationFileExporter =
documentationExporter
?? new DocumentationFileExporter(docSet.Build.ReadFileSystem, _writeFileSystem, HtmlWriter, conversionCollector);
Expand Down
3 changes: 3 additions & 0 deletions src/Elastic.Markdown/Myst/FrontMatter/FrontMatterParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public class YamlFrontMatter
[YamlMember(Alias = "title")]
public string? Title { get; set; }

[YamlMember(Alias = "description")]
public string? Description { get; set; }

[YamlMember(Alias = "navigation_title")]
public string? NavigationTitle { get; set; }

Expand Down
124 changes: 124 additions & 0 deletions src/Elastic.Markdown/Slices/DescriptionGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// 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 System.Text;
using Elastic.Markdown.Myst.Substitution;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;

namespace Elastic.Markdown.Slices;

public interface IDescriptionGenerator
{
string GenerateDescription(MarkdownDocument document);
}


public class DescriptionGenerator : IDescriptionGenerator
{
private const int MaxLength = 150;

public string GenerateDescription(MarkdownDocument document)
{
var description = new StringBuilder();
foreach (var block in document.TakeWhile(_ => description.Length < MaxLength))
{
// TODO: Add support for IncludeBlock
// This is needed when the first block is an IncludeBlock.
switch (block)
{
case ParagraphBlock paragraph:
{
ProcessParagraph(paragraph, description);
break;
}
case ListBlock listBlock:
{
ProcessListBlock(listBlock, description);
break;
}
}
}

var result = description.ToString();
// It can happen that the last parsed block is longer, hence the result is longer than maxLength
// Hence we need to shorten it. In this case it will be shorted to until the next space after `MaxLength`
if (result.Length > MaxLength)
{
var endIndex = result.IndexOf(' ', MaxLength - 1);
if (endIndex == -1)
endIndex = MaxLength;
result = string.Concat(result.AsSpan(0, endIndex + 1).Trim().TrimEnd('.'), "...");
}

return result;
}

private static void ProcessParagraph(ParagraphBlock paragraph, StringBuilder description)
{
if (paragraph.Inline == null)
return;

var paragraphText = GetInlineText(paragraph.Inline);
if (string.IsNullOrEmpty(paragraphText))
return;

_ = description.Append(paragraphText);
_ = description.Append(' ');
}

private static void ProcessListBlock(ListBlock listBlock, StringBuilder description)
{
foreach (var item in listBlock)
{
if (item is not ListItemBlock listItem)
continue;

foreach (var listItemBlock in listItem)
{
if (listItemBlock is not ParagraphBlock listItemParagraph || listItemParagraph.Inline == null)
continue;

var paragraphText = GetInlineText(listItemParagraph.Inline);
_ = description.Append(paragraphText);
var lastChar = paragraphText[^1];
if (lastChar is not '.' and not ',' and not '!' and not '?')
_ = description.Append(listItem == listBlock.LastChild ? ". " : ", ");
}
}
}

private static string GetInlineText(ContainerInline inline)
{
var builder = new StringBuilder();
foreach (var item in inline)
{
switch (item)
{
case SubstitutionLeaf subs:
_ = builder.Append(subs.Replacement);
break;
case LiteralInline literal:
_ = builder.Append(literal.Content.ToString());
break;
case EmphasisInline emphasis:
_ = builder.Append(GetInlineText(emphasis));
break;
case LinkInline link:
_ = builder.Append(GetInlineText(link));
break;
case CodeInline code:
_ = builder.Append(code.Content);
break;
case LineBreakInline:
_ = builder.Append(' ');
break;
case ContainerInline container:
_ = builder.Append(GetInlineText(container));
break;
}
}
return builder.ToString();
}
}
3 changes: 2 additions & 1 deletion src/Elastic.Markdown/Slices/HtmlWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private NavigationViewModel CreateNavigationModel(INavigation navigation)
}
}

public class HtmlWriter(DocumentationSet documentationSet, IFileSystem writeFileSystem, INavigationHtmlWriter? navigationHtmlWriter = null)
public class HtmlWriter(DocumentationSet documentationSet, IFileSystem writeFileSystem, IDescriptionGenerator descriptionGenerator, INavigationHtmlWriter? navigationHtmlWriter = null)
{
private DocumentationSet DocumentationSet { get; } = documentationSet;
public INavigationHtmlWriter NavigationHtmlWriter { get; } = navigationHtmlWriter ?? new IsolatedBuildNavigationHtmlWriter(documentationSet);
Expand Down Expand Up @@ -93,6 +93,7 @@ private async Task<string> RenderLayout(MarkdownFile markdown, MarkdownDocument
var slice = Index.Create(new IndexViewModel
{
Title = markdown.Title ?? "[TITLE NOT SET]",
Description = markdown.YamlFrontMatter?.Description ?? descriptionGenerator.GenerateDescription(document),
TitleRaw = markdown.TitleRaw ?? "[TITLE NOT SET]",
MarkdownHtml = html,
PageTocItems = [.. markdown.PageTableOfContent.Values],
Expand Down
1 change: 1 addition & 0 deletions src/Elastic.Markdown/Slices/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
public LayoutViewModel LayoutModel => new()
{
Title = $"Elastic Documentation: {Model.Title}",
Description = Model.Description,
PageTocItems = Model.PageTocItems.Where(i => i is { Level: 2 or 3 }).ToList(),
CurrentDocument = Model.CurrentDocument,
Previous = Model.PreviousDocument,
Expand Down
6 changes: 3 additions & 3 deletions src/Elastic.Markdown/Slices/Layout/_Head.cshtml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
@inherits RazorSlice<LayoutViewModel>
@using Elastic.Markdown.Helpers
<head>
<title>@Model.Title</title>
<meta name="description" content="@Model.Description">
@foreach (var fontFile in await FontPreloader.GetFontUrisAsync(@Model.UrlPathPrefix))
{
<link rel="preload" href="@fontFile" as="font" type="font/woff2" crossorigin>
}
<link rel="stylesheet preload" as="style" type="text/css" href="@Model.Static("styles.css")" crossorigin/>
<title>@Model.Title</title>

<link rel="stylesheet preload" as="style" type="text/css" href="@Model.Static("styles.css")" crossorigin/>
@if (Model.CanonicalBaseUrl is not null)
{
<link rel="canonical" href="@(new Uri(Model.CanonicalBaseUrl, Model.CurrentDocument.Url).ToString())">
Expand Down
2 changes: 2 additions & 0 deletions src/Elastic.Markdown/Slices/_ViewModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace Elastic.Markdown.Slices;
public class IndexViewModel
{
public required string Title { get; init; }
public required string Description { get; init; }
public required string TitleRaw { get; init; }
public required string MarkdownHtml { get; init; }
public required DocumentationGroup Tree { get; init; }
Expand All @@ -36,6 +37,7 @@ public class LayoutViewModel
/// the guids that no longer exist
public static string CurrentNavigationId { get; } = Guid.NewGuid().ToString("N")[..8];
public string Title { get; set; } = "Elastic Documentation";
public required string Description { get; init; }
public required IReadOnlyCollection<PageTocItem> PageTocItems { get; init; }
public required MarkdownFile CurrentDocument { get; init; }
public required MarkdownFile? Previous { get; init; }
Expand Down
Loading