Files
PalladiumWallet/src/App/Views/MainView.axaml
T
davide 34b4313a36 feat(app): check GitHub releases on startup and prompt for updates
Compares the running assembly version against the latest GitHub release
tag on every app launch (desktop and Android) and shows an in-app
overlay with the new version tag when one is available. Best-effort:
network/parse failures are silent, matching the app's existing
non-blocking startup checks.
2026-07-02 21:33:18 +02:00

1672 lines
114 KiB
XML

<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:PalladiumWallet.App.ViewModels"
xmlns:net="using:PalladiumWallet.Core.Net"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="620"
x:Class="PalladiumWallet.App.Views.MainView"
x:DataType="vm:MainWindowViewModel">
<Design.DataContext>
<vm:MainWindowViewModel/>
</Design.DataContext>
<UserControl.Styles>
<!-- On desktop, overlay cards are centred with a max size;
on mobile the default (stretch, no margin) fills the whole screen. -->
<Style Selector="Border.desktop-overlay">
<Setter Property="MaxWidth" Value="540"/>
<Setter Property="MaxHeight" Value="540"/>
<Setter Property="Margin" Value="16"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="CornerRadius" Value="8"/>
</Style>
</UserControl.Styles>
<Grid RowDefinitions="Auto,*,Auto" Background="{DynamicResource AppBackgroundBrush}">
<!-- ============ MENU (Electrum style) ============ -->
<Menu Grid.Row="0" IsVisible="{Binding IsDesktop}">
<MenuItem Header="{Binding Loc[menu.file]}">
<MenuItem Header="{Binding Loc[menu.file.new]}" Command="{Binding NewWalletCommand}"/>
<Separator/>
<MenuItem Header="{Binding Loc[menu.file.close]}" Command="{Binding CloseWalletCommand}"
IsEnabled="{Binding IsWalletOpen}"/>
<Separator/>
<MenuItem Header="{Binding Loc[menu.file.quit]}" Command="{Binding QuitAppCommand}"/>
</MenuItem>
<MenuItem Header="{Binding Loc[menu.wallet]}"
Command="{Binding OpenWalletInfoCommand}"
IsVisible="{Binding IsWalletOpen}"/>
<MenuItem Header="{Binding Loc[menu.settings]}"
Command="{Binding OpenSettingsCommand}"/>
<MenuItem Header="{Binding Loc[menu.help]}"
Command="{Binding OpenHelpCommand}"/>
</Menu>
<!-- Settings/Help buttons — mobile only (the menu is hidden on mobile) -->
<StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="8"
HorizontalAlignment="Right" Margin="8,4"
IsVisible="{Binding IsMobile}">
<Button Content="{Binding Loc[menu.wallet]}"
Command="{Binding OpenWalletInfoCommand}"
IsVisible="{Binding IsWalletOpen}"/>
<Button Content="{Binding Loc[menu.settings]}" Command="{Binding OpenSettingsCommand}"/>
<Button Content="{Binding Loc[menu.help]}" Command="{Binding OpenHelpCommand}"/>
</StackPanel>
<!-- ============ SETUP WIZARD (§15): one step at a time ============ -->
<ScrollViewer Grid.Row="1" IsVisible="{Binding IsSetupVisible}">
<StackPanel MaxWidth="560" Margin="24,40" Spacing="18"
HorizontalAlignment="Center">
<TextBlock Text="Palladium Wallet" FontSize="28" FontWeight="Bold"
HorizontalAlignment="Center"/>
<!-- Step 0 (first launch): where to store data -->
<StackPanel IsVisible="{Binding IsStepDataLocation}" Spacing="12">
<TextBlock Text="{Binding Loc[wiz.data.title]}" FontSize="18" FontWeight="Bold"/>
<TextBlock Text="{Binding Loc[wiz.data.info]}" TextWrapping="Wrap" Foreground="{DynamicResource TextSecondaryBrush}"/>
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[wiz.data.default]}" FontSize="11" Foreground="{DynamicResource TextSecondaryBrush}"/>
<SelectableTextBlock Text="{Binding DefaultDataPath}"
FontFamily="monospace" FontSize="13" TextWrapping="Wrap"/>
</StackPanel>
<Button Content="{Binding Loc[wiz.data.usedefault]}" FontSize="16" Classes="accent"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
Command="{Binding UseDefaultDataLocationCommand}"/>
<Button Content="{Binding Loc[wiz.data.choose]}" FontSize="16"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
Click="OnChooseDataFolderClick"/>
</StackPanel>
<!-- Step 1: initial choice -->
<StackPanel IsVisible="{Binding IsStepStart}" Spacing="12">
<Button Content="{Binding Loc[wiz.open.btn]}" FontSize="16"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
IsVisible="{Binding WalletFileExists}"
Command="{Binding WizardStartOpenCommand}"/>
<Button Content="{Binding Loc[wiz.new.btn]}" FontSize="16"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
Command="{Binding WizardStartNewCommand}"/>
<Button Content="{Binding Loc[wiz.restore.btn]}" FontSize="16"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
Command="{Binding WizardStartRestoreCommand}"/>
<Button Content="{Binding Loc[wiz.importxkey.btn]}" FontSize="16"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
Command="{Binding WizardStartImportXkeyCommand}"/>
<Button Content="{Binding Loc[wiz.importwif.btn]}" FontSize="16"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
Command="{Binding WizardStartImportWifCommand}"/>
</StackPanel>
<!-- Step: choose wallet (multiple files present) -->
<StackPanel IsVisible="{Binding IsStepChooseWallet}" Spacing="12">
<TextBlock Text="{Binding Loc[wiz.choose.title]}" FontSize="18" FontWeight="Bold"/>
<ItemsControl ItemsSource="{Binding WalletList}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:WalletFileEntry">
<Button Content="{Binding Name}" FontFamily="monospace"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Left"
Margin="0,0,0,6"
Command="{Binding $parent[ItemsControl].((vm:MainWindowViewModel)DataContext).ChooseWalletCommand}"
CommandParameter="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="{Binding Loc[wiz.back]}" Command="{Binding WizardBackCommand}"/>
</StackPanel>
<!-- Step: password for existing wallet -->
<StackPanel IsVisible="{Binding IsStepOpen}" Spacing="12">
<TextBlock Text="{Binding Loc[wiz.open.title]}" FontSize="18" FontWeight="Bold"/>
<TextBox PlaceholderText="{Binding Loc[wiz.open.placeholder]}"
PasswordChar="●" Text="{Binding PasswordInput}"/>
<StackPanel Orientation="Horizontal" Spacing="10">
<Button Content="{Binding Loc[wiz.back]}" Command="{Binding WizardBackCommand}"/>
<Button Content="{Binding Loc[wiz.open.ok]}" Classes="accent"
Command="{Binding OpenExistingCommand}"/>
</StackPanel>
</StackPanel>
<!-- Step: show the new seed -->
<StackPanel IsVisible="{Binding IsStepShowSeed}" Spacing="12">
<TextBlock Text="{Binding Loc[wiz.seed.title]}" FontSize="18" FontWeight="Bold"/>
<Border BorderBrush="{DynamicResource SystemAccentColor}" BorderThickness="1"
CornerRadius="6" Padding="14">
<SelectableTextBlock Text="{Binding MnemonicInput}"
FontFamily="monospace" FontSize="16" TextWrapping="Wrap"/>
</Border>
<TextBlock Foreground="{DynamicResource WarningBrush}" TextWrapping="Wrap"
Text="{Binding Loc[wiz.seed.warning]}"/>
<StackPanel Orientation="Horizontal" Spacing="10">
<Button Content="{Binding Loc[wiz.back]}" Command="{Binding WizardBackCommand}"/>
<Button Content="{Binding Loc[wiz.seed.next]}" Classes="accent"
Command="{Binding WizardNextFromShowSeedCommand}"/>
</StackPanel>
</StackPanel>
<!-- Step: seed confirmation -->
<StackPanel IsVisible="{Binding IsStepConfirmSeed}" Spacing="12">
<TextBlock Text="{Binding Loc[wiz.confirm.title]}" FontSize="18" FontWeight="Bold"/>
<TextBox PlaceholderText="{Binding Loc[wiz.confirm.placeholder]}"
AcceptsReturn="False" Text="{Binding ConfirmMnemonicInput}"/>
<StackPanel Orientation="Horizontal" Spacing="10">
<Button Content="{Binding Loc[wiz.back]}" Command="{Binding WizardBackCommand}"/>
<Button Content="{Binding Loc[wiz.next]}" Classes="accent"
Command="{Binding WizardNextFromConfirmSeedCommand}"/>
</StackPanel>
</StackPanel>
<!-- Step: enter seed words (restore) -->
<StackPanel IsVisible="{Binding IsStepWords}" Spacing="12">
<TextBlock Text="{Binding Loc[wiz.words.title]}" FontSize="18" FontWeight="Bold"/>
<TextBox PlaceholderText="{Binding Loc[wiz.words.placeholder]}"
AcceptsReturn="False" Text="{Binding MnemonicInput}"/>
<StackPanel Orientation="Horizontal" Spacing="10">
<Button Content="{Binding Loc[wiz.back]}" Command="{Binding WizardBackCommand}"/>
<Button Content="{Binding Loc[wiz.next]}" Classes="accent"
Command="{Binding WizardNextFromWordsCommand}"/>
</StackPanel>
</StackPanel>
<!-- Step: optional passphrase -->
<StackPanel IsVisible="{Binding IsStepPassphrase}" Spacing="12">
<TextBlock Text="{Binding Loc[wiz.passphrase.title]}" FontSize="18" FontWeight="Bold"/>
<TextBox PlaceholderText="{Binding Loc[wiz.passphrase.placeholder]}"
Text="{Binding PassphraseInput}"/>
<StackPanel Orientation="Horizontal" Spacing="10">
<Button Content="{Binding Loc[wiz.back]}" Command="{Binding WizardBackCommand}"/>
<Button Content="{Binding Loc[wiz.next]}" Classes="accent"
Command="{Binding WizardNextFromPassphraseCommand}"/>
</StackPanel>
</StackPanel>
<!-- Step: import extended xpub/xprv (SLIP-132) -->
<StackPanel IsVisible="{Binding IsStepImportXkey}" Spacing="12">
<TextBlock Text="{Binding Loc[wiz.importxkey.title]}" FontSize="18" FontWeight="Bold"/>
<TextBlock Text="{Binding Loc[wiz.importxkey.hint]}" TextWrapping="Wrap" Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBox PlaceholderText="{Binding Loc[wiz.importxkey.placeholder]}"
Text="{Binding ImportXkeyInput}" AcceptsReturn="False"/>
<TextBlock Text="{Binding ImportXkeyDetectedKind}"
IsVisible="{Binding ImportXkeyDetectedKind, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
Foreground="{DynamicResource SuccessBrush}" FontSize="12"/>
<StackPanel Orientation="Horizontal" Spacing="10">
<Button Content="{Binding Loc[wiz.back]}" Command="{Binding WizardBackCommand}"/>
<Button Content="{Binding Loc[wiz.next]}" Classes="accent"
Command="{Binding WizardNextFromImportXkeyCommand}"/>
</StackPanel>
</StackPanel>
<!-- Step: import single WIF key -->
<StackPanel IsVisible="{Binding IsStepImportWif}" Spacing="12">
<TextBlock Text="{Binding Loc[wiz.importwif.title]}" FontSize="18" FontWeight="Bold"/>
<TextBlock Text="{Binding Loc[wiz.importwif.hint]}" TextWrapping="Wrap" Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBox PlaceholderText="{Binding Loc[wiz.importwif.placeholder]}"
Text="{Binding ImportWifInput}" AcceptsReturn="True" Height="80"/>
<StackPanel Orientation="Horizontal" Spacing="10">
<Button Content="{Binding Loc[wiz.back]}" Command="{Binding WizardBackCommand}"/>
<Button Content="{Binding Loc[wiz.next]}" Classes="accent"
Command="{Binding WizardNextFromImportWifCommand}"/>
</StackPanel>
</StackPanel>
<!-- Step: choose script/address type -->
<StackPanel IsVisible="{Binding IsStepScriptType}" Spacing="12">
<TextBlock Text="{Binding Loc[wiz.scripttype.title]}" FontSize="18" FontWeight="Bold"/>
<TextBlock Text="{Binding Loc[wiz.scripttype.hint]}" TextWrapping="Wrap" Foreground="{DynamicResource TextSecondaryBrush}"/>
<RadioButton GroupName="ScriptType"
IsChecked="{Binding IsLegacySelected, Mode=OneWay}"
Command="{Binding SelectLegacyCommand}">
<StackPanel Spacing="2">
<TextBlock Text="Legacy (P2PKH)" FontWeight="SemiBold"/>
<TextBlock Text="{Binding Loc[wiz.scripttype.legacy.desc]}" Foreground="{DynamicResource TextSecondaryBrush}" FontSize="12"/>
</StackPanel>
</RadioButton>
<RadioButton GroupName="ScriptType"
IsChecked="{Binding IsWrappedSegwitSelected, Mode=OneWay}"
Command="{Binding SelectWrappedSegwitCommand}">
<StackPanel Spacing="2">
<TextBlock Text="Wrapped SegWit (P2SH-P2WPKH)" FontWeight="SemiBold"/>
<TextBlock Text="{Binding Loc[wiz.scripttype.wrapped.desc]}" Foreground="{DynamicResource TextSecondaryBrush}" FontSize="12"/>
</StackPanel>
</RadioButton>
<RadioButton GroupName="ScriptType"
IsChecked="{Binding IsNativeSegwitSelected, Mode=OneWay}"
Command="{Binding SelectNativeSegwitCommand}">
<StackPanel Spacing="2">
<TextBlock Text="Native SegWit (P2WPKH)" FontWeight="SemiBold"/>
<TextBlock Text="{Binding Loc[wiz.scripttype.native.desc]}" Foreground="{DynamicResource TextSecondaryBrush}" FontSize="12"/>
</StackPanel>
</RadioButton>
<RadioButton GroupName="ScriptType"
IsChecked="{Binding IsTaprootSelected, Mode=OneWay}"
Command="{Binding SelectTaprootCommand}">
<StackPanel Spacing="2">
<TextBlock Text="Taproot (P2TR)" FontWeight="SemiBold"/>
<TextBlock Text="{Binding Loc[wiz.scripttype.taproot.desc]}" Foreground="{DynamicResource TextSecondaryBrush}" FontSize="12"/>
</StackPanel>
</RadioButton>
<StackPanel Orientation="Horizontal" Spacing="10">
<Button Content="{Binding Loc[wiz.back]}" Command="{Binding WizardBackCommand}"/>
<Button Content="{Binding Loc[wiz.next]}" Classes="accent"
Command="{Binding WizardNextFromScriptTypeCommand}"/>
</StackPanel>
</StackPanel>
<!-- Final step: wallet name + file password (encryption, Electrum style) -->
<StackPanel IsVisible="{Binding IsStepPassword}" Spacing="16">
<TextBlock Text="{Binding Loc[wiz.password.title]}" FontSize="18" FontWeight="Bold"/>
<!-- Name section: visually distinct from the password group -->
<Border BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="0,0,0,0"
Background="{DynamicResource SurfaceAltBrush}"
CornerRadius="6" Padding="12,10">
<StackPanel Spacing="6">
<TextBlock Text="{Binding Loc[wiz.name.label]}"
FontWeight="SemiBold" FontSize="13"/>
<TextBox PlaceholderText="{Binding Loc[wiz.name.placeholder]}"
Text="{Binding WalletNameInput}"/>
</StackPanel>
</Border>
<!-- Visual separator -->
<Separator Margin="0,0"/>
<!-- Encryption section -->
<StackPanel Spacing="8">
<TextBox PlaceholderText="{Binding Loc[wiz.password.placeholder]}"
PasswordChar="●" Text="{Binding PasswordInput}"/>
<TextBox PlaceholderText="{Binding Loc[wiz.password.confirm]}"
PasswordChar="●" Text="{Binding ConfirmPasswordInput}"/>
<CheckBox Content="{Binding Loc[wiz.password.encrypt]}"
IsChecked="{Binding EncryptWallet}"/>
<TextBlock Text="{Binding Loc[wiz.password.encrypt.hint]}"
Foreground="{DynamicResource TextSecondaryBrush}" FontSize="12" TextWrapping="Wrap"
IsVisible="{Binding !EncryptWallet}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="10">
<Button Content="{Binding Loc[wiz.back]}" Command="{Binding WizardBackCommand}"/>
<Button Content="{Binding Loc[wiz.password.create]}" Classes="accent"
Command="{Binding CreateOrRestoreCommand}"/>
</StackPanel>
</StackPanel>
</StackPanel>
</ScrollViewer>
<!-- ============ WALLET PANEL ============ -->
<Grid Grid.Row="1" RowDefinitions="Auto,*" IsVisible="{Binding IsWalletOpen}" Margin="16">
<!-- Header: balance card. Connect/sync is automatic; close wallet is in File.
Connection status is shown in the bottom bar. -->
<Border Grid.Row="0" Classes="balance-hero" Margin="0,0,0,16">
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[addr.balance]}" Classes="label on-hero"/>
<TextBlock Text="{Binding BalanceText}" Classes="amount"/>
<TextBlock Text="{Binding UnconfirmedText}" Foreground="#FCD34D"
TextWrapping="Wrap"
IsVisible="{Binding UnconfirmedText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<TextBlock Text="{Binding ImmatureText}" Foreground="#FCD34D"
TextWrapping="Wrap"
IsVisible="{Binding ImmatureText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<TextBlock Text="{Binding NetworkInfo}" Classes="on-hero" FontSize="12"
Margin="0,2,0,0"/>
</StackPanel>
</Border>
<!-- Tabs: History / Send / Receive / Addresses / Contacts -->
<TabControl Grid.Row="1"
SelectedIndex="{Binding SelectedTabIndex}"
TabStripPlacement="Top">
<TabControl.Styles>
<Style Selector="TabStrip">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Background" Value="{DynamicResource SurfaceBrush}"/>
<Setter Property="ItemsPanel">
<ItemsPanelTemplate>
<UniformGrid Rows="1" Columns="5"/>
</ItemsPanelTemplate>
</Setter>
</Style>
<Style Selector="TabItem">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="8,12"/>
</Style>
</TabControl.Styles>
<!-- 1. History -->
<TabItem>
<TabItem.Header>
<StackPanel Spacing="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabSpacing}"
HorizontalAlignment="Center">
<Viewbox Width="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabIconSize}"
Height="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabIconSize}"
HorizontalAlignment="Center">
<Canvas Width="24" Height="24">
<Path Fill="{Binding $parent[TabItem].Foreground}"
Data="M7,5H21V7H7V5M7,13V11H21V13H7M4,4.5A1.5,1.5 0 0,1 5.5,6A1.5,1.5 0 0,1 4,7.5A1.5,1.5 0 0,1 2.5,6A1.5,1.5 0 0,1 4,4.5M4,10.5A1.5,1.5 0 0,1 5.5,12A1.5,1.5 0 0,1 4,13.5A1.5,1.5 0 0,1 2.5,12A1.5,1.5 0 0,1 4,10.5M7,19V17H21V19H7M4,16.5A1.5,1.5 0 0,1 5.5,18A1.5,1.5 0 0,1 4,19.5A1.5,1.5 0 0,1 2.5,18A1.5,1.5 0 0,1 4,16.5Z"/>
</Canvas>
</Viewbox>
<TextBlock Text="{Binding Loc[tab.history]}"
FontSize="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabFontSize}"
HorizontalAlignment="Center"/>
</StackPanel>
</TabItem.Header>
<Grid RowDefinitions="Auto,*" Margin="4">
<TextBlock Grid.Row="0" Text="{Binding Loc[history.hint]}"
IsVisible="{Binding IsDesktop}"
Foreground="{DynamicResource TextSecondaryBrush}" FontSize="11" Margin="6,2"/>
<ListBox Grid.Row="1" ItemsSource="{Binding History}"
DoubleTapped="OnHistoryRowDoubleTapped">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:HistoryRow">
<Panel Cursor="Hand">
<!-- Desktop: 3 fixed columns -->
<Grid ColumnDefinitions="90,160,*"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsDesktop}">
<TextBlock Grid.Column="0" Text="{Binding Conferma}" Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBlock Grid.Column="1" Text="{Binding Importo}" FontFamily="monospace"/>
<TextBlock Grid.Column="2" Text="{Binding Txid}"
FontFamily="monospace" FontSize="12"
TextTrimming="CharacterEllipsis"/>
</Grid>
<!-- Mobile: vertical card -->
<StackPanel Spacing="2"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsMobile}">
<TextBlock Text="{Binding Importo}"
FontFamily="monospace" FontWeight="SemiBold"/>
<TextBlock Text="{Binding Conferma}" Foreground="{DynamicResource TextSecondaryBrush}" FontSize="11"/>
<TextBlock Text="{Binding Txid}" FontFamily="monospace" FontSize="11"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
</Panel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</TabItem>
<!-- 2. Send -->
<TabItem>
<TabItem.Header>
<StackPanel Spacing="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabSpacing}"
HorizontalAlignment="Center">
<Viewbox Width="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabIconSize}"
Height="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabIconSize}"
HorizontalAlignment="Center">
<Canvas Width="24" Height="24">
<Path Fill="{Binding $parent[TabItem].Foreground}"
Data="M2,21L23,12L2,3V10L17,12L2,14V21Z"/>
</Canvas>
</Viewbox>
<TextBlock Text="{Binding Loc[tab.send]}"
FontSize="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabFontSize}"
HorizontalAlignment="Center"/>
</StackPanel>
</TabItem.Header>
<ScrollViewer>
<Panel Margin="16,14">
<!-- ── DESKTOP: Recipient | Amount side-by-side ── -->
<Grid IsVisible="{Binding IsDesktop}"
RowDefinitions="Auto,Auto,Auto" RowSpacing="14">
<Grid Grid.Row="0" ColumnDefinitions="*,*" ColumnSpacing="14">
<!-- Recipient card -->
<Border Grid.Column="0" Classes="card" Padding="22,18">
<StackPanel Spacing="14">
<TextBlock Text="{Binding Loc[send.sect.recipient]}" Classes="section"/>
<Grid ColumnDefinitions="Auto,*">
<TextBlock Grid.Column="0" Text="{Binding Loc[send.from.contact]}"
VerticalAlignment="Center" Margin="0,0,10,0"
Foreground="{DynamicResource TextSecondaryBrush}" FontSize="13"/>
<ComboBox Grid.Column="1" HorizontalAlignment="Stretch"
ItemsSource="{Binding Contacts}"
SelectedItem="{Binding SendToContact}"
PlaceholderText="{Binding Loc[send.contact.hint]}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="vm:ContactEntry">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[send.to]}" Classes="label"/>
<TextBox PlaceholderText="{Binding Loc[send.to]}"
Text="{Binding SendTo}"
FontFamily="monospace" FontSize="13"/>
</StackPanel>
</StackPanel>
</Border>
<!-- Amount & fee card -->
<Border Grid.Column="1" Classes="card" Padding="22,18">
<StackPanel Spacing="14">
<TextBlock Text="{Binding Loc[send.sect.amount]}" Classes="section"/>
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[send.amount]}" Classes="label"/>
<Grid ColumnDefinitions="*,Auto,Auto">
<TextBox Grid.Column="0"
PlaceholderText="0.00000000"
Text="{Binding SendAmount}"
IsEnabled="{Binding !SendAll}"
FontFamily="monospace"/>
<TextBlock Grid.Column="1" Text="{Binding UnitLabel}"
VerticalAlignment="Center" Margin="10,0,0,0"
Foreground="{DynamicResource TextSecondaryBrush}"
FontWeight="Medium"/>
<CheckBox Grid.Column="2" Content="{Binding Loc[send.all]}"
IsChecked="{Binding SendAll}"
Margin="16,0,0,0" VerticalAlignment="Center"/>
</Grid>
</StackPanel>
<Grid ColumnDefinitions="Auto,*">
<TextBlock Grid.Column="0" Text="{Binding Loc[send.feerate]}"
VerticalAlignment="Center" Classes="label" Margin="0,0,12,0"/>
<TextBox Grid.Column="1" Text="{Binding SendFeeRate}"
MaxWidth="120" HorizontalAlignment="Left"
FontFamily="monospace"/>
</Grid>
</StackPanel>
</Border>
</Grid>
<!-- Summary card — full width, visible after Prepare -->
<Border Grid.Row="1" Classes="card" Padding="22,16"
IsVisible="{Binding SendPreview, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<StackPanel Spacing="8">
<TextBlock Text="{Binding Loc[send.summary]}" Classes="section"/>
<SelectableTextBlock Text="{Binding SendPreview}"
TextWrapping="Wrap" FontSize="13" Classes="mono"
Foreground="{DynamicResource TextSecondaryBrush}"/>
</StackPanel>
</Border>
<!-- Action buttons -->
<Grid Grid.Row="2" ColumnDefinitions="*,*" ColumnSpacing="14">
<Button Grid.Column="0" Content="{Binding Loc[send.prepare]}"
Command="{Binding PrepareSendCommand}"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"/>
<Button Grid.Column="1" Content="{Binding Loc[send.confirm]}" Classes="accent"
Command="{Binding ConfirmSendCommand}"
IsEnabled="{Binding HasPendingSend}"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"/>
</Grid>
</Grid>
<!-- ── MOBILE: vertical stack ── -->
<StackPanel IsVisible="{Binding IsMobile}" Spacing="12">
<!-- Recipient card with labelled inputs -->
<Border Classes="card" Padding="20,18">
<StackPanel Spacing="14">
<TextBlock Text="{Binding Loc[send.sect.recipient]}" Classes="section"/>
<!-- Contact picker -->
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[send.from.contact]}" Classes="label"/>
<ComboBox HorizontalAlignment="Stretch"
ItemsSource="{Binding Contacts}"
SelectedItem="{Binding SendToContact}"
PlaceholderText="{Binding Loc[send.contact.hint]}"
MinHeight="48">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="vm:ContactEntry">
<TextBlock Text="{Binding Name}" FontSize="15"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
<!-- Address + QR scan button -->
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[send.to]}" Classes="label"/>
<Grid ColumnDefinitions="*,Auto">
<TextBox Grid.Column="0"
PlaceholderText="{Binding Loc[send.to]}"
Text="{Binding SendTo}"
FontFamily="monospace" FontSize="15"
MinHeight="48"/>
<Button Grid.Column="1" Margin="8,0,0,0"
Width="52" Height="48"
Command="{Binding ScanQrCommand}"
ToolTip.Tip="{Binding Loc[send.scan]}">
<Viewbox Width="22" Height="22">
<Canvas Width="24" Height="24">
<Path Fill="{DynamicResource SystemBaseHighColor}"
Data="M20,5H16.83L15,3H9L7.17,5H4A2,2 0 0,0 2,7V19A2,2 0 0,0 4,21H20A2,2 0 0,0 22,19V7A2,2 0 0,0 20,5M20,19H4V7H8.05L9.88,5H14.12L15.95,7H20V19M12,8A5,5 0 0,0 7,13A5,5 0 0,0 12,18A5,5 0 0,0 17,13A5,5 0 0,0 12,8M12,16A3,3 0 0,1 9,13A3,3 0 0,1 12,10A3,3 0 0,1 15,13A3,3 0 0,1 12,16Z"/>
</Canvas>
</Viewbox>
</Button>
</Grid>
</StackPanel>
</StackPanel>
</Border>
<!-- Amount & fee card with labelled inputs -->
<Border Classes="card" Padding="20,18">
<StackPanel Spacing="14">
<TextBlock Text="{Binding Loc[send.sect.amount]}" Classes="section"/>
<!-- Amount field -->
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[send.amount]}" Classes="label"/>
<Grid ColumnDefinitions="*,Auto">
<TextBox Grid.Column="0"
PlaceholderText="0.00000000"
Text="{Binding SendAmount}"
IsEnabled="{Binding !SendAll}"
FontFamily="monospace" FontSize="16"
MinHeight="48"/>
<TextBlock Grid.Column="1" Text="{Binding UnitLabel}"
VerticalAlignment="Center" Margin="12,0,0,0"
FontWeight="Medium"
Foreground="{DynamicResource TextSecondaryBrush}"/>
</Grid>
</StackPanel>
<!-- Send-all toggle -->
<CheckBox Content="{Binding Loc[send.all]}"
IsChecked="{Binding SendAll}"
MinHeight="44" FontSize="14"/>
<!-- Fee rate field -->
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[send.feerate]}" Classes="label"/>
<TextBox Text="{Binding SendFeeRate}"
FontFamily="monospace" FontSize="15"
MinHeight="48" HorizontalAlignment="Stretch"/>
</StackPanel>
</StackPanel>
</Border>
<!-- Summary card -->
<Border Classes="card" Padding="20,16"
IsVisible="{Binding SendPreview, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<StackPanel Spacing="8">
<TextBlock Text="{Binding Loc[send.summary]}" Classes="section"/>
<SelectableTextBlock Text="{Binding SendPreview}"
TextWrapping="Wrap" FontSize="13" Classes="mono"
Foreground="{DynamicResource TextSecondaryBrush}"/>
</StackPanel>
</Border>
<!-- Action buttons: primary (Confirm) prominent, secondary below -->
<Button Content="{Binding Loc[send.confirm]}" Classes="accent"
Command="{Binding ConfirmSendCommand}"
IsEnabled="{Binding HasPendingSend}"
MinHeight="52"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
FontSize="15"/>
<Button Content="{Binding Loc[send.prepare]}"
Command="{Binding PrepareSendCommand}"
MinHeight="48"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"/>
</StackPanel>
</Panel>
</ScrollViewer>
</TabItem>
<!-- 3. Receive -->
<TabItem>
<TabItem.Header>
<StackPanel Spacing="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabSpacing}"
HorizontalAlignment="Center">
<Viewbox Width="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabIconSize}"
Height="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabIconSize}"
HorizontalAlignment="Center">
<Canvas Width="24" Height="24">
<Path Fill="{Binding $parent[TabItem].Foreground}"
Data="M2,12H4V17H20V12H22V17A2,2 0 0,1 20,19H4A2,2 0 0,1 2,17V12M12,15L17,10L15.59,8.58L13,11.17V2H11V11.17L8.41,8.59L7,10L12,15Z"/>
</Canvas>
</Viewbox>
<TextBlock Text="{Binding Loc[tab.receive]}"
FontSize="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabFontSize}"
HorizontalAlignment="Center"/>
</StackPanel>
</TabItem.Header>
<ScrollViewer>
<Panel Margin="16,14">
<!-- ── DESKTOP: QR left | address right ── -->
<Grid IsVisible="{Binding IsDesktop}"
ColumnDefinitions="Auto,*" ColumnSpacing="14">
<!-- QR hero card -->
<Border Grid.Column="0" Classes="card" Padding="28,24"
IsVisible="{Binding ReceiveQr, Converter={x:Static ObjectConverters.IsNotNull}}">
<StackPanel Spacing="18" HorizontalAlignment="Center">
<TextBlock Text="{Binding Loc[receive.next]}" Classes="label"
HorizontalAlignment="Center"/>
<Border Background="White" Padding="14" CornerRadius="10"
HorizontalAlignment="Center">
<Image Source="{Binding ReceiveQr}" Width="230" Height="230"
RenderOptions.BitmapInterpolationMode="None"/>
</Border>
</StackPanel>
</Border>
<!-- Address + hint column -->
<StackPanel Grid.Column="1" Spacing="14" VerticalAlignment="Center">
<Border Classes="card" Padding="24,22">
<StackPanel Spacing="16">
<TextBlock Text="{Binding Loc[receive.your.address]}" Classes="section"/>
<Border Background="{DynamicResource SurfaceAltBrush}"
CornerRadius="8" Padding="16,14">
<SelectableTextBlock Text="{Binding ReceiveAddress}"
FontFamily="monospace" FontSize="14"
TextWrapping="Wrap" LineHeight="22"/>
</Border>
<Button Content="{Binding Loc[receive.copy]}" Classes="accent"
Click="OnCopyReceiveAddressClick"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"/>
</StackPanel>
</Border>
<TextBlock Text="{Binding Loc[receive.hint]}"
Foreground="{DynamicResource TextSecondaryBrush}" FontSize="12"
TextWrapping="Wrap" Margin="4,0"/>
</StackPanel>
</Grid>
<!-- ── MOBILE: vertical stack ── -->
<StackPanel IsVisible="{Binding IsMobile}" Spacing="12">
<!-- QR card: full-width, centered with generous padding -->
<Border Classes="card" Padding="24,22"
IsVisible="{Binding ReceiveQr, Converter={x:Static ObjectConverters.IsNotNull}}">
<StackPanel Spacing="16" HorizontalAlignment="Center">
<TextBlock Text="{Binding Loc[receive.next]}" Classes="label"
HorizontalAlignment="Center" FontSize="13"/>
<Border Background="White" Padding="16" CornerRadius="12"
HorizontalAlignment="Center">
<Image Source="{Binding ReceiveQr}" Width="220" Height="220"
RenderOptions.BitmapInterpolationMode="None"/>
</Border>
</StackPanel>
</Border>
<!-- Address card: label + monospace box + big Copy button -->
<Border Classes="card" Padding="20,20">
<StackPanel Spacing="14">
<TextBlock Text="{Binding Loc[receive.your.address]}" Classes="section"/>
<Border Background="{DynamicResource SurfaceAltBrush}"
CornerRadius="8" Padding="16,12">
<SelectableTextBlock Text="{Binding ReceiveAddress}"
FontFamily="monospace" FontSize="14"
TextWrapping="Wrap"
TextAlignment="Center"
HorizontalAlignment="Center"
LineHeight="22"/>
</Border>
<Button Content="{Binding Loc[receive.copy]}" Classes="accent"
Click="OnCopyReceiveAddressClick"
MinHeight="52"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
FontSize="15"/>
</StackPanel>
</Border>
<!-- Hint -->
<TextBlock Text="{Binding Loc[receive.hint]}"
Foreground="{DynamicResource TextSecondaryBrush}" FontSize="12"
TextWrapping="Wrap" TextAlignment="Center" Margin="4,0"/>
</StackPanel>
</Panel>
</ScrollViewer>
</TabItem>
<!-- 4. Addresses -->
<TabItem>
<TabItem.Header>
<StackPanel Spacing="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabSpacing}"
HorizontalAlignment="Center">
<Viewbox Width="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabIconSize}"
Height="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabIconSize}"
HorizontalAlignment="Center">
<Canvas Width="24" Height="24">
<Path Fill="{Binding $parent[TabItem].Foreground}"
Data="M21,18V19A2,2 0 0,1 19,21H5C3.89,21 3,20.1 3,19V5A2,2 0 0,1 5,3H19A2,2 0 0,1 21,5V6H12C10.89,6 10,6.9 10,8V16A2,2 0 0,0 12,18H21M12,16H22V8H12V16M16,13.5A1.5,1.5 0 0,1 14.5,12A1.5,1.5 0 0,1 16,10.5A1.5,1.5 0 0,1 17.5,12A1.5,1.5 0 0,1 16,13.5Z"/>
</Canvas>
</Viewbox>
<TextBlock Text="{Binding Loc[tab.addresses]}"
FontSize="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabFontSize}"
HorizontalAlignment="Center"/>
</StackPanel>
</TabItem.Header>
<Grid RowDefinitions="Auto,*" Margin="4">
<!-- Column header — desktop only -->
<Grid Grid.Row="0" ColumnDefinitions="90,60,*,140,60" Margin="12,4"
IsVisible="{Binding IsDesktop}">
<TextBlock Grid.Column="0" Text="{Binding Loc[addr.type]}" FontWeight="Bold"/>
<TextBlock Grid.Column="1" Text="{Binding Loc[addr.index]}" FontWeight="Bold"/>
<TextBlock Grid.Column="2" Text="{Binding Loc[addr.address]}" FontWeight="Bold"/>
<TextBlock Grid.Column="3" Text="{Binding Loc[addr.balance]}" FontWeight="Bold"/>
<TextBlock Grid.Column="4" Text="Tx" FontWeight="Bold"/>
</Grid>
<ListBox Grid.Row="1" ItemsSource="{Binding Addresses}"
SelectedItem="{Binding SelectedAddressRow}"
x:Name="AddressesListBox"
Tapped="OnAddressListTapped"
PointerPressed="OnAddressListPointerPressed">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:AddressRow">
<Panel>
<!-- Desktop: 5 fixed columns -->
<Grid ColumnDefinitions="90,60,*,140,60"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsDesktop}">
<TextBlock Grid.Column="0" Text="{Binding Tipo}" Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBlock Grid.Column="1" Text="{Binding Indice}" Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBlock Grid.Column="2" Text="{Binding Indirizzo}"
FontFamily="monospace" FontSize="13"/>
<TextBlock Grid.Column="3" Text="{Binding Saldo}" FontFamily="monospace"/>
<TextBlock Grid.Column="4" Text="{Binding NumTx}" Foreground="{DynamicResource TextSecondaryBrush}"/>
</Grid>
<!-- Mobile: vertical card -->
<StackPanel Spacing="1" Margin="0,3"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsMobile}">
<TextBlock Text="{Binding Indirizzo}"
FontFamily="monospace" FontSize="12"
TextTrimming="CharacterEllipsis"/>
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="{Binding Tipo}" Foreground="{DynamicResource TextSecondaryBrush}" FontSize="11"/>
<TextBlock Text="{Binding Indice}" Foreground="{DynamicResource TextSecondaryBrush}" FontSize="11"/>
<TextBlock Text="{Binding Saldo}" FontFamily="monospace" FontSize="11"/>
<TextBlock Text="{Binding NumTx}" Foreground="{DynamicResource TextSecondaryBrush}" FontSize="11"/>
</StackPanel>
</StackPanel>
</Panel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</TabItem>
<!-- 5. Contacts -->
<TabItem>
<TabItem.Header>
<StackPanel Spacing="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabSpacing}"
HorizontalAlignment="Center">
<Viewbox Width="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabIconSize}"
Height="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabIconSize}"
HorizontalAlignment="Center">
<Canvas Width="24" Height="24">
<Path Fill="{Binding $parent[TabItem].Foreground}"
Data="M16,13C15.71,13 15.38,13 15.03,13.05C16.19,13.89 17,15 17,16.5V19H23V16.5C23,14.17 18.33,13 16,13M8,13C5.67,13 1,14.17 1,16.5V19H15V16.5C15,14.17 10.33,13 8,13M8,11A3,3 0 0,0 11,8A3,3 0 0,0 8,5A3,3 0 0,0 5,8A3,3 0 0,0 8,11M16,11A3,3 0 0,0 19,8A3,3 0 0,0 16,5C15.71,5 15.42,5.03 15.14,5.09C15.68,5.95 16,6.94 16,8C16,9.06 15.68,10.05 15.14,10.91C15.42,10.97 15.71,11 16,11Z"/>
</Canvas>
</Viewbox>
<TextBlock Text="{Binding Loc[tab.contacts]}"
FontSize="{Binding $parent[TabControl].((vm:MainWindowViewModel)DataContext).TabFontSize}"
HorizontalAlignment="Center"/>
</StackPanel>
</TabItem.Header>
<Grid RowDefinitions="Auto,*,Auto,Auto" Margin="4">
<!-- Column headers — desktop only -->
<Grid Grid.Row="0" ColumnDefinitions="180,*" Margin="12,4"
IsVisible="{Binding IsDesktop}">
<TextBlock Grid.Column="0" Text="{Binding Loc[contacts.name]}" FontWeight="Bold"/>
<TextBlock Grid.Column="1" Text="{Binding Loc[contacts.address]}" FontWeight="Bold"/>
</Grid>
<!-- Contacts list -->
<ListBox Grid.Row="1" ItemsSource="{Binding Contacts}"
SelectedItem="{Binding SelectedContactInList}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:ContactEntry">
<Panel>
<!-- Desktop: 2 columns -->
<Grid ColumnDefinitions="180,*"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsDesktop}">
<TextBlock Grid.Column="0" Text="{Binding Name}"
VerticalAlignment="Center"/>
<SelectableTextBlock Grid.Column="1" Text="{Binding Address}"
FontFamily="monospace" FontSize="12"
VerticalAlignment="Center"/>
</Grid>
<!-- Mobile: vertical card -->
<StackPanel Spacing="1" Margin="0,3"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsMobile}">
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" FontSize="13"
TextTrimming="CharacterEllipsis"/>
<SelectableTextBlock Text="{Binding Address}"
FontFamily="monospace" FontSize="11"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
</Panel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- Remove selected button -->
<Button Grid.Row="2" Content="{Binding Loc[contacts.remove]}"
Command="{Binding RemoveSelectedContactCommand}"
IsEnabled="{Binding SelectedContactInList, Converter={x:Static ObjectConverters.IsNotNull}}"
Margin="0,6,0,0"/>
<!-- Add contact form — Desktop: 3 columns -->
<Grid Grid.Row="3" ColumnDefinitions="180,*,Auto" Margin="0,8,0,0"
IsVisible="{Binding IsDesktop}">
<TextBox Grid.Column="0" PlaceholderText="{Binding Loc[contacts.name.ph]}"
Text="{Binding NewContactName}" Margin="0,0,6,0"/>
<TextBox Grid.Column="1" PlaceholderText="{Binding Loc[contacts.address.ph]}"
Text="{Binding NewContactAddress}" FontFamily="monospace"
Margin="0,0,6,0"/>
<Button Grid.Column="2" Content="{Binding Loc[contacts.add]}"
Command="{Binding AddContactCommand}"/>
</Grid>
<!-- Add contact form — Mobile: vertical -->
<StackPanel Grid.Row="3" Spacing="6" Margin="0,8,0,0"
IsVisible="{Binding IsMobile}">
<TextBox PlaceholderText="{Binding Loc[contacts.name.ph]}"
Text="{Binding NewContactName}"/>
<TextBox PlaceholderText="{Binding Loc[contacts.address.ph]}"
Text="{Binding NewContactAddress}" FontFamily="monospace"/>
<Button Content="{Binding Loc[contacts.add]}"
Command="{Binding AddContactCommand}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"/>
</StackPanel>
</Grid>
</TabItem>
</TabControl>
</Grid>
<!-- Status bar: message on the left, connection status on the right.
The connection status is tappable and opens the server settings. -->
<Border Grid.Row="2" Background="{DynamicResource SurfaceBrush}"
BorderBrush="{DynamicResource BorderSubtleBrush}" BorderThickness="0,1,0,0"
Padding="12,7">
<Grid ColumnDefinitions="*,Auto">
<TextBlock Grid.Column="0" Text="{Binding StatusMessage}" FontSize="12"
TextWrapping="Wrap" VerticalAlignment="Center"/>
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="6"
Margin="12,0,0,0" VerticalAlignment="Center"
Cursor="Hand" Background="Transparent"
ToolTip.Tip="{Binding Loc[server.title]}"
Tapped="OnConnectionStatusTapped">
<Ellipse Width="10" Height="10" Fill="{DynamicResource SuccessBrush}" VerticalAlignment="Center"
IsVisible="{Binding IsConnected}"/>
<Ellipse Width="10" Height="10" Fill="{DynamicResource DangerBrush}" VerticalAlignment="Center"
IsVisible="{Binding !IsConnected}"/>
<TextBlock Text="{Binding ConnectionStatusShort}" FontSize="12"
Foreground="{DynamicResource SystemAccentColor}"
VerticalAlignment="Center"/>
</StackPanel>
</Grid>
</Border>
<!-- ============ ADDRESS DETAIL OVERLAY ============ -->
<!-- In-app overlay instead of a separate Window: instant open/close
(no top-level window create/destroy). -->
<Border Grid.Row="0" Grid.RowSpan="3"
Background="{DynamicResource ScrimBrush}"
Tapped="OnAddressOverlayBackdropTapped"
IsVisible="{Binding AddressInfo, Converter={x:Static ObjectConverters.IsNotNull}}">
<Border Background="{DynamicResource OverlayCardBrush}"
BorderBrush="{DynamicResource BorderSubtleBrush}" BorderThickness="1" CornerRadius="8"
MaxWidth="540" Margin="16"
HorizontalAlignment="Center" VerticalAlignment="Center"
DataContext="{Binding AddressInfo}">
<ScrollViewer MaxHeight="640">
<StackPanel Margin="24" Spacing="16">
<TextBlock Text="{Binding Loc[addr.info.title]}"
FontSize="18" FontWeight="Bold"/>
<!-- Address -->
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[addr.address]}" FontSize="11" Foreground="{DynamicResource TextSecondaryBrush}"/>
<SelectableTextBlock Text="{Binding Address}"
FontFamily="monospace" FontSize="13"
TextWrapping="Wrap"/>
</StackPanel>
<!-- Derivation path -->
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[addr.derivpath]}" FontSize="11" Foreground="{DynamicResource TextSecondaryBrush}"/>
<SelectableTextBlock Text="{Binding DerivPath}"
FontFamily="monospace" FontSize="13"/>
</StackPanel>
<!-- Public key -->
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[addr.pubkey]}" FontSize="11" Foreground="{DynamicResource TextSecondaryBrush}"/>
<SelectableTextBlock Text="{Binding PubKey}"
FontFamily="monospace" FontSize="12"
TextWrapping="Wrap"/>
</StackPanel>
<!-- Private key (only when available) -->
<StackPanel Spacing="8" IsVisible="{Binding HasPrivKey}">
<TextBlock Text="{Binding Loc[addr.privkey]}" FontSize="11"
Foreground="{DynamicResource DangerBrush}"/>
<!-- Not yet revealed: single reveal button -->
<Button IsVisible="{Binding !$parent[UserControl].((vm:MainWindowViewModel)DataContext).IsAddressPrivKeyRevealed}"
Content="{Binding Loc[walletinfo.seed.reveal]}"
Command="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).RequestPrivKeyRevealCommand}"/>
<!-- Revealed: key text + hide button -->
<StackPanel IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsAddressPrivKeyRevealed}"
Spacing="6">
<SelectableTextBlock Text="{Binding PrivKey}"
FontFamily="monospace" FontSize="12"
Foreground="{DynamicResource DangerBrush}"
TextWrapping="Wrap"/>
<Button Content="{Binding Loc[walletinfo.seed.hide]}"
Command="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).HideAddressPrivKeyCommand}"/>
</StackPanel>
</StackPanel>
<Button Content="{Binding Loc[addr.close]}"
HorizontalAlignment="Right"
Command="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).CloseAddressInfoCommand}"/>
</StackPanel>
</ScrollViewer>
</Border>
</Border>
<!-- ============ PRIVATE KEY PASSWORD PROMPT ============ -->
<!-- Appears on top of the address detail overlay when the wallet is encrypted. -->
<Border Grid.Row="0" Grid.RowSpan="3"
Background="{DynamicResource ScrimBrush}"
IsVisible="{Binding IsPrivKeyPromptOpen}">
<Border Background="{DynamicResource OverlayCardBrush}"
BorderBrush="{DynamicResource BorderSubtleBrush}" BorderThickness="1" CornerRadius="8"
MaxWidth="360" Margin="16"
HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Margin="24" Spacing="14">
<TextBlock Text="{Binding Loc[addr.privkey.prompt.title]}"
FontSize="16" FontWeight="Bold"/>
<TextBlock Text="{Binding Loc[addr.privkey.prompt.desc]}"
FontSize="12" TextWrapping="Wrap"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBox Text="{Binding PrivKeyPromptPassword}"
PasswordChar="●"
PlaceholderText="{Binding Loc[wiz.open.placeholder]}"/>
<TextBlock Text="{Binding PrivKeyPromptError}"
Foreground="{DynamicResource DangerBrush}" FontSize="12"
IsVisible="{Binding PrivKeyPromptError,
Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Right">
<Button Content="{Binding Loc[addr.close]}"
Command="{Binding CancelPrivKeyPromptCommand}"/>
<Button Content="{Binding Loc[walletinfo.seed.reveal]}" Classes="accent"
Command="{Binding ConfirmPrivKeyPasswordCommand}"/>
</StackPanel>
</StackPanel>
</Border>
</Border>
<!-- ============ TRANSACTION DETAIL OVERLAY ============ -->
<!-- In-app overlay (like the address overlay): appears immediately with a
spinner; data arrives from the server in the background; instant close. -->
<Border Grid.Row="0" Grid.RowSpan="3"
Background="{DynamicResource ScrimBrush}"
Tapped="OnTxDetailsOverlayBackdropTapped"
IsVisible="{Binding IsTxDetailsOpen}">
<Border Background="{DynamicResource OverlayCardBrush}"
BorderBrush="{DynamicResource BorderSubtleBrush}" BorderThickness="1" CornerRadius="8"
MaxWidth="660" MaxHeight="800" Margin="16" Padding="0"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Panel Margin="24">
<!-- Loading -->
<StackPanel IsVisible="{Binding IsTxDetailsLoading}"
HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="14">
<ProgressBar IsIndeterminate="True" Width="220"/>
<TextBlock Text="{Binding Loc[tx.loading]}" Foreground="{DynamicResource TextSecondaryBrush}"
HorizontalAlignment="Center"/>
</StackPanel>
<!-- Content (IsVisible on the outer container so it stays bound
to the main VM; the inner DataContext is TxDetails) -->
<Grid IsVisible="{Binding !IsTxDetailsLoading}">
<DockPanel DataContext="{Binding TxDetails}">
<TextBlock DockPanel.Dock="Top" Text="{Binding Loc[tx.title]}"
FontSize="18" FontWeight="Bold" Margin="0,0,0,12"/>
<Button DockPanel.Dock="Bottom" Content="{Binding Loc[tx.close]}"
HorizontalAlignment="Right" Margin="0,12,0,0"
Command="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).CloseTransactionDetailsCommand}"/>
<ScrollViewer>
<StackPanel Spacing="18">
<!-- Header: net amount highlighted + status/SPV badges -->
<StackPanel Spacing="10">
<TextBlock Text="{Binding AmountHeader}" Classes="label"/>
<SelectableTextBlock Text="{Binding NetText}"
FontFamily="monospace" FontFeatures="+tnum"
FontSize="28" FontWeight="Bold"/>
<WrapPanel>
<!-- Coinbase badge: coins generated by mining (no sender) -->
<Border Classes="chip" Margin="0,0,8,6" IsVisible="{Binding IsCoinbase}"
Background="{DynamicResource WarningBrush}">
<StackPanel Orientation="Horizontal" Spacing="6">
<Viewbox Width="14" Height="14" VerticalAlignment="Center">
<Canvas Width="24" Height="24">
<Path Fill="#FFFFFF"
Data="M21,16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V7.5C3,7.12 3.21,6.79 3.53,6.62L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.79,6.79 21,7.12 21,7.5V16.5M12,4.15L6.04,7.5L12,10.85L17.96,7.5L12,4.15M5,15.91L11,19.29V12.58L5,9.21V15.91M19,15.91V9.21L13,12.58V19.29L19,15.91Z"/>
</Canvas>
</Viewbox>
<TextBlock Text="{Binding Loc[tx.coinbase]}"
Foreground="#FFFFFF"
FontSize="12" FontWeight="SemiBold" VerticalAlignment="Center"/>
</StackPanel>
</Border>
<Border Classes="chip" Margin="0,0,8,6">
<TextBlock Text="{Binding StatusText}" FontSize="12"
Foreground="{DynamicResource TextSecondaryBrush}"/>
</Border>
</WrapPanel>
</StackPanel>
<!-- Section: Overview -->
<TextBlock Classes="section" Text="{Binding Loc[tx.sect.overview]}"/>
<Border Classes="card" Padding="16,12">
<StackPanel Spacing="10">
<Grid ColumnDefinitions="150,*">
<TextBlock Grid.Column="0" Classes="label" Text="{Binding Loc[tx.date]}"/>
<SelectableTextBlock Grid.Column="1" FontSize="13" Text="{Binding DateText}"/>
</Grid>
<Grid ColumnDefinitions="150,*">
<TextBlock Grid.Column="0" Classes="label" Text="{Binding CounterpartyHeader}"/>
<SelectableTextBlock Grid.Column="1" FontSize="13" Classes="mono"
TextWrapping="Wrap" Text="{Binding CounterpartyText}"/>
</Grid>
</StackPanel>
</Border>
<!-- Section: Amounts and fees -->
<TextBlock Classes="section" Text="{Binding Loc[tx.sect.amounts]}"/>
<Border Classes="card" Padding="16,12">
<StackPanel Spacing="10">
<Grid ColumnDefinitions="150,*">
<TextBlock Grid.Column="0" Classes="label" Text="{Binding AmountHeader}"/>
<SelectableTextBlock Grid.Column="1" FontSize="13" Classes="mono" Text="{Binding AmountText}"/>
</Grid>
<Grid ColumnDefinitions="150,*">
<TextBlock Grid.Column="0" Classes="label" Text="{Binding Loc[tx.fee]}"/>
<SelectableTextBlock Grid.Column="1" FontSize="13" Classes="mono" Text="{Binding FeeText}"/>
</Grid>
<Grid ColumnDefinitions="150,*">
<TextBlock Grid.Column="0" Classes="label" Text="{Binding Loc[tx.feerate]}"/>
<SelectableTextBlock Grid.Column="1" FontSize="13" Classes="mono" Text="{Binding FeeRateText}"/>
</Grid>
<Border Height="1" Background="{DynamicResource BorderSubtleBrush}" Margin="0,2"/>
<Grid ColumnDefinitions="150,*">
<TextBlock Grid.Column="0" Classes="label" Text="{Binding Loc[tx.net]}"/>
<SelectableTextBlock Grid.Column="1" FontSize="14" Classes="mono"
FontWeight="Bold" Text="{Binding NetText}"/>
</Grid>
</StackPanel>
</Border>
<!-- Section: Technical details -->
<TextBlock Classes="section" Text="{Binding Loc[tx.sect.tech]}"/>
<Border Classes="card" Padding="16,12">
<StackPanel Spacing="10">
<StackPanel Spacing="3">
<TextBlock Classes="label" Text="{Binding Loc[tx.id]}"/>
<SelectableTextBlock FontSize="12" Classes="mono"
TextWrapping="Wrap" Text="{Binding Txid}"/>
</StackPanel>
<Grid ColumnDefinitions="150,*">
<TextBlock Grid.Column="0" Classes="label" Text="{Binding Loc[tx.size.total]}"/>
<SelectableTextBlock Grid.Column="1" FontSize="13" Text="{Binding TotalSizeText}"/>
</Grid>
<Grid ColumnDefinitions="150,*">
<TextBlock Grid.Column="0" Classes="label" Text="{Binding Loc[tx.size.virtual]}"/>
<SelectableTextBlock Grid.Column="1" FontSize="13" Text="{Binding VirtualSizeText}"/>
</Grid>
<Grid ColumnDefinitions="150,*">
<TextBlock Grid.Column="0" Classes="label" Text="{Binding Loc[tx.rbf]}"/>
<SelectableTextBlock Grid.Column="1" FontSize="13" Text="{Binding RbfText}"/>
</Grid>
</StackPanel>
</Border>
<TextBlock Classes="section" Text="{Binding Loc[tx.inputs]}"/>
<Border Classes="card" Padding="14,4">
<ItemsControl ItemsSource="{Binding Inputs}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:TxIoRow">
<!-- Two-line layout: txid ref on first line, address+amount on second -->
<StackPanel Margin="0,6,0,4">
<SelectableTextBlock Text="{Binding Position}"
FontFamily="monospace" FontSize="10"
Foreground="{DynamicResource TextMutedBrush}"
TextWrapping="Wrap"/>
<Grid ColumnDefinitions="*,Auto" Margin="0,2,0,0">
<SelectableTextBlock Grid.Column="0" Text="{Binding Address}"
FontFamily="monospace" FontSize="11"
TextWrapping="Wrap" VerticalAlignment="Top"
Foreground="{Binding IsMine, Converter={x:Static vm:MineColorConverter.Instance}}"/>
<SelectableTextBlock Grid.Column="1" Text="{Binding Amount}"
FontFamily="monospace" FontFeatures="+tnum" FontSize="11"
VerticalAlignment="Top" Margin="12,0,0,0"/>
</Grid>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
<TextBlock Classes="section" Text="{Binding Loc[tx.outputs]}"/>
<Border Classes="card" Padding="14,4">
<ItemsControl ItemsSource="{Binding Outputs}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:TxIoRow">
<Grid ColumnDefinitions="44,*,Auto" Margin="0,6">
<TextBlock Grid.Column="0" Text="{Binding Position}"
FontFamily="monospace" FontSize="11"
Foreground="{DynamicResource TextMutedBrush}"
VerticalAlignment="Top"/>
<SelectableTextBlock Grid.Column="1" Text="{Binding Address}"
FontFamily="monospace" FontSize="11"
TextWrapping="Wrap" VerticalAlignment="Top"
Foreground="{Binding IsMine, Converter={x:Static vm:MineColorConverter.Instance}}"/>
<SelectableTextBlock Grid.Column="2" Text="{Binding Amount}"
FontFamily="monospace" FontFeatures="+tnum" FontSize="11"
VerticalAlignment="Top" Margin="12,0,0,0"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</StackPanel>
</ScrollViewer>
</DockPanel>
</Grid>
</Panel>
</Border>
</Border>
<!-- ============ SERVER SETTINGS OVERLAY ============ -->
<!-- Same pattern as the address overlay: instant open/close.
Accessible from Settings → Server. -->
<Border Grid.Row="0" Grid.RowSpan="3"
Background="{DynamicResource ScrimBrush}"
Tapped="OnServerOverlayBackdropTapped"
IsVisible="{Binding IsServerSettingsOpen}">
<Border Background="{DynamicResource OverlayCardBrush}"
BorderBrush="{DynamicResource BorderSubtleBrush}" BorderThickness="1"
Classes.desktop-overlay="{Binding IsDesktop}">
<ScrollViewer>
<StackPanel Margin="24" Spacing="14">
<TextBlock Text="{Binding Loc[server.title]}"
FontSize="18" FontWeight="Bold"/>
<!-- Host + port — Desktop: 2 columns -->
<Grid ColumnDefinitions="*,Auto" RowDefinitions="Auto,Auto"
IsVisible="{Binding IsDesktop}">
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Loc[server.host]}"
FontSize="11" Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Loc[server.port]}"
FontSize="11" Foreground="{DynamicResource TextSecondaryBrush}" Margin="10,0,0,0" Width="90"/>
<TextBox Grid.Row="1" Grid.Column="0" Text="{Binding ServerHost}"
FontFamily="monospace" Margin="0,2,0,0"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding ServerPort}"
FontFamily="monospace" Width="90" Margin="10,2,0,0"/>
</Grid>
<!-- Host + port — Mobile: vertical, larger font -->
<StackPanel Spacing="10" IsVisible="{Binding IsMobile}">
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[server.host]}" FontSize="13" Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBox Text="{Binding ServerHost}" FontFamily="monospace" FontSize="15"/>
</StackPanel>
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[server.port]}" FontSize="13" Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBox Text="{Binding ServerPort}" FontFamily="monospace" FontSize="15"/>
</StackPanel>
</StackPanel>
<!-- Actions — Desktop: in a row -->
<StackPanel Orientation="Horizontal" Spacing="8" IsVisible="{Binding IsDesktop}">
<Button Content="{Binding Loc[wallet.connect]}" Classes="accent"
Command="{Binding ConnectAndSyncCommand}"/>
<Button Content="{Binding Loc[wallet.discover]}"
Command="{Binding DiscoverServersCommand}"/>
<Button Content="{Binding Loc[wallet.resetcert]}"
Command="{Binding ResetCertificatesCommand}"/>
</StackPanel>
<!-- Actions — Mobile: stacked -->
<StackPanel Spacing="6" IsVisible="{Binding IsMobile}">
<Button Content="{Binding Loc[wallet.connect]}" Classes="accent"
Command="{Binding ConnectAndSyncCommand}"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"/>
<Button Content="{Binding Loc[wallet.discover]}"
Command="{Binding DiscoverServersCommand}"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"/>
<Button Content="{Binding Loc[wallet.resetcert]}"
Command="{Binding ResetCertificatesCommand}"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"/>
</StackPanel>
<!-- Connection status -->
<TextBlock Text="{Binding ConnectionStatus}" Foreground="{DynamicResource TextSecondaryBrush}"/>
<!-- Known servers: click to populate host/port -->
<TextBlock Text="{Binding Loc[server.known]}" FontSize="11" Foreground="{DynamicResource TextSecondaryBrush}"/>
<ListBox ItemsSource="{Binding KnownServers}"
SelectedItem="{Binding SelectedKnownServer}"
MaxHeight="300">
<ListBox.Styles>
<Style Selector="ListBox:empty">
<Setter Property="Template">
<ControlTemplate>
<TextBlock Text="{Binding Loc[server.empty]}"
Foreground="{DynamicResource TextSecondaryBrush}" FontSize="12"
TextWrapping="Wrap" Margin="8"/>
</ControlTemplate>
</Setter>
</Style>
</ListBox.Styles>
<ListBox.ItemTemplate>
<DataTemplate x:DataType="net:KnownServer">
<Panel>
<!-- Desktop: host + ports in 2 columns -->
<Grid ColumnDefinitions="*,Auto"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsDesktop}">
<TextBlock Grid.Column="0" Text="{Binding Host}"
FontFamily="monospace" FontSize="13"
VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" FontSize="11" Foreground="{DynamicResource TextSecondaryBrush}"
VerticalAlignment="Center">
<Run Text="tcp "/><Run Text="{Binding TcpPort}"/>
<Run Text=" ssl "/><Run Text="{Binding SslPort}"/>
</TextBlock>
</Grid>
<!-- Mobile: host above, ports below -->
<StackPanel Spacing="2" Margin="0,4"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsMobile}">
<TextBlock Text="{Binding Host}"
FontFamily="monospace" FontSize="15"
TextTrimming="CharacterEllipsis"/>
<StackPanel Orientation="Horizontal" Spacing="16">
<TextBlock FontSize="12" Foreground="{DynamicResource TextSecondaryBrush}">
<Run Text="tcp "/><Run Text="{Binding TcpPort}"/>
</TextBlock>
<TextBlock FontSize="12" Foreground="{DynamicResource TextSecondaryBrush}">
<Run Text="ssl "/><Run Text="{Binding SslPort}"/>
</TextBlock>
</StackPanel>
</StackPanel>
</Panel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="{Binding Loc[addr.close]}"
HorizontalAlignment="Right"
Command="{Binding CloseServerSettingsCommand}"/>
</StackPanel>
</ScrollViewer>
</Border>
</Border>
<!-- ============ WALLET INFO OVERLAY ============ -->
<Border Grid.Row="0" Grid.RowSpan="3"
Background="{DynamicResource ScrimBrush}"
Tapped="OnWalletInfoOverlayBackdropTapped"
IsVisible="{Binding IsWalletInfoOpen}">
<Border Background="{DynamicResource OverlayCardBrush}"
BorderBrush="{DynamicResource BorderSubtleBrush}" BorderThickness="1" CornerRadius="8"
MaxWidth="500" Margin="16"
HorizontalAlignment="Center" VerticalAlignment="Center">
<ScrollViewer MaxHeight="620">
<StackPanel Margin="24" Spacing="14">
<TextBlock Text="{Binding Loc[walletinfo.title]}"
FontSize="18" FontWeight="Bold"/>
<!-- File name -->
<StackPanel Spacing="3">
<TextBlock Text="{Binding Loc[walletinfo.file]}" FontSize="11"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<SelectableTextBlock Text="{Binding WalletInfoFileName}"
FontFamily="monospace" FontSize="13"/>
</StackPanel>
<!-- Network -->
<StackPanel Spacing="3">
<TextBlock Text="{Binding Loc[walletinfo.network]}" FontSize="11"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBlock Text="{Binding WalletInfoNetwork}" FontSize="13"/>
</StackPanel>
<!-- Wallet type -->
<StackPanel Spacing="3">
<TextBlock Text="{Binding Loc[walletinfo.type]}" FontSize="11"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBlock Text="{Binding WalletInfoType}" FontSize="13"/>
</StackPanel>
<!-- Script kind -->
<StackPanel Spacing="3">
<TextBlock Text="{Binding Loc[walletinfo.script]}" FontSize="11"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBlock Text="{Binding WalletInfoScriptKind}" FontSize="13"/>
</StackPanel>
<!-- Derivation path (only if present) -->
<StackPanel Spacing="3"
IsVisible="{Binding WalletInfoDerivPath,
Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<TextBlock Text="{Binding Loc[walletinfo.derivpath]}" FontSize="11"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<SelectableTextBlock Text="{Binding WalletInfoDerivPath}"
FontFamily="monospace" FontSize="13"/>
</StackPanel>
<!-- Master fingerprint (only if present) -->
<StackPanel Spacing="3"
IsVisible="{Binding WalletInfoFingerprint,
Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<TextBlock Text="{Binding Loc[walletinfo.fingerprint]}" FontSize="11"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<SelectableTextBlock Text="{Binding WalletInfoFingerprint}"
FontFamily="monospace" FontSize="13"/>
</StackPanel>
<!-- Account xpub (only if present) -->
<StackPanel Spacing="3"
IsVisible="{Binding WalletInfoXpub,
Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
<TextBlock Text="{Binding Loc[walletinfo.xpub]}" FontSize="11"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<SelectableTextBlock Text="{Binding WalletInfoXpub}"
FontFamily="monospace" FontSize="11"
TextWrapping="Wrap"/>
</StackPanel>
<!-- BIP39 passphrase indicator -->
<StackPanel Spacing="3" IsVisible="{Binding WalletInfoHasPassphrase}">
<TextBlock Text="{Binding Loc[walletinfo.passphrase]}" FontSize="11"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBlock Text="{Binding Loc[walletinfo.passphrase.set]}"
FontSize="13" Foreground="{DynamicResource TextSecondaryBrush}"/>
</StackPanel>
<!-- Separator -->
<Border Height="1" Background="{DynamicResource BorderSubtleBrush}" Margin="0,4"/>
<!-- Seed section -->
<TextBlock Text="{Binding Loc[walletinfo.seed.section]}"
FontSize="13" FontWeight="SemiBold"/>
<!-- No seed wallet -->
<TextBlock IsVisible="{Binding !WalletInfoHasSeed}"
Text="{Binding Loc[walletinfo.seed.noseed]}"
Foreground="{DynamicResource TextSecondaryBrush}"
FontSize="12" TextWrapping="Wrap"/>
<!-- Seed wallet: needs password -->
<StackPanel IsVisible="{Binding WalletInfoHasSeed}" Spacing="10">
<!-- Not yet revealed + password needed -->
<StackPanel IsVisible="{Binding WalletInfoSeedNeedsPassword}" Spacing="8">
<StackPanel IsVisible="{Binding !IsWalletInfoSeedRevealed}" Spacing="8">
<TextBlock Text="{Binding Loc[walletinfo.seed.password]}"
FontSize="12" TextWrapping="Wrap"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBox Text="{Binding WalletInfoSeedPasswordInput}"
PasswordChar="●"
PlaceholderText="{Binding Loc[wiz.open.placeholder]}"/>
<TextBlock Text="{Binding WalletInfoSeedError}"
Foreground="{DynamicResource DangerBrush}" FontSize="12"
IsVisible="{Binding WalletInfoSeedError,
Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<Button Content="{Binding Loc[walletinfo.seed.reveal]}"
Command="{Binding RevealSeedCommand}"/>
</StackPanel>
</StackPanel>
<!-- Not yet revealed + no password needed -->
<Button IsVisible="{Binding !WalletInfoSeedNeedsPassword}"
Content="{Binding Loc[walletinfo.seed.reveal]}"
Command="{Binding RevealSeedCommand}"
IsEnabled="{Binding !IsWalletInfoSeedRevealed}"/>
<!-- Revealed seed -->
<StackPanel IsVisible="{Binding IsWalletInfoSeedRevealed}" Spacing="8">
<Border BorderBrush="{DynamicResource DangerBrush}" BorderThickness="1"
CornerRadius="6" Padding="14"
Background="{DynamicResource SurfaceAltBrush}">
<SelectableTextBlock Text="{Binding WalletInfoSeedText}"
FontFamily="monospace" FontSize="15"
TextWrapping="Wrap"/>
</Border>
<TextBlock Text="{Binding Loc[walletinfo.seed.warning]}"
Foreground="{DynamicResource DangerBrush}"
FontSize="12" TextWrapping="Wrap"/>
<Button Content="{Binding Loc[walletinfo.seed.hide]}"
Command="{Binding HideSeedCommand}"/>
</StackPanel>
</StackPanel>
<Button Content="{Binding Loc[addr.close]}"
HorizontalAlignment="Right"
Command="{Binding CloseWalletInfoCommand}"/>
</StackPanel>
</ScrollViewer>
</Border>
</Border>
<!-- ============ SETTINGS OVERLAY ============ -->
<!-- In-app instead of nested submenus: avoids slow OS popups on WSLg. -->
<Border Grid.Row="0" Grid.RowSpan="3"
Background="{DynamicResource ScrimBrush}"
Tapped="OnSettingsOverlayBackdropTapped"
IsVisible="{Binding IsSettingsOpen}">
<Border Background="{DynamicResource OverlayCardBrush}"
BorderBrush="{DynamicResource BorderSubtleBrush}" BorderThickness="1" CornerRadius="8"
MaxWidth="440" Margin="16"
HorizontalAlignment="Center" VerticalAlignment="Center">
<ScrollViewer MaxHeight="640">
<StackPanel Margin="24" Spacing="16">
<TextBlock Text="{Binding Loc[settings.title]}"
FontSize="18" FontWeight="Bold"/>
<!-- Language -->
<StackPanel Spacing="6">
<TextBlock Text="{Binding Loc[settings.language]}" FontSize="11" Foreground="{DynamicResource TextSecondaryBrush}"/>
<WrapPanel>
<RadioButton GroupName="lang" Content="Italiano" Margin="0,0,14,4"
IsChecked="{Binding IsLangIt, Mode=OneWay}"
Command="{Binding SetLanguageCommand}" CommandParameter="it"/>
<RadioButton GroupName="lang" Content="English" Margin="0,0,14,4"
IsChecked="{Binding IsLangEn, Mode=OneWay}"
Command="{Binding SetLanguageCommand}" CommandParameter="en"/>
<RadioButton GroupName="lang" Content="Español" Margin="0,0,14,4"
IsChecked="{Binding IsLangEs, Mode=OneWay}"
Command="{Binding SetLanguageCommand}" CommandParameter="es"/>
<RadioButton GroupName="lang" Content="Français" Margin="0,0,14,4"
IsChecked="{Binding IsLangFr, Mode=OneWay}"
Command="{Binding SetLanguageCommand}" CommandParameter="fr"/>
<RadioButton GroupName="lang" Content="Português" Margin="0,0,14,4"
IsChecked="{Binding IsLangPt, Mode=OneWay}"
Command="{Binding SetLanguageCommand}" CommandParameter="pt"/>
<RadioButton GroupName="lang" Content="Deutsch" Margin="0,0,14,4"
IsChecked="{Binding IsLangDe, Mode=OneWay}"
Command="{Binding SetLanguageCommand}" CommandParameter="de"/>
</WrapPanel>
</StackPanel>
<!-- Unit -->
<StackPanel Spacing="6">
<TextBlock Text="{Binding Loc[settings.unit]}" FontSize="11" Foreground="{DynamicResource TextSecondaryBrush}"/>
<WrapPanel>
<RadioButton GroupName="unit" Content="PLM" Margin="0,0,14,4"
IsChecked="{Binding IsUnitPlm, Mode=OneWay}"
Command="{Binding SetUnitCommand}" CommandParameter="PLM"/>
<RadioButton GroupName="unit" Content="mPLM" Margin="0,0,14,4"
IsChecked="{Binding IsUnitMilli, Mode=OneWay}"
Command="{Binding SetUnitCommand}" CommandParameter="mPLM"/>
<RadioButton GroupName="unit" Content="µPLM" Margin="0,0,14,4"
IsChecked="{Binding IsUnitMicro, Mode=OneWay}"
Command="{Binding SetUnitCommand}" CommandParameter="µPLM"/>
<RadioButton GroupName="unit" Content="sat" Margin="0,0,14,4"
IsChecked="{Binding IsUnitSat, Mode=OneWay}"
Command="{Binding SetUnitCommand}" CommandParameter="sat"/>
</WrapPanel>
</StackPanel>
<!-- Indexing server (configurable even before a wallet is open) -->
<Button Content="{Binding Loc[settings.server]}"
HorizontalAlignment="Left"
Command="{Binding OpenServerSettingsCommand}"/>
<Button Content="{Binding Loc[addr.close]}"
HorizontalAlignment="Right"
Command="{Binding CloseSettingsCommand}"/>
</StackPanel>
</ScrollViewer>
</Border>
</Border>
<!-- ============ HELP / ABOUT OVERLAY ============ -->
<!-- Same pattern as the settings overlay: instant open/close. -->
<Border Grid.Row="0" Grid.RowSpan="3"
Background="{DynamicResource ScrimBrush}"
Tapped="OnHelpOverlayBackdropTapped"
IsVisible="{Binding IsHelpOpen}">
<Border Background="{DynamicResource OverlayCardBrush}"
BorderBrush="{DynamicResource BorderSubtleBrush}" BorderThickness="1" CornerRadius="8"
MaxWidth="440" Margin="16"
HorizontalAlignment="Stretch" VerticalAlignment="Center">
<StackPanel Margin="24" Spacing="16">
<TextBlock Text="{Binding Loc[help.title]}"
FontSize="18" FontWeight="Bold"/>
<!-- Fixed height so the overlay never resizes when switching tabs. -->
<TabControl Height="320">
<!-- Tab: Info -->
<TabItem Header="{Binding Loc[help.tab.info]}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Spacing="12" Margin="0,12,0,0">
<StackPanel Spacing="4">
<TextBlock Text="Palladium Wallet" FontSize="16" FontWeight="Bold"/>
<TextBlock Text="{Binding WindowTitle}" FontSize="12"
Foreground="{DynamicResource TextSecondaryBrush}"/>
</StackPanel>
<TextBlock Text="{Binding Loc[help.info]}" TextWrapping="Wrap"/>
<Button Content="{Binding Loc[help.bug.report]}"
Click="OnOpenBugReportClick"
HorizontalAlignment="Left"/>
<Border BorderBrush="{DynamicResource BorderSubtleBrush}"
BorderThickness="0,1,0,0" Margin="0,4,0,0"/>
<StackPanel Spacing="4">
<TextBlock Text="© 2026 Davide Grilli — MIT License"
FontSize="12"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<TextBlock Text="Built with .NET 10 · Avalonia 12 · NBitcoin"
FontSize="11"
Foreground="{DynamicResource TextSecondaryBrush}"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
</TabItem>
<!-- Tab: Donate -->
<TabItem Header="{Binding Loc[help.tab.donate]}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Spacing="12" Margin="0,12,0,0">
<TextBlock Text="{Binding Loc[donate.desc]}" TextWrapping="Wrap"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<!-- Dev address (read-only) -->
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[donate.dev.address]}" FontSize="11"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<SelectableTextBlock Text="{x:Static vm:MainWindowViewModel.DevAddress}"
FontFamily="monospace" FontSize="11"
TextWrapping="Wrap"/>
</StackPanel>
<!-- Amount input -->
<StackPanel Spacing="4">
<TextBlock Text="{Binding Loc[donate.amount]}" FontSize="11"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<Grid ColumnDefinitions="*,Auto">
<TextBox Grid.Column="0"
Text="{Binding DonateAmount}"
PlaceholderText="{Binding UnitLabel}"/>
<TextBlock Grid.Column="1" Text="{Binding UnitLabel}"
VerticalAlignment="Center" Margin="8,0,0,0"
Foreground="{DynamicResource TextSecondaryBrush}"/>
</Grid>
</StackPanel>
<!-- Preview -->
<SelectableTextBlock Text="{Binding DonatePreview}"
IsVisible="{Binding DonatePreview,
Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
FontFamily="monospace" FontSize="11"
TextWrapping="Wrap"
Foreground="{DynamicResource TextSecondaryBrush}"/>
<!-- Buttons -->
<StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Right">
<Button Content="{Binding Loc[donate.confirm]}"
Classes="accent"
IsEnabled="{Binding HasPendingDonate}"
Command="{Binding ConfirmDonateCommand}"/>
<Button Content="{Binding Loc[donate.prepare]}"
Command="{Binding PrepareDonateCommand}"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
</TabItem>
</TabControl>
<Button Content="{Binding Loc[addr.close]}"
HorizontalAlignment="Right"
Command="{Binding CloseHelpCommand}"/>
</StackPanel>
</Border>
</Border>
<!-- ============ UPDATE AVAILABLE OVERLAY ============ -->
<!-- Same pattern as the help overlay: instant open/close. -->
<Border Grid.Row="0" Grid.RowSpan="3"
Background="{DynamicResource ScrimBrush}"
Tapped="OnUpdateAvailableOverlayBackdropTapped"
IsVisible="{Binding IsUpdateAvailableOpen}">
<Border Background="{DynamicResource OverlayCardBrush}"
BorderBrush="{DynamicResource BorderSubtleBrush}" BorderThickness="1" CornerRadius="8"
MaxWidth="440" Margin="16"
HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Margin="24" Spacing="16">
<TextBlock Text="{Binding Loc[update.title]}"
FontSize="18" FontWeight="Bold"/>
<TextBlock TextWrapping="Wrap">
<Run Text="{Binding Loc[update.message]}"/>
<Run Text=" "/>
<Run Text="{Binding UpdateAvailableTag}" FontWeight="Bold"/>
</TextBlock>
<StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Right">
<Button Content="{Binding Loc[update.download]}"
Classes="accent"
Click="OnOpenReleasePageClick"/>
<Button Content="{Binding Loc[update.dismiss]}"
Command="{Binding CloseUpdateAvailableCommand}"/>
</StackPanel>
</StackPanel>
</Border>
</Border>
</Grid>
</UserControl>