Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d70af24
Adding cornerradius to image
niels9001 Jan 9, 2026
86ce5b6
Making images scale accordingly
niels9001 Jan 9, 2026
0de08e8
Merge branch 'main' into niels9001/mdtb-improvements
niels9001 Jan 9, 2026
0811436
Apply suggestion from @Copilot
Arlodotexe Jan 10, 2026
4cc9798
Add ImageProviderConstraintTest to verify theme constraint behavior
Arlodotexe Jan 10, 2026
db7cf00
Remove references about image corner radius
niels9001 Jan 12, 2026
acb4755
Update MyImage.cs
niels9001 Jan 12, 2026
bcfe6c6
XAML styling
niels9001 Jan 12, 2026
c7fd09a
Fix image scaling
niels9001 Jan 12, 2026
ffcd8ff
Add visual sample for image rescaling
niels9001 Jan 12, 2026
563f4b4
Update components/MarkdownTextBlock/tests/ImageProviderConstraintTest.cs
niels9001 Jan 12, 2026
9e35244
Update MarkdownTextBlockImageProviderSample.xaml
niels9001 Jan 12, 2026
ce2ce57
Merge branch 'niels9001/mdtb-improvements' of https://github.com/Comm…
niels9001 Jan 12, 2026
a2bfa7e
Update MarkdownTextBlockImageProviderSample.xaml.cs
niels9001 Jan 12, 2026
4501564
Update MarkdownTextBlockImageProviderSample.xaml.cs
niels9001 Jan 13, 2026
ad46d46
Change image tag in tests
niels9001 Jan 13, 2026
f93803e
Update MarkdownTextBlock.md
niels9001 Jan 13, 2026
b4fab55
Update MarkdownTextBlockImageProviderSample.xaml.cs
niels9001 Jan 13, 2026
4492759
Update MarkdownTextBlockImageProviderSample.xaml.cs
niels9001 Jan 13, 2026
079b375
Update MarkdownTextBlockImageProviderSample.xaml.cs
niels9001 Jan 13, 2026
cabc532
Revert "Change image tag in tests"
Arlodotexe Jan 13, 2026
44e39a4
Use html syntax for specifying custom image size inline where appropr…
Arlodotexe Jan 13, 2026
5a1d4f7
Fix async void issues
Arlodotexe Jan 13, 2026
b5c2514
Removing sample
niels9001 Jan 13, 2026
585d3d0
Updating more default values
niels9001 Jan 15, 2026
ca31fca
Update MarkdownThemes.cs
niels9001 Jan 15, 2026
277fd43
Update MarkdownThemes.cs
niels9001 Jan 15, 2026
cd6f78c
Merge branch 'main' into niels9001/mdtb-improvements
niels9001 Jan 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions components/MarkdownTextBlock/samples/MarkdownTextBlock.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,4 @@ Try typing markdown and see it rendered in real-time:
## Custom theme sample
Try different styling options for all markdown elements with a custom theme:

> [!Sample MarkdownTextBlockCustomThemeSample]

> [!Sample MarkdownTextBlockCustomThemeSample]
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ This sample demonstrates the **custom theming** capabilities of the `MarkdownTex

The image above automatically scales to respect the max width setting while maintaining its aspect ratio. Text continues to flow seamlessly below it.

![Shortcut Conflict](https://devblogs.microsoft.com/commandline/wp-content/uploads/sites/33/2025/09/ShortcutConflict.png)

## Code Blocks

```csharp
Expand Down
38 changes: 19 additions & 19 deletions components/MarkdownTextBlock/src/MarkdownThemes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ public sealed class MarkdownThemes : DependencyObject

public FontWeight H1FontWeight { get; set; } = FontWeights.SemiBold;

public FontWeight H2FontWeight { get; set; } = FontWeights.Normal;
public FontWeight H2FontWeight { get; set; } = FontWeights.SemiBold;

