feat: styles for essays.

This commit is contained in:
jackfiled 2025-01-24 16:29:32 +08:00
parent 24fb498d59
commit f071b02ae3
12 changed files with 129 additions and 91 deletions

View File

@ -18,9 +18,11 @@
文章 文章
</div> </div>
<a href="/blog/archives/">
<div> <div>
@(Contents.Count) @(Contents.Count)
</div> </div>
</a>
</div> </div>
<div class="flex flex-row justify-between px-6 py-2 text-xl"> <div class="flex flex-row justify-between px-6 py-2 text-xl">
@ -28,9 +30,11 @@
标签 标签
</div> </div>
<a href="/blog/tags/">
<div> <div>
@(Contents.Tags.Count) @(Contents.Tags.Count)
</div> </div>
</a>
</div> </div>
<div class="text-xl px-2 py-2"> <div class="text-xl px-2 py-2">

View File

@ -9,8 +9,8 @@
<div> <div>
文章地址: 文章地址:
<a href="/blog/essays/@(EssayAddress)" target="_blank" class="text-blue-600"> <a href="/blog/essays/@(EssayFilename)" target="_blank" class="text-blue-600">
@($"https://rrricardo.top/blog/essays/{EssayAddress}") @($"https://rrricardo.top/blog/essays/{EssayFilename}")
</a> </a>
</div> </div>
@ -19,15 +19,15 @@
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" class="text-blue-600"> <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" class="text-blue-600">
CC BY-NC-SA 4.0 CC BY-NC-SA 4.0
</a> </a>
许可协议,转载请注明来 许可协议,诸位读者如有兴趣可任意转载,不必征询许可,但请注明“转载
<a href="https://rrricardo.top/blog/" target="_blank" class="text-blue-600"> <a href="https://rrricardo.top/blog/" target="_blank" class="text-blue-600">
Ricardo's Blog Ricardo's Blog
</a>。 </a>
</div> </div>
</div> </div>
</div> </div>
@code @code
{ {
[Parameter] public string? EssayAddress { get; set; } [Parameter] public string? EssayFilename { get; set; }
} }

View File

@ -22,9 +22,8 @@ public static class WebApplicationBuilderExtensions
builder.Services.AddSingleton<RendererService>(); builder.Services.AddSingleton<RendererService>();
builder.Services.AddSingleton<IEssayContentService, EssayContentService>(); builder.Services.AddSingleton<IEssayContentService, EssayContentService>();
builder.Services.AddTransient<ImagePostRenderProcessor>(); builder.Services.AddTransient<ImagePostRenderProcessor>();
builder.Services.AddTransient<CodeBlockPostRenderProcessor>();
builder.Services.AddTransient<TablePostRenderProcessor>();
builder.Services.AddTransient<HeadlinePostRenderProcessor>(); builder.Services.AddTransient<HeadlinePostRenderProcessor>();
builder.Services.AddTransient<EssayStylesPostRenderProcessor>();
builder.Services.AddTransient<BlogOptions>(provider => builder.Services.AddTransient<BlogOptions>(provider =>
provider.GetRequiredService<IOptions<BlogOptions>>().Value); provider.GetRequiredService<IOptions<BlogOptions>>().Value);

View File

@ -9,9 +9,8 @@ public static class WebApplicationExtensions
public static void UseYaeBlog(this WebApplication application) public static void UseYaeBlog(this WebApplication application)
{ {
application.UsePostRenderProcessor<ImagePostRenderProcessor>(); application.UsePostRenderProcessor<ImagePostRenderProcessor>();
application.UsePostRenderProcessor<CodeBlockPostRenderProcessor>();
application.UsePostRenderProcessor<TablePostRenderProcessor>();
application.UsePostRenderProcessor<HeadlinePostRenderProcessor>(); application.UsePostRenderProcessor<HeadlinePostRenderProcessor>();
application.UsePostRenderProcessor<EssayStylesPostRenderProcessor>();
} }
private static void UsePreRenderProcessor<T>(this WebApplication application) where T : IPreRenderProcessor private static void UsePreRenderProcessor<T>(this WebApplication application) where T : IPreRenderProcessor

