Skip to content

Commit 74dc35f

Browse files
committed
feat: add ability to copy code block (closes #366)
1 parent 43a7c08 commit 74dc35f

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using Markdig;
2+
using Markdig.Renderers;
3+
using Markdig.Renderers.Html;
4+
using Markdig.Syntax;
5+
6+
namespace LinkDotNet.Blog.Web.Features;
7+
8+
internal static class MarkdownPipelineBuilderExtensions
9+
{
10+
public static MarkdownPipelineBuilder UseCopyCodeBlock(this MarkdownPipelineBuilder pipeline)
11+
{
12+
pipeline.Extensions.Add(new CopyCodeBlockToClipboardExtension());
13+
return pipeline;
14+
}
15+
}
16+
17+
internal sealed class CopyCodeBlockToClipboardExtension : IMarkdownExtension
18+
{
19+
public void Setup(MarkdownPipelineBuilder pipeline)
20+
{
21+
}
22+
23+
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
24+
{
25+
if (renderer is not HtmlRenderer htmlRenderer)
26+
{
27+
return;
28+
}
29+
30+
var originalCodeBlockRenderer = htmlRenderer.ObjectRenderers.FindExact<CodeBlockRenderer>();
31+
if (originalCodeBlockRenderer is null)
32+
{
33+
return;
34+
}
35+
36+
htmlRenderer.ObjectRenderers.Remove(originalCodeBlockRenderer);
37+
htmlRenderer.ObjectRenderers.Add(new CustomCodeBlockRenderer());
38+
}
39+
}
40+
41+
internal sealed class CustomCodeBlockRenderer : CodeBlockRenderer
42+
{
43+
protected override void Write(HtmlRenderer renderer, CodeBlock obj)
44+
{
45+
renderer.Write("""<div class="position-relative">""");
46+
renderer.Write("""
47+
<button class="btn btn-sm position-absolute top-0 end-0 m-2 border border-primary text-primary copy-btn"
48+
type="button"
49+
onclick="navigator.clipboard.writeText(this.parentElement.querySelector('pre code').textContent)">
50+
<i class="copy"></i>
51+
</button>
52+
""");
53+
base.Write(renderer, obj);
54+
renderer.Write("</div>");
55+
}
56+
}

src/LinkDotNet.Blog.Web/Features/MarkdownConverter.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public static class MarkdownConverter
1818
.UseAutoIdentifiers(AutoIdentifierOptions.GitHub)
1919
.UseEmojiAndSmiley()
2020
.UseBootstrap()
21+
.UseCopyCodeBlock()
2122
.Build();
2223

2324
public static MarkupString ToMarkupString(string markdown)

src/LinkDotNet.Blog.Web/wwwroot/css/basic.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,21 @@ code {
115115
background: #696969;
116116
}
117117

118+
.copy-btn {
119+
border-color: whitesmoke !important;
120+
}
121+
122+
.copy-btn:active {
123+
animation: copy-flash 0.5s ease-out;
124+
}
125+
126+
@keyframes copy-flash {
127+
50% {
128+
border-color: whitesmoke !important;
129+
background: whitesmoke;
130+
}
131+
}
132+
118133
/* Template defined css */
119134
.valid.modified:not([type=checkbox]) {
120135
outline: 1px solid #26b050;
@@ -127,6 +142,7 @@ code {
127142
.validation-message {
128143
color: red;
129144
}
145+
130146
#blazor-error-ui {
131147
background: lightyellow;
132148
bottom: 0;

0 commit comments

Comments
 (0)