Files
marechai/Marechai.App/Presentation/Views/GpuDetailPage.xaml
Natalia Portillo 96b211aa77 Fix markdown rendering in Marechai.App via native XAML renderer
MarkdownTextBlock crashed with a NullReferenceException in ThemeListener
under Uno, and the WebView2-based fallback used by description pages
doesn't composite on Linux desktop. Render markdown directly to XAML
elements (TextBlock/Span/Hyperlink/Image trees) via Markdig's AST instead,
shared by MessageMarkdownView and all entity description pages, with no
embedded browser dependency on any platform.
2026-06-30 14:16:22 +01:00

592 lines
37 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<Page x:Class="Marechai.App.Presentation.Views.GpuDetailPage"
x:Name="PageRoot"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="using:Marechai.App.Presentation.Converters"
xmlns:local="using:Marechai.App.Presentation.Views"
xmlns:utu="using:Uno.Toolkit.UI"
xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls"
NavigationCacheMode="Required"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
</Page.Resources>
<Grid utu:SafeArea.Insets="VisibleBounds">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Header -->
<utu:NavigationBar Grid.Row="0"
Content="{Binding Path=Title}"
MainCommandMode="Action">
<utu:NavigationBar.MainCommand>
<AppBarButton Label="{Binding Source={StaticResource Strings}, Path=BackButton}" Icon="Back"
Command="{Binding GoBackCommand}"
AutomationProperties.Name="Go back" />
</utu:NavigationBar.MainCommand>
</utu:NavigationBar>
<!-- Content -->
<Grid Grid.Row="1">
<!-- Loading State -->
<StackPanel Visibility="{Binding IsLoading}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Padding="32"
Spacing="16">
<ProgressRing IsActive="True"
IsIndeterminate="True"
Height="48"
Width="48" />
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=LoadingText}"
TextAlignment="Center"
FontSize="14" />
</StackPanel>
<!-- Error State -->
<StackPanel Visibility="{Binding HasError}"
Padding="16"
Background="{ThemeResource SystemErrorBackgroundColor}"
CornerRadius="8"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=ErrorTitleText}"
FontWeight="Bold"
Foreground="{ThemeResource SystemErrorTextForegroundColor}" />
<TextBlock Text="{Binding ErrorMessage}"
Foreground="{ThemeResource SystemErrorTextForegroundColor}"
TextWrapping="Wrap" />
</StackPanel>
<!-- GPU Details -->
<Grid Visibility="{Binding IsDataLoaded}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- GPU Name -->
<TextBlock Grid.Row="0"
Text="{Binding DisplayName}"
FontSize="28"
FontWeight="Bold"
TextWrapping="Wrap"
Margin="16,16,16,0" />
<TabView Grid.Row="1"
Style="{StaticResource DetailTabViewStyle}">
<TabViewItem Style="{StaticResource DetailTabViewItemStyle}"
Header="{Binding Source={StaticResource Strings}, Path=SpecificationsTabHeader}">
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<StackPanel Padding="16"
Spacing="16">
<StackPanel Visibility="{Binding ShowSpecifications}"
Spacing="16">
<!-- Company/Manufacturer -->
<StackPanel Style="{StaticResource MetadataPanelStyle}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=ManufacturerText}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding ManufacturerName}"
FontSize="14"
TextWrapping="Wrap" />
</StackPanel>
<!-- Model Code -->
<StackPanel Visibility="{Binding Gpu.ModelCode, Converter={StaticResource StringToVisibilityConverter}}"
Style="{StaticResource MetadataPanelStyle}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=ModelCodeText}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding Gpu.ModelCode}"
FontSize="14"
TextWrapping="Wrap" />
</StackPanel>
<!-- Introduced Date -->
<StackPanel Visibility="{Binding Gpu.Introduced, Converter={StaticResource ObjectToVisibilityConverter}}"
Style="{StaticResource MetadataPanelStyle}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=IntroducedText}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding Gpu.Introduced}"
FontSize="14" />
</StackPanel>
<!-- Package -->
<StackPanel Visibility="{Binding Gpu.Package, Converter={StaticResource StringToVisibilityConverter}}"
Style="{StaticResource MetadataPanelStyle}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=PackageText}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding Gpu.Package}"
FontSize="14"
TextWrapping="Wrap" />
</StackPanel>
<!-- Process -->
<StackPanel Visibility="{Binding Gpu.Process, Converter={StaticResource StringToVisibilityConverter}}"
Style="{StaticResource MetadataPanelStyle}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=ProcessText}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding Gpu.Process}"
FontSize="14"
TextWrapping="Wrap" />
</StackPanel>
<!-- Process (nm) -->
<StackPanel Visibility="{Binding Gpu.ProcessNm, Converter={StaticResource StringToVisibilityConverter}}"
Style="{StaticResource MetadataPanelStyle}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=ProcessNmText}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding Gpu.ProcessNm}"
FontSize="14"
TextWrapping="Wrap" />
</StackPanel>
<!-- Die Size -->
<StackPanel Visibility="{Binding Gpu.DieSize, Converter={StaticResource ObjectToVisibilityConverter}}"
Style="{StaticResource MetadataPanelStyle}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=DieSizeText}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding Gpu.DieSize}"
FontSize="14"
TextWrapping="Wrap" />
</StackPanel>
<!-- Transistors -->
<StackPanel Visibility="{Binding Gpu.Transistors, Converter={StaticResource ObjectToVisibilityConverter}}"
Style="{StaticResource MetadataPanelStyle}"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=TransistorsText}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding Gpu.Transistors}"
FontSize="14"
TextWrapping="Wrap" />
</StackPanel>
<!-- Resolutions Section -->
<StackPanel Visibility="{Binding Resolutions.Count, Converter={StaticResource ZeroToVisibilityConverter}}"
Spacing="8">
<StackPanel Orientation="Horizontal"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=SupportedResolutionsText}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding Resolutions.Count}"
FontSize="14"
FontWeight="Bold"
Foreground="{ThemeResource SystemAccentColor}" />
</StackPanel>
<!-- Scrollable Resolutions List -->
<ScrollViewer Height="200"
BorderThickness="1"
BorderBrush="{ThemeResource ControlElevationBorderBrush}"
CornerRadius="8">
<ItemsRepeater ItemsSource="{Binding Resolutions}"
Margin="0">
<ItemsRepeater.Layout>
<StackLayout Spacing="4" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate>
<Border Padding="12,8"
HorizontalAlignment="Stretch"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
CornerRadius="4"
Margin="0,4">
<StackPanel Spacing="4">
<TextBlock Text="{Binding ResolutionDisplay}"
FontSize="14"
FontWeight="SemiBold" />
<TextBlock Text="{Binding ColorDisplay}"
FontSize="12"
Foreground="{ThemeResource SystemBaseMediumColor}" />
</StackPanel>
</Border>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</ScrollViewer>
</StackPanel>
</StackPanel>
<!-- Description Section -->
<StackPanel Visibility="{Binding ShowDescription}"
Spacing="8"
Margin="0,16,0,0">
<TextBlock Text="{Binding Source={StaticResource Strings}, Path=DescriptionSectionTitle}"
FontSize="20"
FontWeight="SemiBold" />
<local:MessageMarkdownView Markdown="{Binding DescriptionMarkdown}" />
</StackPanel>
</StackPanel>
</ScrollViewer>
</TabViewItem>
<TabViewItem Style="{StaticResource DetailTabViewItemStyle}"
Header="{Binding Source={StaticResource Strings}, Path=MediaTabHeader}">
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<StackPanel Padding="16"
Spacing="16">
<!-- Photos Carousel -->
<StackPanel Visibility="{Binding ShowPhotos}"
Spacing="12">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=PhotosLabel}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource TextControlForeground}" />
<controls:Carousel ItemsSource="{Binding Photos}"
Height="280"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="8"
HorizontalAlignment="Stretch">
<controls:Carousel.ItemTemplate>
<DataTemplate>
<Button Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderThickness="0"
Padding="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding ElementName=PageRoot, Path=DataContext.SelectPhotoCommand}"
CommandParameter="{Binding PhotoId}">
<Image Source="{Binding ThumbnailImageSource}"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center"
MaxWidth="256"
MaxHeight="256" />
</Button>
</DataTemplate>
</controls:Carousel.ItemTemplate>
</controls:Carousel>
</StackPanel>
<!-- Videos -->
<StackPanel Visibility="{Binding ShowVideos}"
Spacing="12">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=VideosLabel}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource TextControlForeground}" />
<ItemsControl ItemsSource="{Binding Videos}"
HorizontalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"
Spacing="0"
HorizontalAlignment="Stretch" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Padding="0"
Margin="0,0,0,8"
MinHeight="92"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
HorizontalAlignment="Stretch"
Command="{Binding DataContext.OpenVideoCommand, ElementName=PageRoot}"
CommandParameter="{Binding}"
Background="Transparent"
BorderThickness="0">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid MinHeight="92"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Border x:Name="VideoShadowBorder"
Style="{StaticResource MetadataCardStyle}"
Translation="0, 0, 4"
VerticalAlignment="Stretch">
<Border.Shadow>
<ThemeShadow />
</Border.Shadow>
<Grid ColumnSpacing="16">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0"
Height="68"
CornerRadius="6"
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
VerticalAlignment="Center">
<Image Source="{Binding ThumbnailUrl}"
Stretch="UniformToFill"
Visibility="{Binding HasThumbnail, Converter={StaticResource BoolToVisibilityConverter}}" />
<FontIcon Glyph="&#xE768;"
FontSize="26"
Foreground="{ThemeResource SystemAccentColor}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{Binding HasThumbnail, Converter={StaticResource InvertBoolToVisibilityConverter}}" />
</Grid>
<StackPanel Grid.Column="1"
Spacing="4"
VerticalAlignment="Center"
HorizontalAlignment="Stretch">
<TextBlock Text="{Binding DisplayTitle}"
FontSize="15"
FontWeight="SemiBold"
Foreground="{ThemeResource TextControlForeground}"
TextWrapping="WrapWholeWords"
MaxLines="2"
TextTrimming="CharacterEllipsis" />
<TextBlock Text="{Binding Provider}"
FontSize="12"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding VideoId}"
FontSize="11"
Foreground="{ThemeResource SystemBaseMediumColor}"
TextTrimming="CharacterEllipsis" />
</StackPanel>
<StackPanel Grid.Column="2"
Spacing="6"
VerticalAlignment="Center">
<FontIcon Glyph="&#xE8A7;"
FontSize="18"
HorizontalAlignment="Center"
Foreground="{ThemeResource SystemAccentColor}" />
<TextBlock Text="{Binding Source={StaticResource Strings}, Path=OpenVideoButton}"
FontSize="11"
Foreground="{ThemeResource SystemAccentColor}"
HorizontalAlignment="Center" />
</StackPanel>
</Grid>
</Border>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="VideoShadowBorder.Background"
Value="{ThemeResource CardBackgroundFillColorSecondaryBrush}" />
<Setter Target="VideoShadowBorder.Translation"
Value="0, -2, 8" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Target="VideoShadowBorder.Background"
Value="{ThemeResource CardBackgroundFillColorTertiaryBrush}" />
<Setter Target="VideoShadowBorder.Translation"
Value="0, 0, 2" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</StackPanel>
</ScrollViewer>
</TabViewItem>
<TabViewItem Style="{StaticResource DetailTabViewItemStyle}"
Header="{Binding Source={StaticResource Strings}, Path=MachinesTabHeader}">
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<StackPanel Padding="16"
Spacing="16">
<!-- Computers Section -->
<StackPanel Visibility="{Binding HasComputers}"
Spacing="8">
<StackPanel Orientation="Horizontal"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=ComputersText}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding Computers.Count}"
FontSize="14"
FontWeight="Bold"
Foreground="{ThemeResource SystemAccentColor}" />
</StackPanel>
<!-- Filter Box -->
<AutoSuggestBox
PlaceholderText="{Binding Source={StaticResource Strings}, Path=ComputersSearchBox_Placeholder}"
Text="{Binding ComputersFilterText, Mode=TwoWay}"
TextChanged="ComputersSearchBox_TextChanged"
BorderThickness="1"
BorderBrush="{ThemeResource ControlElevationBorderBrush}" />
<!-- Scrollable Computers List -->
<ScrollViewer Height="200"
BorderThickness="1"
BorderBrush="{ThemeResource ControlElevationBorderBrush}"
CornerRadius="8">
<ItemsRepeater ItemsSource="{Binding FilteredComputers}"
Margin="0">
<ItemsRepeater.Layout>
<StackLayout Spacing="4" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate>
<Button Click="Computer_Click"
Tag="{Binding Id}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="12"
Margin="0,4">
<StackPanel Spacing="4"
HorizontalAlignment="Left">
<TextBlock Text="{Binding Name}"
FontSize="14"
FontWeight="SemiBold" />
<StackPanel Orientation="Horizontal"
Spacing="8">
<TextBlock Text="{Binding Manufacturer}"
FontSize="12"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding YearDisplay}"
FontSize="12"
Foreground="{ThemeResource SystemBaseMediumColor}" />
</StackPanel>
</StackPanel>
</Button>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</ScrollViewer>
</StackPanel>
<!-- Consoles Section -->
<StackPanel Visibility="{Binding HasConsoles}"
Spacing="8">
<StackPanel Orientation="Horizontal"
Spacing="8">
<TextBlock
Text="{Binding Source={StaticResource Strings}, Path=ConsolesText}"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding Consoles.Count}"
FontSize="14"
FontWeight="Bold"
Foreground="{ThemeResource SystemAccentColor}" />
</StackPanel>
<!-- Filter Box -->
<AutoSuggestBox
PlaceholderText="{Binding Source={StaticResource Strings}, Path=ConsolesSearchBox_Placeholder}"
Text="{Binding ConsolesFilterText, Mode=TwoWay}"
TextChanged="ConsolesSearchBox_TextChanged"
BorderThickness="1"
BorderBrush="{ThemeResource ControlElevationBorderBrush}" />
<!-- Scrollable Consoles List -->
<ScrollViewer Height="200"
BorderThickness="1"
BorderBrush="{ThemeResource ControlElevationBorderBrush}"
CornerRadius="8">
<ItemsRepeater ItemsSource="{Binding FilteredConsoles}"
Margin="0">
<ItemsRepeater.Layout>
<StackLayout Spacing="4" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate>
<Button Click="Console_Click"
Tag="{Binding Id}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Padding="12"
Margin="0,4">
<StackPanel Spacing="4"
HorizontalAlignment="Left">
<TextBlock Text="{Binding Name}"
FontSize="14"
FontWeight="SemiBold" />
<StackPanel Orientation="Horizontal"
Spacing="8">
<TextBlock Text="{Binding Manufacturer}"
FontSize="12"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding YearDisplay}"
FontSize="12"
Foreground="{ThemeResource SystemBaseMediumColor}" />
</StackPanel>
</StackPanel>
</Button>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</ScrollViewer>
</StackPanel>
</StackPanel>
</ScrollViewer>
</TabViewItem>
</TabView>
</Grid>
</Grid>
</Grid>
</Page>