View File

@ -41,10 +41,14 @@
</div> </div>
<div class="grid grid-cols-3"> <div class="grid grid-cols-3">
<div class="col-span-3 md:col-span-2"> <div class="col-span-3 md:col-span-2 flex flex-col gap-3">
<div>
@((MarkupString)_essay!.HtmlContent) @((MarkupString)_essay!.HtmlContent)
</div>
<LicenseDisclaimer EssayAddress="@BlogKey"/> <div>
<LicenseDisclaimer EssayFilename="@BlogKey"/>
</div>
</div> </div>
<div class="col-span-3 md:col-span-1"> <div class="col-span-3 md:col-span-1">

View File

@ -38,7 +38,7 @@
<li class="p-2"> <li class="p-2">
<div class="flex flex-row"> <div class="flex flex-row">
<a href="/blog/tags/?tagName=@(pair.Key.UrlEncodedTagName)"> <a href="/blog/tags/?tagName=@(pair.Key.UrlEncodedTagName)">
<div class="text-blue-600 text-lg"> <div class="text-sky-600 text-lg">
# @(pair.Key.TagName) # @(pair.Key.TagName)
</div> </div>
</a> </a>

View File

@ -1,29 +0,0 @@
using AngleSharp;
using AngleSharp.Dom;
using YaeBlog.Abstraction;
using YaeBlog.Models;
namespace YaeBlog.Processors;
public class CodeBlockPostRenderProcessor : IPostRenderProcessor
{
public async Task<BlogEssay> ProcessAsync(BlogEssay essay)
{
BrowsingContext context = new(Configuration.Default);
IDocument document = await context.OpenAsync(
req => req.Content(essay.HtmlContent));
IEnumerable<IElement> preElements = from e in document.All
where e.LocalName == "pre"
select e;
foreach (IElement element in preElements)
{
element.ClassList.Add("p-3 text-bg-secondary rounded-1");
}
return essay.WithNewHtmlContent(document.DocumentElement.OuterHtml);
}
public string Name => nameof(CodeBlockPostRenderProcessor);
}

View File

@ -0,0 +1,102 @@
using AngleSharp;
using AngleSharp.Dom;
using YaeBlog.Abstraction;
using YaeBlog.Models;
namespace YaeBlog.Processors;
/// <summary>
/// 向渲染的HTML中插入Tailwind CSS的渲染后处理器
/// </summary>
public sealed class EssayStylesPostRenderProcessor : IPostRenderProcessor
{
public string Name => nameof(EssayStylesPostRenderProcessor);
public async Task<BlogEssay> ProcessAsync(BlogEssay essay)
{
BrowsingContext context = new(Configuration.Default);
IDocument document = await context.OpenAsync(
req => req.Content(essay.HtmlContent));
ApplyGlobalCssStyles(document);
BeatifyTable(document);
return essay.WithNewHtmlContent(document.DocumentElement.OuterHtml);
}
private readonly Dictionary<string, string> _globalCssStyles = new()
{
{ "pre", "p-4 bg-slate-300 rounded-sm overflow-x-auto" },
{ "h2", "text-3xl font-bold py-4" },
{ "h3", "text-2xl font-bold py-3" },
{ "h4", "text-xl font-bold py-2" },
{ "h5", "text-lg font-bold py-1" },
{ "p", "p-2" },
{ "img", "w-11/12 block mx-auto my-2 rounded-md shadow-md" },
{ "ul", "list-disc pl-2" }
};
private void ApplyGlobalCssStyles(IDocument document)
{
foreach ((string tag, string style) in _globalCssStyles)
{
foreach (IElement element in document.GetElementsByTagName(tag))
{
element.ClassList.Add(style);
}
}
}
private static void BeatifyTable(IDocument document)
{
foreach (IElement element in from e in document.All
where e.LocalName == "table"
select e)
{
element.ClassList.Add("mx-auto border-collapse table-auto overflow-x-auto");
// thead元素
foreach (IElement headElement in from e in element.Children
where e.LocalName == "thead"
select e)
{
headElement.ClassList.Add("bg-slate-200");
// tr in thead
foreach (IElement trElement in from e in headElement.Children
where e.LocalName == "tr"
select e)
{
trElement.ClassList.Add("border border-slate-300");
// th in tr
foreach (IElement thElement in from e in trElement.Children
where e.LocalName == "th"
select e)
{
thElement.ClassList.Add("px-4 py-1");
}
}
}
// tbody元素
foreach (IElement bodyElement in from e in element.Children
where e.LocalName == "tbody"
select e)
{
// tr in tbody
foreach (IElement trElement in from e in bodyElement.Children
where e.LocalName == "tr"
select e)
{
foreach (IElement tdElement in from e in trElement.Children
where e.LocalName == "td"
select e)
{
tdElement.ClassList.Add("px-4 py-1 border border-slate-300");
}
}
}
}
}
}

