Add canonical URL preservation for embed assets in HTML and JSON exports

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-12-31 16:33:42 +00:00
parent a4f6aa165a
commit 088236c539
2 changed files with 43 additions and 8 deletions

View file

@ -138,6 +138,12 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
cancellationToken cancellationToken
) )
); );
// Include canonical URL if a proxy URL is being used
if (!string.IsNullOrWhiteSpace(embedAuthor.IconProxyUrl))
{
_writer.WriteString("iconCanonicalUrl", embedAuthor.IconUrl);
}
} }
_writer.WriteEndObject(); _writer.WriteEndObject();
@ -160,6 +166,12 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
cancellationToken cancellationToken
) )
); );
// Include canonical URL if a proxy URL is being used
if (!string.IsNullOrWhiteSpace(embedImage.ProxyUrl))
{
_writer.WriteString("canonicalUrl", embedImage.Url);
}
} }
_writer.WriteNumber("width", embedImage.Width); _writer.WriteNumber("width", embedImage.Width);
@ -185,6 +197,12 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
cancellationToken cancellationToken
) )
); );
// Include canonical URL if a proxy URL is being used
if (!string.IsNullOrWhiteSpace(embedVideo.ProxyUrl))
{
_writer.WriteString("canonicalUrl", embedVideo.Url);
}
} }
_writer.WriteNumber("width", embedVideo.Width); _writer.WriteNumber("width", embedVideo.Width);
@ -212,6 +230,12 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
cancellationToken cancellationToken
) )
); );
// Include canonical URL if a proxy URL is being used
if (!string.IsNullOrWhiteSpace(embedFooter.IconProxyUrl))
{
_writer.WriteString("iconCanonicalUrl", embedFooter.IconUrl);
}
} }
_writer.WriteEndObject(); _writer.WriteEndObject();

View file