public FontWeight H3FontWeight { get; set; } = FontWeights.Normal;
public FontWeight H3FontWeight { get; set; } = FontWeights.SemiBold;

public FontWeight H4FontWeight { get; set;} = FontWeights.Normal;
public FontWeight H4FontWeight { get; set;} = FontWeights.SemiBold;

public FontWeight H5FontWeight { get; set; } = FontWeights.Normal;
public FontWeight H5FontWeight { get; set; } = FontWeights.SemiBold;

public FontWeight H6FontWeight { get; set; } = FontWeights.Normal;
public FontWeight H6FontWeight { get; set; } = FontWeights.SemiBold;

public Thickness H1Margin { get; set; } = new(left: 0, top: 16, right: 0, bottom: 0);
public Thickness H2Margin { get; set; } = new(left: 0, top: 16, right: 0, bottom: 0);
Expand All @@ -60,14 +60,14 @@ public sealed class MarkdownThemes : DependencyObject
public Thickness H5Margin { get; set; } = new(left: 0, top: 8, right: 0, bottom: 0);
public Thickness H6Margin { get; set; } = new(left: 0, top: 8, right: 0, bottom: 0);

public Brush BorderBrush { get; set; } = new SolidColorBrush(Colors.Gray);
public Brush BorderBrush { get; set; } = (Brush)Application.Current.Resources["CardStrokeColorDefaultBrush"];

public Brush TableHeadingBackground { get; set; } = Extensions.GetAccentColorBrush(Windows.UI.ViewManagement.UIColorType.AccentLight3);
public Brush TableHeadingBackground { get; set; } = (Brush)Application.Current.Resources["CardBackgroundFillColorDefaultBrush"];

public Brush InlineCodeBackground { get; set; } = (Brush)Application.Current.Resources["ExpanderHeaderBackground"];
public Brush InlineCodeBackground { get; set; } = (Brush)Application.Current.Resources["CardBackgroundFillColorSecondaryBrush"];
public Brush InlineCodeForeground { get; set; } = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"];

public Brush InlineCodeBorderBrush { get; set; } = new SolidColorBrush(Colors.Gray);
public Brush InlineCodeBorderBrush { get; set; } = (Brush)Application.Current.Resources["CardStrokeColorDefaultBrush"];

public Thickness InlineCodeBorderThickness { get; set; } = new (1);

Expand All @@ -79,12 +79,12 @@ public sealed class MarkdownThemes : DependencyObject

public FontWeight InlineCodeFontWeight { get; set; } = FontWeights.Normal;

public FontWeight BoldFontWeight { get; set; } = FontWeights.Bold;
public FontWeight BoldFontWeight { get; set; } = FontWeights.SemiBold;

// Legacy parity properties (new)
// Code block styling
public Brush CodeBlockBackground { get; set; } = (Brush)Application.Current.Resources["ExpanderHeaderBackground"];
public Brush CodeBlockBorderBrush { get; set; } = new SolidColorBrush(Colors.Gray);
public Brush CodeBlockBackground { get; set; } = (Brush)Application.Current.Resources["CardBackgroundFillColorSecondaryBrush"];
public Brush CodeBlockBorderBrush { get; set; } = (Brush)Application.Current.Resources["CardStrokeColorDefaultBrush"];
public Thickness CodeBlockBorderThickness { get; set; } = new Thickness(1);
public Thickness CodeBlockPadding { get; set; } = new Thickness(8);
public Thickness CodeBlockMargin { get; set; } = new Thickness(0, 8, 0, 8);
Expand All @@ -93,23 +93,23 @@ public sealed class MarkdownThemes : DependencyObject
public CornerRadius CodeBlockCornerRadius { get; set; } = new CornerRadius(4);