View File

@ -11,13 +11,11 @@ public class ImagePostRenderProcessor(ILogger<ImagePostRenderProcessor> logger,
IOptions<BlogOptions> options) IOptions<BlogOptions> options)
: IPostRenderProcessor : IPostRenderProcessor
{ {
private static readonly AngleSharp.IConfiguration s_configuration = Configuration.Default;
private readonly BlogOptions _options = options.Value; private readonly BlogOptions _options = options.Value;
public async Task<BlogEssay> ProcessAsync(BlogEssay essay) public async Task<BlogEssay> ProcessAsync(BlogEssay essay)
{ {
BrowsingContext context = new(s_configuration); BrowsingContext context = new(Configuration.Default);
IDocument html = await context.OpenAsync( IDocument html = await context.OpenAsync(
req => req.Content(essay.HtmlContent)); req => req.Content(essay.HtmlContent));
@ -33,7 +31,6 @@ public class ImagePostRenderProcessor(ILogger<ImagePostRenderProcessor> logger,
logger.LogDebug("Found image link: '{}'", attr.Value); logger.LogDebug("Found image link: '{}'", attr.Value);
attr.Value = GenerateImageLink(attr.Value, essay.FileName); attr.Value = GenerateImageLink(attr.Value, essay.FileName);
} }
element.ClassList.Add("essay-image");
} }
return essay.WithNewHtmlContent(html.DocumentElement.OuterHtml); return essay.WithNewHtmlContent(html.DocumentElement.OuterHtml);
} }

View File

@ -1,34 +0,0 @@
using AngleSharp;
using AngleSharp.Dom;
using AngleSharp.Html.Dom;
using YaeBlog.Abstraction;
using YaeBlog.Models;
namespace YaeBlog.Processors;
public class TablePostRenderProcessor: IPostRenderProcessor
{
public async Task<BlogEssay> ProcessAsync(BlogEssay essay)
{
BrowsingContext browsingContext = new(Configuration.Default);
IDocument document = await browsingContext.OpenAsync(
req => req.Content(essay.HtmlContent));
IEnumerable<IHtmlTableElement> tableElements = from item in document.All
where item.LocalName == "table"
select item as IHtmlTableElement;
foreach (IHtmlTableElement element in tableElements)
{
IHtmlDivElement divElement = document.CreateElement<IHtmlDivElement>();
divElement.InnerHtml = element.OuterHtml;
divElement.ClassList.Add("py-2", "table-wrapper");
element.Replace(divElement);
}
return essay.WithNewHtmlContent(document.DocumentElement.OuterHtml);
}
public string Name => nameof(TablePostRenderProcessor);
}

View File

@ -7,10 +7,6 @@
<PackageReference Include="YamlDotNet" Version="16.2.1"/> <PackageReference Include="YamlDotNet" Version="16.2.1"/>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Watch Remove="**\*.razor~"/>
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>

View File

@ -1,6 +1,6 @@
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
module.exports = { module.exports = {
content: ["**/*.razor", "**/*.cshtml", "**/*.html"], content: ["**/*.razor", "**/*.cshtml", "**/*.html", "Processors/EssayStylesPostRenderProcessor.cs"],
theme: { theme: {
extend: {}, extend: {},
}, },