Skip to content

Commit 7e0541c

Browse files
yotsudaclaude
andcommitted
v0.13.1: HTML/EML viewing, code block copy, bug fix
- HTML/HTM file viewing directly in WebView2 - EML file viewing with CSP sandboxing (MimeKit) - Right-click code blocks to copy content to clipboard - Disable pointing mode and slide view for HTML/EML files - Fix status bar Loading message persisting after tab switch Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8fcc5e7 commit 7e0541c

9 files changed

Lines changed: 107 additions & 121 deletions

MarkdownPointer.Mcp/MarkdownPointer.Mcp.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<ImplicitUsings>enable</ImplicitUsings>
88
<Nullable>enable</Nullable>
99
<AssemblyName>mdp-mcp</AssemblyName>
10-
<Version>0.14.0</Version>
10+
<Version>0.13.1</Version>
1111

1212
<!-- NuGet Tool Package settings -->
1313
<PackAsTool>true</PackAsTool>

MarkdownPointer/MainWindow.EventHandlers.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,8 @@ private async void SlideViewToggle_Click(object sender, RoutedEventArgs e)
287287
/// </summary>
288288
internal void UpdateSlideViewButton(TabItemData tab)
289289
{
290+
var isNonMarkdown = IsHtmlFile(tab.FilePath) || IsEmlFile(tab.FilePath);
291+
SlideViewToggle.IsEnabled = !isNonMarkdown;
290292
SlideViewToggle.IsChecked = tab.IsSlideView;
291293
}
292294

@@ -358,16 +360,18 @@ private void PointingModeToggle_Click(object sender, RoutedEventArgs e)
358360

359361
/// <summary>
360362
/// Updates pointing mode availability based on file type.
361-
/// SVG files don't support pointing mode since they are typically auto-generated.
363+
/// SVG and HTML files don't support pointing mode.
362364
/// </summary>
363365
private void UpdatePointingModeAvailability(TabItemData tab)
364366
{
365-
var isSvgFile = !string.IsNullOrEmpty(tab.FilePath) &&
366-
tab.FilePath.EndsWith(".svg", StringComparison.OrdinalIgnoreCase);
367-
368-
PointingModeToggle.IsEnabled = !isSvgFile;
369-
370-
if (isSvgFile)
367+
var isNonPointable = !string.IsNullOrEmpty(tab.FilePath) &&
368+
(tab.FilePath.EndsWith(".svg", StringComparison.OrdinalIgnoreCase) ||
369+
IsHtmlFile(tab.FilePath) ||
370+
IsEmlFile(tab.FilePath));
371+
372+
PointingModeToggle.IsEnabled = !isNonPointable;
373+
374+
if (isNonPointable)
371375
{
372376
if (_isPointingMode)
373377
{

MarkdownPointer/MainWindow.TabManagement.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,39 @@ private async void RenderMarkdown(TabItemData tab, bool viewToggle = false)
646646
}
647647
}
648648

649+
// HTML files: display directly in WebView2 without markdown processing
650+
if (IsHtmlFile(tab.FilePath))
651+
{
652+
tab.CleanupTempFile();
653+
tab.RenderedHtml = null;
654+
tab.WebView.CoreWebView2.Navigate(new Uri(tab.FilePath).AbsoluteUri);
655+
return;
656+
}
657+
658+
// EML files: extract HTML body via MimeKit and display
659+
// CSP blocks scripts and external resources (tracking pixels, etc.)
660+
if (IsEmlFile(tab.FilePath))
661+
{
662+
const string csp = "<meta http-equiv=\"Content-Security-Policy\" "
663+
+ "content=\"default-src 'none'; style-src 'unsafe-inline'; img-src data:;\">";
664+
var message = await MimeKit.MimeMessage.LoadAsync(tab.FilePath);
665+
var emlHtml = message.HtmlBody;
666+
if (emlHtml != null)
667+
{
668+
// Inject CSP into <head>, or prepend if no <head> tag
669+
var headIndex = emlHtml.IndexOf("<head>", StringComparison.OrdinalIgnoreCase);
670+
emlHtml = headIndex >= 0
671+
? emlHtml.Insert(headIndex + "<head>".Length, csp)
672+
: csp + emlHtml;
673+
}
674+
else
675+
{
676+
emlHtml = $"<html><head>{csp}</head><body><pre>{System.Net.WebUtility.HtmlEncode(message.TextBody ?? "(empty)")}</pre></body></html>";
677+
}
678+
NavigateToHtml(tab, emlHtml);
679+
return;
680+
}
681+
649682
// Fast path: view toggle with cached HTML
650683
if (viewToggle)
651684
{

MarkdownPointer/MainWindow.WebView.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ private async Task HandleContextMenuRequestedAsync(TabItemData tab, Microsoft.We
186186
if (!element) return 'none';
187187
if (element.closest('.mermaid')) return 'mermaid';
188188
if (element.closest('.katex') || element.closest('.math')) return 'math';
189+
var pre = element.closest('pre');
190+
if (pre && pre.querySelector('code')) return 'code';
189191
return 'none';
190192
}})()";
191193

@@ -196,7 +198,11 @@ private async Task HandleContextMenuRequestedAsync(TabItemData tab, Microsoft.We
196198
e.MenuItems.Clear();
197199
e.Handled = true;
198200

199-
if (elementType == "mermaid" || elementType == "math")
201+
if (elementType == "code")
202+
{
203+
await CopyCodeBlockToClipboard(tab, e.Location.X, e.Location.Y);
204+
}
205+
else if (elementType == "mermaid" || elementType == "math")
200206
{
201207
ShowDiagramContextMenu(tab, elementType);
202208
}
@@ -207,6 +213,28 @@ private async Task HandleContextMenuRequestedAsync(TabItemData tab, Microsoft.We
207213
}
208214
}
209215