@ -388,7 +388,7 @@
<div class="chatlog__embed-author-container"> <div class="chatlog__embed-author-container">
@if (!string.IsNullOrWhiteSpace(embed.Author.IconUrl)) @if (!string.IsNullOrWhiteSpace(embed.Author.IconUrl))
{ {
<img class="chatlog__embed-author-icon" src="@await ResolveAssetUrlAsync(embed.Author.IconProxyUrl ?? embed.Author.IconUrl)" alt="Author icon" loading="lazy" onerror="this.style.visibility='hidden'"> <img class="chatlog__embed-author-icon" src="@await ResolveAssetUrlAsync(embed.Author.IconProxyUrl ?? embed.Author.IconUrl)" alt="Author icon" loading="lazy" onerror="this.style.visibility='hidden'" @(embed.Author.IconProxyUrl is not null && embed.Author.IconUrl is not null ? $"data-canonical-url=\"{embed.Author.IconUrl}\"" : null)>
} }
@if (!string.IsNullOrWhiteSpace(embed.Author.Name)) @if (!string.IsNullOrWhiteSpace(embed.Author.Name))
@ -441,9 +441,14 @@
embed.Thumbnail?.ProxyUrl ?? embed.Thumbnail?.Url ?? embed.Thumbnail?.ProxyUrl ?? embed.Thumbnail?.Url ??
embed.Url; embed.Url;
var embedImageCanonicalUrl =
(embed.Image?.ProxyUrl is not null && embed.Image?.Url is not null) ? embed.Image?.Url :
(embed.Thumbnail?.ProxyUrl is not null && embed.Thumbnail?.Url is not null) ? embed.Thumbnail?.Url :
null;
<div class="chatlog__embed"> <div class="chatlog__embed">
<a href="@await ResolveAssetUrlAsync(embedImageUrl)"> <a href="@await ResolveAssetUrlAsync(embedImageUrl)">
<img class="chatlog__embed-generic-image" src="@await ResolveAssetUrlAsync(embedImageUrl)" alt="Embedded image" loading="lazy"> <img class="chatlog__embed-generic-image" src="@await ResolveAssetUrlAsync(embedImageUrl)" alt="Embedded image" loading="lazy" @(embedImageCanonicalUrl is not null ? $"data-canonical-url=\"{embedImageCanonicalUrl}\"" : null)>
</a> </a>
</div> </div>
} }
@ -457,8 +462,11 @@
embed.Video?.ProxyUrl ?? embed.Video?.Url ?? embed.Video?.ProxyUrl ?? embed.Video?.Url ??
embed.Url; embed.Url;
var embedVideoCanonicalUrl =
embed.Video?.ProxyUrl is not null && embed.Video?.Url is not null ? embed.Video?.Url : null;
<div class="chatlog__embed"> <div class="chatlog__embed">
<video class="chatlog__embed-generic-video" width="@embed.Video?.Width" height="@embed.Video?.Height" controls> <video class="chatlog__embed-generic-video" width="@embed.Video?.Width" height="@embed.Video?.Height" controls @(embedVideoCanonicalUrl is not null ? $"data-canonical-url=\"{embedVideoCanonicalUrl}\"" : null)>
<source src="@await ResolveAssetUrlAsync(embedVideoUrl)" alt="Embedded video"> <source src="@await ResolveAssetUrlAsync(embedVideoUrl)" alt="Embedded video">
</video> </video>
</div> </div>
@ -470,8 +478,11 @@
embed.Video?.ProxyUrl ?? embed.Video?.Url ?? embed.Video?.ProxyUrl ?? embed.Video?.Url ??
embed.Url; embed.Url;
var embedVideoCanonicalUrl =
embed.Video?.ProxyUrl is not null && embed.Video?.Url is not null ? embed.Video?.Url : null;
<div class="chatlog__embed"> <div class="chatlog__embed">
<video class="chatlog__embed-generic-gifv" width="@embed.Video?.Width" height="@embed.Video?.Height" loop onmouseover="this.play()" onmouseout="this.pause()"> <video class="chatlog__embed-generic-gifv" width="@embed.Video?.Width" height="@embed.Video?.Height" loop onmouseover="this.play()" onmouseout="this.pause()" @(embedVideoCanonicalUrl is not null ? $"data-canonical-url=\"{embedVideoCanonicalUrl}\"" : null)>
<source src="@await ResolveAssetUrlAsync(embedVideoUrl)" alt="Embedded gifv"> <source src="@await ResolveAssetUrlAsync(embedVideoUrl)" alt="Embedded gifv">
</video> </video>
</div> </div>
@ -499,7 +510,7 @@
<div class="chatlog__embed-author-container"> <div class="chatlog__embed-author-container">
@if (!string.IsNullOrWhiteSpace(embed.Author.IconUrl)) @if (!string.IsNullOrWhiteSpace(embed.Author.IconUrl))
{ {
<img class="chatlog__embed-author-icon" src="@await ResolveAssetUrlAsync(embed.Author.IconProxyUrl ?? embed.Author.IconUrl)" alt="Author icon" loading="lazy" onerror="this.style.visibility='hidden'"> <img class="chatlog__embed-author-icon" src="@await ResolveAssetUrlAsync(embed.Author.IconProxyUrl ?? embed.Author.IconUrl)" alt="Author icon" loading="lazy" onerror="this.style.visibility='hidden'" @(embed.Author.IconProxyUrl is not null && embed.Author.IconUrl is not null ? $"data-canonical-url=\"{embed.Author.IconUrl}\"" : null)>
} }
@if (!string.IsNullOrWhiteSpace(embed.Author.Name)) @if (!string.IsNullOrWhiteSpace(embed.Author.Name))
@ -574,7 +585,7 @@
{ {
<div class="chatlog__embed-thumbnail-container"> <div class="chatlog__embed-thumbnail-container">
<a class="chatlog__embed-thumbnail-link" href="@await ResolveAssetUrlAsync(embed.Thumbnail.ProxyUrl ?? embed.Thumbnail.Url)"> <a class="chatlog__embed-thumbnail-link" href="@await ResolveAssetUrlAsync(embed.Thumbnail.ProxyUrl ?? embed.Thumbnail.Url)">
<img class="chatlog__embed-thumbnail" src="@await ResolveAssetUrlAsync(embed.Thumbnail.ProxyUrl ?? embed.Thumbnail.Url)" alt="Thumbnail" loading="lazy"> <img class="chatlog__embed-thumbnail" src="@await ResolveAssetUrlAsync(embed.Thumbnail.ProxyUrl ?? embed.Thumbnail.Url)" alt="Thumbnail" loading="lazy" @(embed.Thumbnail.ProxyUrl is not null && embed.Thumbnail.Url is not null ? $"data-canonical-url=\"{embed.Thumbnail.Url}\"" : null)>
</a> </a>
</div> </div>
} }
@ -590,7 +601,7 @@
{ {
<div class="chatlog__embed-image-container"> <div class="chatlog__embed-image-container">
<a class="chatlog__embed-image-link" href="@await ResolveAssetUrlAsync(image.ProxyUrl ?? image.Url)"> <a class="chatlog__embed-image-link" href="@await ResolveAssetUrlAsync(image.ProxyUrl ?? image.Url)">
<img class="chatlog__embed-image" src="@await ResolveAssetUrlAsync(image.ProxyUrl ?? image.Url)" alt="Image" loading="lazy"> <img class="chatlog__embed-image" src="@await ResolveAssetUrlAsync(image.ProxyUrl ?? image.Url)" alt="Image" loading="lazy" @(image.ProxyUrl is not null && image.Url is not null ? $"data-canonical-url=\"{image.Url}\"" : null)>
</a> </a>
</div> </div>
} }
@ -605,7 +616,7 @@
@* Footer icon *@ @* Footer icon *@
@if (!string.IsNullOrWhiteSpace(embed.Footer?.IconUrl)) @if (!string.IsNullOrWhiteSpace(embed.Footer?.IconUrl))
{ {
<img class="chatlog__embed-footer-icon" src="@await ResolveAssetUrlAsync(embed.Footer.IconProxyUrl ?? embed.Footer.IconUrl)" alt="Footer icon" loading="lazy"> <img class="chatlog__embed-footer-icon" src="@await ResolveAssetUrlAsync(embed.Footer.IconProxyUrl ?? embed.Footer.IconUrl)" alt="Footer icon" loading="lazy" @(embed.Footer.IconProxyUrl is not null && embed.Footer.IconUrl is not null ? $"data-canonical-url=\"{embed.Footer.IconUrl}\"" : null)>
} }
<span class="chatlog__embed-footer-text"> <span class="chatlog__embed-footer-text">