OfficeIMO.MarkdownRenderer
0.1.4
Prefix Reserved
dotnet add package OfficeIMO.MarkdownRenderer --version 0.1.4
NuGet\Install-Package OfficeIMO.MarkdownRenderer -Version 0.1.4
<PackageReference Include="OfficeIMO.MarkdownRenderer" Version="0.1.4" />
<PackageVersion Include="OfficeIMO.MarkdownRenderer" Version="0.1.4" />
<PackageReference Include="OfficeIMO.MarkdownRenderer" />
paket add OfficeIMO.MarkdownRenderer --version 0.1.4
#r "nuget: OfficeIMO.MarkdownRenderer, 0.1.4"
#:package OfficeIMO.MarkdownRenderer@0.1.4
#addin nuget:?package=OfficeIMO.MarkdownRenderer&version=0.1.4
#tool nuget:?package=OfficeIMO.MarkdownRenderer&version=0.1.4
OfficeIMO.MarkdownRenderer
Small helper library to render Markdown using OfficeIMO.Markdown into HTML that is easy to host in WebView2 (or any browser):
BuildShellHtml(...): returns a full HTML page that preloads CSS/Prism/Mermaid onceRenderBodyHtml(...): returns an HTML fragment for a given Markdown stringBuildUpdateScript(...): returns a JavaScript snippet callingupdateContent(...)RenderUpdateScript(...): convenience helper that renders Markdown and returns theupdateContent(...)snippet
Chat quickstart (WebView2)
using OfficeIMO.MarkdownRenderer;
// 1) Load shell once
var opts = MarkdownRendererPresets.CreateChatStrict(baseHref: null);
webView.NavigateToString(MarkdownRenderer.BuildShellHtml("Chat", opts));
// 2) For each message update
await webView.ExecuteScriptAsync(MarkdownRenderer.RenderUpdateScript(markdownText, opts));
Alternative update path (recommended for streaming/large payloads)
BuildShellHtml(...) includes a WebView2 message listener, so you can send the updated HTML without calling ExecuteScriptAsync:
using OfficeIMO.MarkdownRenderer;
var opts = MarkdownRendererPresets.CreateChatStrict(baseHref: null);
webView.NavigateToString(MarkdownRenderer.BuildShellHtml("Chat", opts));
// After CoreWebView2 is initialized and navigation completed:
var bodyHtml = MarkdownRenderer.RenderBodyHtml(markdownText, opts);
webView.CoreWebView2.PostWebMessageAsString(bodyHtml);
You can also send an object payload if you want to extend the message contract later:
webView.CoreWebView2.PostWebMessageAsJson("{\"bodyHtml\":" + System.Text.Json.JsonSerializer.Serialize(bodyHtml) + "}");
Chat bubble helpers (optional)
If you want message "bubbles" without authoring HTML in the app, use the bubble wrapper helpers.
The chat presets already use HtmlStyle.ChatAuto which includes bubble CSS classes (opt-in):
var opts = MarkdownRendererPresets.CreateChatStrict();
// Render a single user message as a bubble
var bubbleHtml = MarkdownRenderer.RenderChatBubbleBodyHtml(markdownText, ChatMessageRole.User, opts);
webView.CoreWebView2.PostWebMessageAsString(bubbleHtml);
Presets
MarkdownRendererPresets.CreateChatStrict(...): safe defaults for untrusted content and a compact chat-friendly theme (HtmlStyle.ChatAuto).MarkdownRendererPresets.CreateChatRelaxed(...): enables HTML parsing and sanitizes raw HTML blocks (still conservative).MarkdownRendererPresets.CreateChatStrictMinimal(...): strict, but disables Mermaid/Chart/Math/Prism and copy buttons.- strict presets also enable chat-output normalization for short soft-wrapped
**bold**labels, inline-code line breaks, escaped inline-code spans (\code``), tight strong boundaries (**bold**next), and loose strong delimiters (** text**/**text **).
- strict presets also enable chat-output normalization for short soft-wrapped
Options (high level)
MarkdownRendererOptions.ReaderOptions: parsing behavior (HTML enabled/disabled, URL scheme restrictions, etc.).MarkdownRendererOptions.HtmlOptions: HTML + CSS rendering (theme, Prism, link/image hardening, same-origin restrictions).MarkdownRendererOptions.NormalizeSoftWrappedStrongSpans/NormalizeInlineCodeSpanLineBreaks/NormalizeEscapedInlineCodeSpans/NormalizeTightStrongBoundaries/NormalizeLooseStrongDelimiters: optional markdown text normalization before parsing.MarkdownRendererOptions.MarkdownPreProcessors: custom markdown text transforms before parsing.MarkdownRendererOptions.Mermaid/Chart/Math: optional client-side renderers for fenced blocks.MarkdownRendererOptions.HtmlPostProcessors: last-mile HTML transformations (custom diagram types, host integration).
Normalization is backed by OfficeIMO.Markdown.MarkdownInputNormalizer, so the same behavior is available directly via MarkdownReaderOptions.InputNormalization when parsing outside the renderer.
Offline assets (no network at runtime)
If your host runs with limited or no network access (or you want deterministic rendering), set:
MarkdownRendererOptions.HtmlOptions.AssetMode = AssetMode.Offline
When AssetMode.Offline is used, the shell builder will attempt to inline Mermaid/Chart/Math assets into the HTML
as data: URLs (best-effort). This avoids WebView runtime fetches.
using OfficeIMO.Markdown;
using OfficeIMO.MarkdownRenderer;
var opts = MarkdownRendererPresets.CreateChatStrict();
opts.HtmlOptions.AssetMode = AssetMode.Offline;
// Optional: point to local files so bundling never hits the network.
opts.Mermaid.ScriptUrl = @"C:\app\assets\mermaid.min.js";
opts.Chart.ScriptUrl = @"C:\app\assets\chart.umd.min.js";
opts.Math.CssUrl = @"C:\app\assets\katex.min.css";
opts.Math.ScriptUrl = @"C:\app\assets\katex.min.js";
opts.Math.AutoRenderScriptUrl = @"C:\app\assets\auto-render.min.js";
webView.NavigateToString(MarkdownRenderer.BuildShellHtml("Chat", opts));
Notes:
- Bundling supports
http(s)URLs and local file paths. Custom schemes (e.g. virtual host mappings) are not fetchable by the .NET process and will not be inlined. - If you set a strict CSP via
MarkdownRendererOptions.ContentSecurityPolicy, ensure it allowsdata:sources forscript-srcandstyle-src(or keepAssetMode.Onlineand host assets from allowed origins).
Theming and customization
- You are not stuck with the built-in styles. You can:
- choose a built-in preset:
HtmlStyle.ChatAuto,HtmlStyle.GithubAuto,HtmlStyle.Clean,HtmlStyle.Plain, etc. - override colors/spacing via
MarkdownRendererOptions.ShellCss(appended after built-ins, so it wins) - fully replace styling by using
HtmlStyle.Plainand providing your own CSS
- choose a built-in preset:
Useful HTML structure and CSS hooks
- Shell root:
#omdRoot(this is where content is injected) - Default content wrapper (from
RenderBodyHtml):<article class="markdown-body">...</article>(class is controlled byHtmlOptions.BodyClass) - Optional bubble wrappers (from
RenderChatBubbleBodyHtml):.omd-chat-row.omd-chat-bubble- roles:
.omd-role-user,.omd-role-assistant,.omd-role-system
- Optional block helpers emitted by the renderer:
- blocked images:
.omd-image-blocked - charts:
canvas.omd-chart - math:
.omd-math
- blocked images:
Overriding styles in the chat app
var opts = MarkdownRendererPresets.CreateChatStrict();
opts.ShellCss = """
/* Example: tighter paragraphs + custom bubble colors */
.omd-chat-bubble { border-radius: 18px; }
.omd-chat-row.omd-role-user .omd-chat-bubble { background: rgba(0, 120, 212, .18); }
""";
webView.NavigateToString(MarkdownRenderer.BuildShellHtml("Chat", opts));
WebView2 host message contract (optional)
Shell listens for WebView2 messages and updates content:
- Host → Web
PostWebMessageAsString(bodyHtml)(string payload)PostWebMessageAsJson({ type: "omd.update", bodyHtml: "..." })(recommended object payload)
Shell may also send helper messages to the host:
- Web → Host
{ type: "omd.copy", text: "..." }when the user clicks a copy button (code/table)
If the host handles omd.copy, it can put the text onto the native clipboard (more reliable than browser clipboard APIs in some environments).
Copy buttons (code + tables)
The chat presets enable copy buttons by default:
MarkdownRendererOptions.EnableCodeCopyButtons = trueMarkdownRendererOptions.EnableTableCopyButtons = true
If you are building your own preset, enable them explicitly:
var opts = new MarkdownRendererOptions();
opts.EnableCodeCopyButtons = true;
opts.EnableTableCopyButtons = true;
webView.NavigateToString(MarkdownRenderer.BuildShellHtml("Chat", opts));
Mermaid diagrams
Write Mermaid in fenced code blocks:
```mermaid
flowchart LR
A --> B
Charts (Chart.js)
To enable Chart.js rendering:
- set `opts.Chart.Enabled = true`
Write charts in fenced code blocks named `chart` containing JSON:
```markdown
~~~chart
{"type":"bar","data":{"labels":["A","B"],"datasets":[{"label":"Count","data":[3,7]}]}}
~~~
Security note
Defaults are biased for untrusted chat output:
- raw HTML parsing is disabled
javascript:/vbscript:URLs are blocked by the readerfile:URLs are blocked by default inMarkdownRendererOptions.ReaderOptions
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 is compatible. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.7.2
- OfficeIMO.Markdown (>= 0.5.7)
-
.NETStandard 2.0
- OfficeIMO.Markdown (>= 0.5.7)
-
net8.0
- OfficeIMO.Markdown (>= 0.5.7)
-
net9.0
- OfficeIMO.Markdown (>= 0.5.7)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.