// Horizontal rule
public Brush HorizontalRuleBrush { get; set; } = new SolidColorBrush(Colors.Gray);
public Brush HorizontalRuleBrush { get; set; } = (Brush)Application.Current.Resources["DividerStrokeColorDefaultBrush"];
public double HorizontalRuleThickness { get; set; } = 1.0;
public Thickness HorizontalRuleMargin { get; set; } = new Thickness(0, 12, 0, 12);

// Link styling
public Brush LinkForeground { get; set; } = (Brush)Application.Current.Resources["AccentTextFillColorPrimaryBrush"] ?? new SolidColorBrush(Colors.DodgerBlue);
public Brush LinkForeground { get; set; } = (Brush)Application.Current.Resources["AccentTextFillColorPrimaryBrush"];

// Paragraph / list
public Thickness ParagraphMargin { get; set; } = new Thickness(0, 8, 0, 8);
public double ParagraphLineHeight { get; set; } = 0; // 0 = default
public double ListBulletSpacing { get; set; } = 4; // spaces after bullet
public double ListGutterWidth { get; set; } = 30; // indent delta per level
public double ListGutterWidth { get; set; } = 32; // indent delta per level
public Thickness ListMargin { get; set; } = new Thickness(0, 4, 0, 4);

// Quote styling
public Brush QuoteBackground { get; set; } = new SolidColorBrush(Colors.Transparent);
public Brush QuoteBorderBrush { get; set; } = new SolidColorBrush(Colors.Gray);
public Brush QuoteBorderBrush { get; set; } = (Brush)Application.Current.Resources["CardStrokeColorDefaultBrush"];
public Thickness QuoteBorderThickness { get; set; } = new Thickness(4, 0, 0, 0);
public Brush QuoteForeground { get; set; } = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"];
public Thickness QuoteMargin { get; set; } = new Thickness(0, 4, 0, 4);
Expand All @@ -122,12 +122,12 @@ public sealed class MarkdownThemes : DependencyObject
public Stretch ImageStretch { get; set; } = Stretch.Uniform;

// Table styling
public Brush TableBorderBrush { get; set; } = new SolidColorBrush(Colors.Gray);
public Brush TableBorderBrush { get; set; } = (Brush)Application.Current.Resources["CardStrokeColorDefaultBrush"];
public double TableBorderThickness { get; set; } = 1;
public Thickness TableCellPadding { get; set; } = new Thickness(4);
public Thickness TableMargin { get; set; } = new Thickness(0, 10, 0, 10);
public Thickness TableMargin { get; set; } = new Thickness(0, 8, 0, 8);

// YAML / not currently used - placeholders for parity
public Brush YamlBorderBrush { get; set; } = new SolidColorBrush(Colors.Gray);
public Brush YamlBorderBrush { get; set; } = (Brush)Application.Current.Resources["CardStrokeColorDefaultBrush"];
public Thickness YamlBorderThickness { get; set; } = new Thickness(1);
}
94 changes: 56 additions & 38 deletions components/MarkdownTextBlock/src/TextElements/MyImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,31 @@ private async void LoadImage(object sender, RoutedEventArgs e)
if (_loaded) return;
try
{
// Track whether we have valid natural dimensions to constrain against
bool hasNaturalWidth = false;
bool hasNaturalHeight = false;

if (_imageProvider != null && _imageProvider.ShouldUseThisProvider(_uri.AbsoluteUri))
{
_image = await _imageProvider.GetImage(_uri.AbsoluteUri);
_container.Child = _image;

// Capture natural dimensions as max constraints from the provider image
// Then clear fixed Width/Height so images can shrink responsively
if (_image.Width > 0 && !double.IsNaN(_image.Width) && !double.IsInfinity(_image.Width))
{
_image.MaxWidth = _image.Width;
_image.Width = double.NaN; // Clear fixed width to allow shrinking
hasNaturalWidth = true;
}
if (_image.Height > 0 && !double.IsNaN(_image.Height) && !double.IsInfinity(_image.Height))
{
_image.MaxHeight = _image.Height;
_image.Height = double.NaN; // Clear fixed height to allow shrinking
hasNaturalHeight = true;
}

_loaded = true;
}
else
{
Expand Down Expand Up @@ -137,53 +158,50 @@ private async void LoadImage(object sender, RoutedEventArgs e)
await bitmap.SetSourceAsync(stream);
}
_image.Source = bitmap;
_image.Width = bitmap.PixelWidth == 0 ? bitmap.DecodePixelWidth : bitmap.PixelWidth;
_image.Height = bitmap.PixelHeight == 0 ? bitmap.DecodePixelHeight : bitmap.PixelHeight;
// Don't set fixed Width/Height - let layout system handle it
// Store natural dimensions for MaxWidth/MaxHeight constraints
double naturalWidth = bitmap.PixelWidth == 0 ? bitmap.DecodePixelWidth : bitmap.PixelWidth;
double naturalHeight = bitmap.PixelHeight == 0 ? bitmap.DecodePixelHeight : bitmap.PixelHeight;

// Use natural size as max constraint so image doesn't upscale
if (naturalWidth > 0)
{
_image.MaxWidth = naturalWidth;
hasNaturalWidth = true;
}
if (naturalHeight > 0)
{
_image.MaxHeight = naturalHeight;
hasNaturalHeight = true;
}
}

