Skip to content

Commit bedddef

Browse files
committed
feat: Add Markdown support with AutoUpdater.NET.Official.Markdown package
Introduce a new package for rendering Markdown changelogs with enhanced styling and flexibility: - Add AutoUpdater.NET.Official.Markdown package with modern CSS styling and emoji support - Implement composite viewer pattern allowing to use and viewer like Webview2/WebBrowser/RichTextBox - Improve documentation with XML comments and see tags - Update build scripts to include Markdown package compilation - Bump version to 1.9.5.1 across all packages - Update copyright year to 2025 The new Markdown package provides: - GitHub-style Markdown rendering - Customizable viewer priority system - Support for both WebView2 and WebBrowser rendering - Proper cleanup handling for viewer resources
1 parent f6b6624 commit bedddef

22 files changed

+545
-46
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ bld/
2222
[Oo]bj/
2323
[Ll]og/
2424

25+
build/
26+
# Keep nuspec files in build directories
27+
!**/build/*.nuspec
28+
2529
# Visual Studio 2015 cache/options directory
2630
.vs/
2731
# Uncomment if you have tasks that create the project's static files in wwwroot
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>library</OutputType>
4+
<TargetFrameworks>net462;netcoreapp3.1;net5.0-windows;net6.0-windows;net7.0-windows;net8.0-windows</TargetFrameworks>
5+
<UseWindowsForms>true</UseWindowsForms>
6+
<RootNamespace>AutoUpdaterDotNET.Markdown</RootNamespace>
7+
<AssemblyTitle>AutoUpdater.NET.Markdown</AssemblyTitle>
8+
<Company>RBSoft</Company>
9+
<Product>AutoUpdater.NET.Markdown</Product>
10+
<Copyright>Copyright © 2012-2025 RBSoft</Copyright>
11+
<Version>1.9.5.1</Version>
12+
<!-- <SignAssembly>true</SignAssembly>-->
13+
<!-- <AssemblyOriginatorKeyFile>..\AutoUpdater.NET\AutoUpdater.NET.snk</AssemblyOriginatorKeyFile>-->
14+
<NeutralLanguage>en</NeutralLanguage>
15+
<PackageId>Autoupdater.NET.Markdown</PackageId>
16+
<IncludeSymbols>true</IncludeSymbols>
17+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
18+
<Title>AutoUpdater.NET Markdown Extension</Title>
19+
<Authors>rbsoft</Authors>
20+
<Description>Markdown extension for AutoUpdater.NET that provides markdown support.</Description>
21+
<PackageProjectUrl>https://github.com/ravibpatel/AutoUpdater.NET</PackageProjectUrl>
22+
<PackageTags>autoupdate updater markdown</PackageTags>
23+
<PackageReleaseNotes>https://github.com/ravibpatel/AutoUpdater.NET/releases</PackageReleaseNotes>
24+
<PackageOutputPath>build</PackageOutputPath>
25+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
26+
<LangVersion>latest</LangVersion>
27+
</PropertyGroup>
28+
29+
<ItemGroup>
30+
<PackageReference Include="Markdig" Version="0.40.0" />
31+
</ItemGroup>
32+
33+
<ItemGroup>
34+
<ProjectReference Include="..\AutoUpdater.NET\AutoUpdater.NET.csproj" />
35+
</ItemGroup>
36+
</Project>
+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
using System.Windows.Forms;
2+
using AutoUpdaterDotNET.ChangelogViewers;
3+
using Markdig;
4+
5+
namespace AutoUpdaterDotNET.Markdown;
6+
7+
/// <summary>
8+
/// A changelog viewer that renders Markdown content using either a provided viewer or a WebBrowser control.
9+
/// </summary>
10+
public class MarkdownViewer : IChangelogViewer
11+
{
12+
private readonly IChangelogViewer _innerViewer;
13+
private readonly bool _cleanup;
14+
private const string DefaultStyle = @"
15+
<style>
16+
@font-face {
17+
font-family: 'Segoe UI Emoji';
18+
src: local('Segoe UI Emoji');
19+
}
20+
body {
21+
font-family: 'Segoe UI Emoji', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
22+
font-size: 10.5pt;
23+
line-height: 1.4;
24+
word-wrap: break-word;
25+
padding: 12px;
26+
margin: 0;
27+
background-color: #F4F4F4;
28+
color: #000000;
29+
}
30+
h1, h2, h3, h4, h5, h6 {
31+
margin-top: 16px;
32+
margin-bottom: 8px;
33+
font-weight: 600;
34+
line-height: 1.25;
35+
}
36+
h1 { font-size: 20pt; }
37+
h2 { font-size: 16pt; }
38+
h3 { font-size: 14pt; }
39+
h4 { font-size: 12pt; }
40+
p { margin: 8px 0; }
41+
code {
42+
font-family: Consolas, 'Courier New', monospace;
43+
padding: 2px 4px;
44+
background-color: rgba(0, 0, 0, 0.03);
45+
border-radius: 3px;
46+
font-size: 10pt;
47+
}
48+
pre code {
49+
display: block;
50+
padding: 8px;
51+
margin: 8px 0;
52+
overflow: auto;
53+
line-height: 1.45;
54+
}
55+
blockquote {
56+
padding: 0 8px;
57+
margin: 8px 0;
58+
color: #666666;
59+
border-left: 3px solid #CCCCCC;
60+
}
61+
ul, ol {
62+
padding-left: 24px;
63+
margin: 8px 0;
64+
}
65+
table {
66+
border-spacing: 0;
67+
border-collapse: collapse;
68+
margin: 8px 0;
69+
width: 100%;
70+
}
71+
table th, table td {
72+
padding: 4px 8px;
73+
border: 1px solid #CCCCCC;
74+
}
75+
table tr:nth-child(2n) {
76+
background-color: rgba(0, 0, 0, 0.02);
77+
}
78+
hr {
79+
height: 1px;
80+
padding: 0;
81+
margin: 16px 0;
82+
background-color: #CCCCCC;
83+
border: 0;
84+
}
85+
img {
86+
max-width: 100%;
87+
height: auto;
88+
}
89+
a {
90+
color: #0066CC;
91+
text-decoration: none;
92+
}
93+
a:hover {
94+
text-decoration: underline;
95+
}
96+
</style>";
97+
98+
/// <summary>
99+
/// Initializes a new instance of the MarkdownViewer class.
100+
/// </summary>
101+
/// <param name="viewer">Optional viewer to use for rendering. If not provided, uses WebBrowser control.</param>
102+
/// <param name="cleanup">Whether to clean up the inner viewer when this viewer is disposed.</param>
103+
public MarkdownViewer(IChangelogViewer viewer = null, bool cleanup = true)
104+
{
105+
_innerViewer = viewer ?? new WebBrowserViewer();
106+
_cleanup = cleanup || viewer == null;
107+
}
108+
109+
/// <inheritdoc />
110+
public Control Control => _innerViewer.Control;
111+
112+
/// <inheritdoc />
113+
public bool SupportsUrl => true;
114+
115+
/// <inheritdoc />
116+
public void LoadContent(string content)
117+
{
118+
if (_innerViewer is RichTextBoxViewer or MarkdownViewer)
119+
{
120+
_innerViewer.LoadContent(content);
121+
return;
122+
}
123+
124+
var pipeline = new MarkdownPipelineBuilder()
125+
.UseAdvancedExtensions()
126+
.Build();
127+
128+
var html = Markdig.Markdown.ToHtml(content, pipeline);
129+
var fullHtml = $"<!DOCTYPE html><html><head>{DefaultStyle}</head><body>{html}</body></html>";
130+
131+
_innerViewer.LoadContent(fullHtml);
132+
}
133+
134+
/// <inheritdoc />
135+
public void LoadUrl(string url)
136+
{
137+
using var client = new System.Net.WebClient();
138+
if (AutoUpdater.BasicAuthChangeLog != null)
139+
{
140+
var auth = (BasicAuthentication)AutoUpdater.BasicAuthChangeLog;
141+
client.Credentials = new System.Net.NetworkCredential(auth.Username, auth.Password);
142+
}
143+
144+
var content = client.DownloadString(url);
145+
LoadContent(content);
146+
}
147+
148+
/// <inheritdoc />
149+
public void Cleanup()
150+
{
151+
if (_cleanup)
152+
_innerViewer.Cleanup();
153+
}
154+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using AutoUpdaterDotNET.ChangelogViewers;
2+
3+
namespace AutoUpdaterDotNET.Markdown;
4+
5+
/// <summary>
6+
/// Provides a Markdown viewer that uses a WebBrowser control or a user provided viewer to render Markdown content.
7+
/// </summary>
8+
public class MarkdownViewerProvider(int priority = 2) : IChangelogViewerProvider
9+
{
10+
private readonly IChangelogViewer _viewer;
11+
12+
/// <summary>
13+
/// Creates a new instance of the <see cref="MarkdownViewerProvider"/> with default priority 2.
14+
/// </summary>
15+
public MarkdownViewerProvider() : this(2) { }
16+
17+
/// <summary>
18+
/// Creates a new instance of the <see cref="MarkdownViewerProvider"/> with a specific viewer.
19+
/// </summary>
20+
/// <param name="viewer">The viewer to use for rendering Markdown content.</param>
21+
/// <param name="priority">The priority of this provider.</param>
22+
public MarkdownViewerProvider(IChangelogViewer viewer, int priority = 2) : this(priority)
23+
{
24+
_viewer = viewer;
25+
}
26+
27+
/// <summary>
28+
/// Gets whether this provider is available. Always returns true as it uses WebBrowser as fallback.
29+
/// </summary>
30+
public bool IsAvailable => true;
31+
32+
/// <inheritdoc />
33+
public int Priority { get; } = priority;
34+
35+
/// <summary>
36+
/// Creates a new instance of the <see cref="MarkdownViewer"/> class.
37+
/// </summary>
38+
public IChangelogViewer CreateViewer() => new MarkdownViewer(_viewer);
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
3+
<metadata>
4+
<id>Autoupdater.NET.Official.Markdown</id>
5+
<version>1.9.5.1</version>
6+
<title>AutoUpdater.NET Markdown Extension</title>
7+
<authors>rbsoft</authors>
8+
<requireLicenseAcceptance>false</requireLicenseAcceptance>
9+
<license type="expression">MIT</license>
10+
<readme>docs\README.md</readme>
11+
<licenseUrl>https://licenses.nuget.org/MIT</licenseUrl>
12+
<projectUrl>https://github.com/ravibpatel/AutoUpdater.NET</projectUrl>
13+
<description>Markdown extension for AutoUpdater.NET that provides Markdown rendering capabilities for changelogs.</description>
14+
<releaseNotes>https://github.com/ravibpatel/AutoUpdater.NET/releases</releaseNotes>
15+
<copyright>Copyright 2012-2025 RBSoft</copyright>
16+
<tags>autoupdate updater markdown changelog</tags>
17+
<dependencies>
18+
<group targetFramework=".NETFramework4.6.2">
19+
<dependency id="Autoupdater.NET.Official" version="1.9.5.1" exclude="Build,Analyzers"/>
20+
<dependency id="Markdig" version="0.40.0" exclude="Build,Analyzers"/>
21+
</group>
22+
<group targetFramework=".NETCoreApp3.1">
23+
<dependency id="Autoupdater.NET.Official" version="1.9.5.1" exclude="Build,Analyzers"/>
24+
<dependency id="Markdig" version="0.40.0" exclude="Build,Analyzers"/>
25+
</group>
26+
<group targetFramework="net5.0-windows7.0">
27+
<dependency id="Autoupdater.NET.Official" version="1.9.5.1" exclude="Build,Analyzers"/>
28+
<dependency id="Markdig" version="0.40.0" exclude="Build,Analyzers"/>
29+
</group>
30+
<group targetFramework="net6.0-windows7.0">
31+
<dependency id="Autoupdater.NET.Official" version="1.9.5.1" exclude="Build,Analyzers"/>
32+
<dependency id="Markdig" version="0.40.0" exclude="Build,Analyzers"/>
33+
</group>
34+
<group targetFramework="net7.0-windows7.0">
35+
<dependency id="Autoupdater.NET.Official" version="1.9.5.1" exclude="Build,Analyzers"/>
36+
<dependency id="Markdig" version="0.40.0" exclude="Build,Analyzers"/>
37+
</group>
38+
<group targetFramework="net8.0-windows7.0">
39+
<dependency id="Autoupdater.NET.Official" version="1.9.5.1" exclude="Build,Analyzers"/>
40+
<dependency id="Markdig" version="0.40.0" exclude="Build,Analyzers"/>
41+
</group>
42+
</dependencies>
43+
</metadata>
44+
<files>
45+
<file src="..\..\README.md" target="docs\"/>
46+
<file src="lib\net462\AutoUpdater.NET.Markdown.*" target="lib\net462" exclude="**\*.deps.json"/>
47+
<file src="lib\netcoreapp3.1\AutoUpdater.NET.Markdown.*" target="lib\netcoreapp3.1" exclude="**\*.deps.json"/>
48+
<file src="lib\net5.0-windows7.0\AutoUpdater.NET.Markdown.*" target="lib\net5.0-windows7.0" exclude="**\*.deps.json"/>
49+
<file src="lib\net6.0-windows7.0\AutoUpdater.NET.Markdown.*" target="lib\net6.0-windows7.0" exclude="**\*.deps.json"/>
50+
<file src="lib\net7.0-windows7.0\AutoUpdater.NET.Markdown.*" target="lib\net7.0-windows7.0" exclude="**\*.deps.json"/>
51+
<file src="lib\net8.0-windows7.0\AutoUpdater.NET.Markdown.*" target="lib\net8.0-windows7.0" exclude="**\*.deps.json"/>
52+
</files>
53+
</package>

AutoUpdater.NET.WebView2/AutoUpdater.NET.WebView2.csproj

+3-6
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,8 @@
88
<AssemblyTitle>AutoUpdater.NET.WebView2</AssemblyTitle>
99
<Company>RBSoft</Company>
1010
<Product>AutoUpdater.NET.WebView2</Product>
11-
<Copyright>Copyright © 2012-2024 RBSoft</Copyright>
12-
<Version>1.9.3.0</Version>
13-
<AssemblyVersion>1.9.3.0</AssemblyVersion>
14-
<FileVersion>1.9.3.0</FileVersion>
15-
<PackageVersion>1.9.3.0</PackageVersion>
11+
<Copyright>Copyright © 2012-2025 RBSoft</Copyright>
12+
<Version>1.9.5.1</Version>
1613
<SignAssembly>true</SignAssembly>
1714
<AssemblyOriginatorKeyFile>..\AutoUpdater.NET\AutoUpdater.NET.snk</AssemblyOriginatorKeyFile>
1815
<NeutralLanguage>en</NeutralLanguage>
@@ -26,7 +23,7 @@
2623
<PackageTags>autoupdate updater webview2 edge</PackageTags>
2724
<PackageReleaseNotes>https://github.com/ravibpatel/AutoUpdater.NET/releases</PackageReleaseNotes>
2825
<PackageOutputPath>build</PackageOutputPath>
29-
<DocumentationFile>$(OutDir)\AutoUpdater.NET.WebView2.xml</DocumentationFile>
26+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
3027
<LangVersion>latest</LangVersion>
3128
</PropertyGroup>
3229

AutoUpdater.NET.WebView2/WebView2Viewer.cs

+16-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
namespace AutoUpdaterDotNET.WebView2;
99

10+
/// <summary>
11+
/// A changelog viewer for displaying changelogs using a modern browser.
12+
/// </summary>
1013
public class WebView2Viewer : IChangelogViewer
1114
{
1215
private bool _isInitialized;
@@ -15,7 +18,11 @@ public class WebView2Viewer : IChangelogViewer
1518
Dock = DockStyle.Fill,
1619
AllowExternalDrop = false
1720
};
21+
22+
/// <inheritdoc />
1823
public Control Control => _webView;
24+
25+
/// <inheritdoc />
1926
public bool SupportsUrl => true;
2027

2128
private async Task EnsureInitialized()
@@ -33,27 +40,29 @@ private async Task EnsureInitialized()
3340
}
3441
}
3542

43+
/// <inheritdoc />
3644
public async void LoadContent(string content)
3745
{
3846
await EnsureInitialized();
3947
_webView.CoreWebView2.SetVirtualHostNameToFolderMapping("local.files", Path.GetTempPath(), CoreWebView2HostResourceAccessKind.Allow);
4048

4149
// Write content to a temporary HTML file
4250
var tempFile = Path.Combine(Path.GetTempPath(), "changelog.html");
43-
File.WriteAllText(tempFile, content);
51+
File.WriteAllText(tempFile, content, System.Text.Encoding.UTF8);
4452

4553
// Navigate to the local file
4654
_webView.CoreWebView2.Navigate("https://local.files/changelog.html");
4755
}
4856

57+
/// <inheritdoc />
4958
public async void LoadUrl(string url)
5059
{
5160
await EnsureInitialized();
5261

5362
if (AutoUpdater.BasicAuthChangeLog != null)
5463
{
5564
_webView.CoreWebView2.BasicAuthenticationRequested += delegate (object _, CoreWebView2BasicAuthenticationRequestedEventArgs args)
56-
{
65+
{
5766
args.Response.UserName = ((BasicAuthentication)AutoUpdater.BasicAuthChangeLog).Username;
5867
args.Response.Password = ((BasicAuthentication)AutoUpdater.BasicAuthChangeLog).Password;
5968
};
@@ -62,6 +71,7 @@ public async void LoadUrl(string url)
6271
_webView.CoreWebView2.Navigate(url);
6372
}
6473

74+
/// <inheritdoc />
6575
public void Cleanup()
6676
{
6777
if (File.Exists(Path.Combine(Path.GetTempPath(), "changelog.html")))
@@ -78,6 +88,10 @@ public void Cleanup()
7888
_webView.Dispose();
7989
}
8090

91+
/// <summary>
92+
/// Checks if WebView2 is available on the current environment
93+
/// </summary>
94+
/// <returns>True if WebView2 is available, false otherwise</returns>
8195
public static bool IsAvailable()
8296
{
8397
try

0 commit comments

Comments
 (0)