216+
private async Task CopyCodeBlockToClipboard(TabItemData tab, int x, int y)
217+
{
218+
var script = $@"
219+
(function() {{
220+
var element = document.elementFromPoint({x}, {y});
221+
if (!element) return '';
222+
var pre = element.closest('pre');
223+
if (pre && pre.querySelector('code')) {{
224+
return pre.querySelector('code').textContent;
225+
}}
226+
return '';
227+
}})()";
228+
var result = await tab.WebView.CoreWebView2.ExecuteScriptAsync(script);
229+
// ExecuteScriptAsync returns JSON-encoded string, so deserialize it
230+
var codeText = JsonSerializer.Deserialize<string>(result) ?? "";
231+
if (!string.IsNullOrEmpty(codeText))
232+
{
233+
Clipboard.SetText(codeText);
234+
ShowStatusMessage("✓ Code copied to clipboard");
235+
}
236+
}
237+
210238
private void ShowDiagramContextMenu(TabItemData tab, string elementType)
211239
{
212240
var contextMenu = new System.Windows.Controls.ContextMenu();

MarkdownPointer/MainWindow.xaml.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@ public partial class MainWindow : Window
4646
".nupkg",
4747
};
4848

49+
private static bool IsHtmlFile(string? filePath)
50+
{
51+
if (string.IsNullOrEmpty(filePath)) return false;
52+
var ext = Path.GetExtension(filePath);
53+
return ext.Equals(".html", StringComparison.OrdinalIgnoreCase) ||
54+
ext.Equals(".htm", StringComparison.OrdinalIgnoreCase);
55+
}
56+
57+
private static bool IsEmlFile(string? filePath)
58+
{
59+
if (string.IsNullOrEmpty(filePath)) return false;
60+
return Path.GetExtension(filePath).Equals(".eml", StringComparison.OrdinalIgnoreCase);
61+
}
62+
4963
#endregion
5064

5165
#region Fields
@@ -660,7 +674,7 @@ private void OpenFileDialog(string? initialDirectory = null)
660674
{
661675
var dialog = new OpenFileDialog
662676
{
663-
Filter = "Markdown files|*.md;*.markdown;*.txt|SVG files|*.svg|All files|*.*",
677+
Filter = "Markdown files|*.md;*.markdown;*.txt|HTML files|*.html;*.htm|Email files|*.eml|SVG files|*.svg|All files|*.*",
664678
Multiselect = true
665679
};
666680
if (initialDirectory != null)

MarkdownPointer/MarkdownPointer.App.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<ImplicitUsings>enable</ImplicitUsings>
88
<UseWPF>true</UseWPF>
99
<AssemblyName>mdp</AssemblyName>
10-
<Version>0.14.0</Version>
10+
<Version>0.13.1</Version>
1111

1212
<!-- Single file publish settings -->
1313
<PublishSingleFile>true</PublishSingleFile>
@@ -33,6 +33,7 @@
3333
<ItemGroup>
3434
<PackageReference Include="Markdig" Version="0.40.0" />
3535
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3595.46" />
36+
<PackageReference Include="MimeKit" Version="4.12.0" />
3637
</ItemGroup>
3738

3839
<ItemGroup>

Module/MarkdownPointer.psd1

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@{
22
RootModule = 'MarkdownPointer.psm1'
3-
ModuleVersion = '0.14.0'
3+
ModuleVersion = '0.13.1'
44
GUID = '4c50c9c4-d155-457d-a3a3-e3952253b51d'
55
Author = 'Yoshifumi Tsuda'
66
Copyright = '(c) 2025-2026 Yoshifumi Tsuda. All rights reserved.'
@@ -46,6 +46,21 @@ Example prompts for AI:
4646
LicenseUri = 'https://github.com/yotsuda/MarkdownPointer/blob/master/LICENSE'
4747
ProjectUri = 'https://github.com/yotsuda/MarkdownPointer'
4848
ReleaseNotes = @'
49+
0.13.1
50+
- HTML/HTM file viewing: open and display HTML files directly in WebView2
51+
- EML file viewing: extract and display email HTML body with CSP sandboxing (via MimeKit)
52+
- Right-click code blocks to copy content to clipboard
53+
- Disable pointing mode and slide view for HTML/EML files
54+
- Fix status bar Loading message persisting after tab switch
55+
56+
0.13.0
57+
- Fix crash on image-heavy markdown (temp file instead of NavigateToString)
58+
- Fix off-by-one line number in code block click reporting
59+
- Improve version mismatch error messages with actionable cmdlet guidance
60+
- Replace YouTube iframe embeds with clickable thumbnails in docx/pptx export
61+
- Support inline markdown bold/italic in pptx text rendering
62+
- Fix MSIX detection in Register-MdpToClaudeDesktop
63+
4964
0.12.0
5065
- Mermaid diagrams in slide view: auto-fit, theme matching, text clipping fix
5166
- Slide theme dropdown with alphabetical sorting

work_procedure.md

Lines changed: 0 additions & 70 deletions
This file was deleted.

work_progress.md

Lines changed: 0 additions & 39 deletions
This file was deleted.

0 commit comments

Comments
 (0)