_loaded = true;
}

// Determine the actual image dimensions
double actualWidth = _precedentWidth != 0 ? _precedentWidth : _image.Width;
double actualHeight = _precedentHeight != 0 ? _precedentHeight : _image.Height;

// Apply max constraints and calculate the final size
// When using Uniform stretch with max constraints, we need to calculate
// the actual rendered size to avoid gaps
double finalWidth = actualWidth;
double finalHeight = actualHeight;

bool hasMaxWidth = _themes.ImageMaxWidth > 0;
bool hasMaxHeight = _themes.ImageMaxHeight > 0;

if (hasMaxWidth || hasMaxHeight)
// Apply precedent (markdown-specified) dimensions if provided
// Precedent always takes priority and sets a known dimension
if (_precedentWidth != 0)
{
double scaleX = hasMaxWidth && actualWidth > _themes.ImageMaxWidth
? _themes.ImageMaxWidth / actualWidth
: 1.0;
double scaleY = hasMaxHeight && actualHeight > _themes.ImageMaxHeight
? _themes.ImageMaxHeight / actualHeight
: 1.0;

// For Uniform stretch, use the smaller scale to maintain aspect ratio
if (_themes.ImageStretch == Stretch.Uniform || _themes.ImageStretch == Stretch.UniformToFill)
{
double uniformScale = Math.Min(scaleX, scaleY);
finalWidth = actualWidth * uniformScale;
finalHeight = actualHeight * uniformScale;
}
else
{
// For other stretch modes, apply constraints independently
finalWidth = actualWidth * scaleX;
finalHeight = actualHeight * scaleY;
}
_image.MaxWidth = _precedentWidth;
hasNaturalWidth = true;
}
if (_precedentHeight != 0)
{
_image.MaxHeight = _precedentHeight;
hasNaturalHeight = true;
}

_image.Width = finalWidth;
_image.Height = finalHeight;
// Apply theme constraints - only if we have a known dimension to constrain
// This prevents theme constraints from enlarging images with unknown natural size
if (_themes.ImageMaxWidth > 0 && hasNaturalWidth && _themes.ImageMaxWidth < _image.MaxWidth)
{
_image.MaxWidth = _themes.ImageMaxWidth;
}
if (_themes.ImageMaxHeight > 0 && hasNaturalHeight && _themes.ImageMaxHeight < _image.MaxHeight)
{
_image.MaxHeight = _themes.ImageMaxHeight;
}
_image.Stretch = _themes.ImageStretch;
}
catch (Exception) { }
Expand Down
Loading
Loading