feat: 从Bootstrap迁移到Tailwind css #9
|
@ -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">
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
102
YaeBlog/Processors/EssayStylesPostRenderProcessor.cs
Normal file
102
YaeBlog/Processors/EssayStylesPostRenderProcessor.cs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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>
|
||||||
|
|
|
@ -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: {},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue
Block a user