Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ private static void ProcessCrossLink(LinkInline link, InlineProcessor processor,
if (url != null)
context.Build.Collector.EmitCrossLink(url);

// Store the original cross-link URL for LLM rendering
link.SetData("originalCrossLinkUrl", uri.ToString());

if (context.CrossLinkResolver.TryResolve(
s => processor.EmitError(link, s),
uri, out var resolvedUri)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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.IO;
using Elastic.Markdown.Myst.InlineParsers.Substitution;
using Elastic.Markdown.Myst.Roles;
using Elastic.Markdown.Myst.Roles.Kbd;
Expand Down Expand Up @@ -32,8 +33,32 @@ protected override void Write(LlmMarkdownRenderer renderer, LinkInline obj)
renderer.WriteChildren(obj);
renderer.Writer.Write("](");
var url = obj.GetDynamicUrl?.Invoke() ?? obj.Url;
var absoluteUrl = LlmRenderingHelpers.MakeAbsoluteUrl(renderer, url);
renderer.Writer.Write(absoluteUrl ?? string.Empty);

// Check if this is an internal link to a markdown page
var isCrossLink = (obj.GetData("isCrossLink") as bool?) == true;
var hasTargetNavigationRoot = obj.GetData($"Target{nameof(MarkdownFile.NavigationRoot)}") != null;
var originalCrossLinkUrl = obj.GetData("originalCrossLinkUrl") as string;
var isInternalMarkdownLink = !isCrossLink && hasTargetNavigationRoot;
var isCrossLinkToMarkdown = isCrossLink && originalCrossLinkUrl is not null && IsCrossLinkToMarkdown(originalCrossLinkUrl);

if (isInternalMarkdownLink)
{
// For internal markdown links, preserve the .md extension
renderer.Writer.Write(EnsureMarkdownExtension(url) ?? string.Empty);
}
else if (isCrossLinkToMarkdown)
{
// For cross-links to markdown files, use absolute URL with .md extension
var absoluteUrl = LlmRenderingHelpers.MakeAbsoluteUrl(renderer, url);
var urlWithMdExtension = EnsureMarkdownExtension(absoluteUrl);
renderer.Writer.Write(urlWithMdExtension ?? string.Empty);
}
else
{
// For external links and non-markdown cross-links, make absolute
var absoluteUrl = LlmRenderingHelpers.MakeAbsoluteUrl(renderer, url);
renderer.Writer.Write(absoluteUrl ?? string.Empty);
}
}
if (!string.IsNullOrEmpty(obj.Title))
{
Expand All @@ -43,6 +68,43 @@ protected override void Write(LlmMarkdownRenderer renderer, LinkInline obj)
}
renderer.Writer.Write(")");
}

/// <summary>
/// Ensures the URL ends with .md extension for markdown links
/// </summary>
private static string? EnsureMarkdownExtension(string? url)
{
if (string.IsNullOrEmpty(url))
return url;

// If it already has .md extension, return as-is
if (url.EndsWith(".md", StringComparison.OrdinalIgnoreCase))
return url;

// Convert absolute paths to relative paths for markdown links
var processedUrl = url.StartsWith('/') ? url.TrimStart('/') : url;

// Add .md extension to internal markdown links
return processedUrl + ".md";
}

/// <summary>
/// Checks if a cross-link URL points to a markdown file
/// </summary>
private static bool IsCrossLinkToMarkdown(string originalCrossLinkUrl)
{
if (string.IsNullOrEmpty(originalCrossLinkUrl))
return false;

// Parse the cross-link URI to extract the path
if (Uri.TryCreate(originalCrossLinkUrl, UriKind.Absolute, out var uri))
{
var path = uri.AbsolutePath;
return path.EndsWith(".md", StringComparison.OrdinalIgnoreCase);
}

return false;
}
}

public class LlmEmphasisInlineRenderer : MarkdownObjectRenderer<LlmMarkdownRenderer, EmphasisInline>
Expand Down
24 changes: 24 additions & 0 deletions tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
Original file line number Diff line number Diff line change
Expand Up @@ -538,3 +538,27 @@ sub:
markdown |> convertsToNewLLM """
## Hello, World!
"""

type ``links`` () =
static let generator = Setup.Generate [
Index """
This is a [link to another page](another-page.md).

This is an [external link](https://example.com).

This is a [cross-link](docs-content:/solutions/observability/apps/apm-server-binary.md).
"""
Markdown "another-page.md" """
# Another Page

This is another page for testing internal links.
"""
]

[<Fact>]
let ``internal markdown links preserve .md extension while external links become absolute`` () =
generator |> convertsToNewLLM """
This is a [link to another page](another-page.md).
This is an [external link](https://example.com).
This is a [cross-link](https://docs-v3-preview.elastic.dev/elastic/docs-content/tree/main/solutions/observability/apps/apm-server-binary.md).
"""
Loading