Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a9ded6497a | |||
| 46aca513b8 | |||
| 6c01d7e6bd | |||
| 3cffba6e98 | |||
| 865daa137d |
@@ -91,6 +91,3 @@ settings.local.json
|
||||
# Local agent/tooling metadata
|
||||
.agents/
|
||||
.codex/
|
||||
CLAUDE.md
|
||||
|
||||
blueprint.md
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Role
|
||||
|
||||
Operate as an **expert in cryptocurrencies and cryptography**: reason with the domain's rigor about UTXO consensus, HD key derivation (BIP32/39/SLIP-132), signature schemes and scripts (P2PKH/P2SH/P2WPKH, PSBT), address encoding (base58/bech32), Merkle/SPV proofs, and at-rest encryption. When a choice touches cryptographic correctness or fund safety, judge it through that lens and flag known risks and pitfalls (nonce reuse, missing validation, exposed keys/seed, wrong fee/coin-selection, unverified server responses). Explain trade-offs with technical precision; never take for granted what hasn't been verified.
|
||||
|
||||
## How to assist
|
||||
|
||||
On **every requested change**, before implementing, judge whether it makes sense and say so plainly: if a request is useful and consistent with the project, proceed; if it is useless, redundant, already covered elsewhere, or risks degrading the code, **say so** with a short rationale and propose the better alternative (or doing nothing). No automatic agreement — an honest opinion is worth more than blind execution.
|
||||
|
||||
## What it is
|
||||
|
||||
SPV desktop wallet (Sparrow-style) for the **Palladium (PLM)** cryptocurrency, a Bitcoin-derived UTXO chain. Windows/Linux only, no mobile. Lightning is excluded from the first release.
|
||||
|
||||
[blueprint.md](blueprint.md) is a **reference for understanding** (consensus parameters verified against the node, algorithms, network protocol): consult it when it helps to understand an area, but **it is no longer binding** — it need not be followed to the letter or read before every change. The source of truth is the current code; the `§` references below point to the blueprint only as further reading.
|
||||
|
||||
## Stack and structure
|
||||
|
||||
.NET 8 + Avalonia UI + NBitcoin.
|
||||
|
||||
```
|
||||
src/Core/ Chain/ Crypto/ Wallet/ Spv/ Net/ Storage/ (no UI dependency)
|
||||
src/App/ Avalonia GUI src/Cli/ CLI tests/ xUnit
|
||||
```
|
||||
|
||||
**Non-negotiable dependency rule:** `App`/`Cli` depend only on `Core`; the UI goes through the wallet domain, never directly through network or cryptography. `Core` knows nothing about the UI.
|
||||
|
||||
## Commands
|
||||
|
||||
.NET 8 SDK lives in `~/.dotnet`: in non-interactive shells, before any `dotnet` command run
|
||||
`export PATH="$HOME/.dotnet:$PATH" DOTNET_ROOT="$HOME/.dotnet"`.
|
||||
|
||||
- Build: `dotnet build`
|
||||
- Tests (headless, the primary verification layer): `dotnet test` — single: `dotnet test --filter "FullyQualifiedName~TestName"`
|
||||
- GUI hot reload: `dotnet watch --project src/App` (on WSL2/WSLg the window shows on the Windows desktop, no graphics dependencies to install)
|
||||
- CLI: `dotnet run --project src/Cli -- <command>` (no args → usage)
|
||||
- Windows publish: `dotnet publish src/App -r win-x64 -p:PublishSingleFile=true --self-contained`
|
||||
- Linux publish: `dotnet publish src/App -r linux-x64 --self-contained` (then AppImage via PupNet Deploy)
|
||||
|
||||
**CLI** (`src/Cli`): `create`/`restore`/`restore-xpub`/`info`; `sync`/`send`/`servers`/`reset-certs` (`--server host:port [--ssl]`); `newseed`/`addresses`. Default wallet file `~/.palladium-wallet/<network>/wallets/default.wallet.json` (`--file` to change it).
|
||||
|
||||
## Architecture (points that require reading multiple files)
|
||||
|
||||
- **Layers (§2):** GUI → wallet domain → SPV/Sync → Network → Cryptography → Persistence; each layer depends only downward.
|
||||
- **Network profile (§3):** all chain constants (address prefixes, BIP32 headers, bech32 HRP, genesis, ports, coin_type 746) **centralized in `Core/Chain`** (`ChainProfiles`/`PalladiumNetworks`), selectable per network (mainnet/testnet/regtest). No scattered magic numbers.
|
||||
- **LWMA / skip PoW (§3, §7):** LWMA difficulty, 2-minute blocks; an SPV client cannot recompute it → `SkipPowValidation = true`, trust anchored to **hardcoded checkpoints** (§7.3). Custom layer: NBitcoin assumes Bitcoin's retargeting.
|
||||
- **NBitcoin vs custom (§19.2):** NBitcoin covers the custom network, BIP32/39, addresses, transactions, PSBT, signing, encoding, hashing — **do not reimplement these**. Hand-written custom code: JSON-RPC client for the indexing server (ElectrumX-like, §10); SPV sync with Merkle verification (§7.4); header/checkpoint validation; coin selection and fee policy; versioned encrypted JSON wallet file.
|
||||
- **PSBT-centric (§6.5):** every signing flow goes through PSBT (offline/air-gapped/multisig/hardware).
|
||||
- **Ports:** 50001/50002 = indexing server (what the SPV wallet talks to), **not** the node's P2P port (2333).
|
||||
|
||||
## GUI conventions (`src/App`)
|
||||
|
||||
- **Single ViewModel** `MainWindowViewModel` (CommunityToolkit.Mvvm: `[ObservableProperty]`, `[RelayCommand]`); `Core` is driven directly from here.
|
||||
- **In-app overlays, not OS windows:** details (address, transaction), settings, and help are full-screen `Border`s gated by an `IsXxxOpen` flag, not separate `Window`s — popups/top-levels are slow on WSLg. Pattern: bool property + Open/Close commands + backdrop handler and Esc key in `MainWindow`'s code-behind. Heavy network work runs off the UI thread (`Task.Run`) so the overlay never freezes.
|
||||
- **Localization:** `Localization/Loc.cs`, key→6 languages dictionary (it/en/es/fr/pt/de); in XAML `{Binding Loc[key]}`, in C# `Loc.Tr("key")`. On language change the `Loc` instance is replaced.
|
||||
- **App version:** single source = `<Version>` in `src/App/PalladiumWallet.App.csproj`; read at runtime (`MainWindowViewModel.AppVersion`) and shown in the title.
|
||||
|
||||
## Implementation state (§16 steps 1–7 + GUI)
|
||||
|
||||
`Core/Chain` network profiles; `Core/Crypto` BIP39/32/SLIP-132/`HdAccount`; `Core/Storage` JSON wallet v1 + AES-GCM (PBKDF2-SHA512) + data paths; `Core/Net` `ElectrumClient` (newline JSON-RPC over TCP/TLS, TOFU in `server-certs.json`, concurrent requests) + `ElectrumApi`; `Core/Spv` scripthash, mandatory Merkle verification on every confirmed tx, sync with gap limit; `Core/Wallet` `TransactionFactory` (RBF on, send-all, watch-only PSBT), `TransactionInspector` (tx detail from the server), `WalletLoader`. GUI: setup wizard, dashboard (history/send/receive with QR+copy/addresses/contacts), transaction detail, settings/server/help, multi-wallet.
|
||||
|
||||
**TODO (§16 steps 8–9):** multisig, hardware wallet, coin control UI, fee ETA/mempool, RBF/CPFP UI, on-disk header chain, multi-server pool, proxy/Tor.
|
||||
|
||||
## Working rules
|
||||
|
||||
- **Cross-implementation tests (§16):** compare addresses, txids, and PSBTs against a reference wallet (golden vectors). A different address or txid is a blocking bug.
|
||||
- **Security (§17):** seed and private keys never in plaintext on disk/logs/network; every server response validated with Merkle + checkpoints; watch-only truly read-only.
|
||||
- *(Optional)* blueprint features may be deferred but must still be considered in the design.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Davide Grilli
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,170 @@
|
||||
# Palladium Wallet
|
||||
|
||||
**An SPV desktop wallet built specifically for the Palladium (PLM) cryptocurrency** and optimized for its chain.
|
||||
|
||||
Unlike generic wallets adapted to many coins, Palladium Wallet is designed around Palladium's consensus parameters — a Bitcoin-derived UTXO chain with 2-minute blocks and LWMA difficulty — and centralizes them in a single network profile. This keeps it lightweight, predictable and faithful to the chain: no client-side difficulty recalculation (trust is anchored to hardcoded checkpoints), mandatory Merkle verification on every confirmed transaction, and a network client written specifically for Palladium's indexing server.
|
||||
|
||||
## Features
|
||||
|
||||
- **Lightweight SPV**: syncs against an indexing server (ElectrumX-like protocol) without downloading the full chain.
|
||||
- **Security**: seed and private keys encrypted on disk (AES-GCM, PBKDF2-SHA512), never in plaintext in logs or on the wire; every server response is validated with Merkle proofs + checkpoints.
|
||||
- **HD wallet** (BIP39/BIP32), SegWit/wrapped/legacy addresses, watch-only from xpub.
|
||||
- **PSBT-centric**: signing flows go through PSBT (offline / air-gapped / multisig).
|
||||
- **Multi-network**: mainnet, testnet, regtest.
|
||||
- **GUI** (Avalonia) and **CLI** on the same core.
|
||||
- **Multilingual**: Italian, English, Spanish, French, Portuguese, German.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
PalladiumWallet.sln
|
||||
├─ src/Core/ Chain/ Crypto/ Wallet/ Spv/ Net/ Storage/ (no UI dependency)
|
||||
├─ src/App/ Avalonia GUI
|
||||
├─ src/Cli/ CLI on the same Core
|
||||
└─ tests/ xUnit
|
||||
```
|
||||
|
||||
Stack: **.NET 8 + Avalonia UI + NBitcoin**.
|
||||
|
||||
---
|
||||
|
||||
## Development environment
|
||||
|
||||
You only need the **.NET 8 SDK**. The core and crypto are fully testable without the GUI or a real network.
|
||||
|
||||
### Windows
|
||||
|
||||
1. Install the .NET 8 SDK:
|
||||
```powershell
|
||||
winget install Microsoft.DotNet.SDK.8
|
||||
```
|
||||
(alternatively, the installer from <https://dotnet.microsoft.com/download/dotnet/8.0>)
|
||||
2. Clone the repository and restore dependencies:
|
||||
```powershell
|
||||
git clone <repo-URL>
|
||||
cd PalladiumWallet
|
||||
dotnet restore
|
||||
```
|
||||
|
||||
### Linux
|
||||
|
||||
1. Install the .NET 8 SDK through your distro's package manager, or without root via the official script:
|
||||
```bash
|
||||
curl -sSL https://dot.net/v1/dotnet-install.sh | bash -s -- --channel 8.0
|
||||
export PATH="$HOME/.dotnet:$PATH" DOTNET_ROOT="$HOME/.dotnet"
|
||||
```
|
||||
(add the two `export` lines to your `~/.bashrc` to make them permanent)
|
||||
2. Clone and restore:
|
||||
```bash
|
||||
git clone <repo-URL>
|
||||
cd PalladiumWallet
|
||||
dotnet restore
|
||||
```
|
||||
|
||||
> The GUI uses Avalonia, which runs natively on both platforms with no extra graphics dependencies.
|
||||
|
||||
---
|
||||
|
||||
## Running it
|
||||
|
||||
**GUI** (with hot reload for development):
|
||||
```bash
|
||||
dotnet watch --project src/App
|
||||
```
|
||||
or a single run:
|
||||
```bash
|
||||
dotnet run --project src/App
|
||||
```
|
||||
|
||||
**CLI** (same core, useful for scripts and headless environments):
|
||||
```bash
|
||||
dotnet run --project src/Cli -- <command>
|
||||
```
|
||||
Run without arguments for the full list of commands.
|
||||
|
||||
---
|
||||
|
||||
## Running tests
|
||||
|
||||
Tests are the **primary verification layer** — the core logic and crypto run headless, without the GUI or a real network.
|
||||
|
||||
Run the whole suite:
|
||||
```bash
|
||||
dotnet test
|
||||
```
|
||||
|
||||
Run a single test (or a group) by name:
|
||||
```bash
|
||||
dotnet test --filter "FullyQualifiedName~TestName"
|
||||
```
|
||||
|
||||
Run only the tests in one project:
|
||||
```bash
|
||||
dotnet test tests/PalladiumWallet.Tests
|
||||
```
|
||||
|
||||
> Cross-implementation tests compare addresses, txids and PSBTs against reference golden vectors: a different address or txid is a blocking bug.
|
||||
|
||||
---
|
||||
|
||||
## Building
|
||||
|
||||
**Development build:**
|
||||
```bash
|
||||
dotnet build
|
||||
```
|
||||
|
||||
**Windows publish** (single self-contained executable):
|
||||
```bash
|
||||
dotnet publish src/App -r win-x64 -p:PublishSingleFile=true --self-contained
|
||||
```
|
||||
|
||||
**Linux publish** (self-contained; the AppImage is then produced with [PupNet Deploy](https://github.com/kuiperzone/PupNet-Deploy)):
|
||||
```bash
|
||||
dotnet publish src/App -r linux-x64 --self-contained
|
||||
```
|
||||
|
||||
The application **version** is set in a single place: the `<Version>` tag in [`src/App/PalladiumWallet.App.csproj`](src/App/PalladiumWallet.App.csproj). It appears in the window title and is stamped into the published binaries.
|
||||
|
||||
---
|
||||
|
||||
## User guide (quick)
|
||||
|
||||
### First launch
|
||||
1. On first launch, choose **where to store data** (wallet, configuration, certificates) — the default path or a folder of your choice.
|
||||
2. Create a new wallet, restore from seed, or open an existing wallet.
|
||||
3. If you create a wallet, **write the seed phrase down on paper**: it will not be shown again. You can protect the file with a password.
|
||||
|
||||
### Main tabs
|
||||
- **History** — list of transactions. *Double-click* a row to open the full detail (amount, fee, addresses, sizes, confirmations).
|
||||
- **Send** — recipient + amount (or "send all"), adjustable fee; for watch-only wallets a PSBT is produced to be signed offline.
|
||||
- **Receive** — next unused address, with a **QR code** and a **Copy** button.
|
||||
- **Addresses** — all derived addresses with balances; click for details (keys, derivation path).
|
||||
- **Contacts** — address book with labels.
|
||||
|
||||
### Connection
|
||||
- The status indicator at the bottom shows the connection to the **indexing server**; tapping it opens the server settings.
|
||||
- Sync is SPV: it downloads only what concerns your wallet and verifies every confirmed transaction with a Merkle proof.
|
||||
|
||||
### Settings and Help
|
||||
- **Settings**: language, display unit (PLM / mPLM / µPLM / sat), server.
|
||||
- **Help**: software information and version.
|
||||
|
||||
### CLI in brief
|
||||
```bash
|
||||
# Wallet
|
||||
dotnet run --project src/Cli -- create [--words 12|24] [--kind segwit|wrapped|legacy] [--net mainnet|testnet|regtest] [--password P]
|
||||
dotnet run --project src/Cli -- restore "<mnemonic>" [...]
|
||||
dotnet run --project src/Cli -- info [--net ...] [--password P]
|
||||
|
||||
# Network
|
||||
dotnet run --project src/Cli -- sync [--server host[:port]] [--ssl]
|
||||
dotnet run --project src/Cli -- send --to ADDRESS (--amount X | --all) [--feerate sat/vB] [--broadcast]
|
||||
```
|
||||
The default wallet file is `~/.palladium-wallet/<network>/wallets/default.wallet.json` (override with `--file`).
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
Released under the MIT License. See the [LICENSE](LICENSE) file.
|
||||
@@ -51,6 +51,15 @@ public sealed class Loc
|
||||
["menu.net.discover"] = ["Cerca altri server (peer)", "Discover servers (peers)", "Buscar otros servidores (peers)", "Rechercher d'autres serveurs (pairs)", "Procurar outros servidores (peers)", "Weitere Server suchen (Peers)"],
|
||||
["menu.net.resetcerts"] = ["Reset certificati SSL", "Reset SSL certificates", "Restablecer certificados SSL", "Réinitialiser les certificats SSL", "Redefinir certificados SSL", "SSL-Zertifikate zurücksetzen"],
|
||||
["menu.settings"] = ["_Impostazioni", "_Settings", "_Configuración", "_Paramètres", "_Configurações", "_Einstellungen"],
|
||||
["menu.help"] = ["_Help", "_Help", "_Ayuda", "_Aide", "_Ajuda", "_Hilfe"],
|
||||
["help.title"] = ["Informazioni sul software", "About this software", "Información del software", "À propos du logiciel", "Sobre o software", "Über die Software"],
|
||||
["help.info"] = [
|
||||
"Wallet SPV per la criptovaluta Palladium (PLM).",
|
||||
"SPV wallet for the Palladium (PLM) cryptocurrency.",
|
||||
"Monedero SPV para la criptomoneda Palladium (PLM).",
|
||||
"Portefeuille SPV pour la cryptomonnaie Palladium (PLM).",
|
||||
"Carteira SPV para a criptomoeda Palladium (PLM).",
|
||||
"SPV-Wallet für die Kryptowährung Palladium (PLM)."],
|
||||
["settings.unit.short"] = ["Unità", "Unit", "Unidad", "Unité", "Unidade", "Einheit"],
|
||||
|
||||
// Wizard
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<!-- Versione dell'applicazione: unico punto da modificare. Compare nel
|
||||
titolo della finestra ed è incisa nei binari pubblicati. -->
|
||||
<Version>0.9.0</Version>
|
||||
<Nullable>enable</Nullable>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<ApplicationIcon>Assets\logo.ico</ApplicationIcon>
|
||||
|
||||
@@ -73,6 +73,15 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
private Loc _loc = Loc.Instance;
|
||||
public Loc Loc => _loc;
|
||||
|
||||
/// <summary>Versione dell'app (da <Version> nel csproj), formato Major.Minor.Patch.</summary>
|
||||
public static string AppVersion =>
|
||||
typeof(MainWindowViewModel).Assembly.GetName().Version is { } v
|
||||
? $"{v.Major}.{v.Minor}.{v.Build}"
|
||||
: "";
|
||||
|
||||
/// <summary>Titolo della finestra con la versione, es. "Palladium Wallet 0.9.0".</summary>
|
||||
public string WindowTitle => $"Palladium Wallet {AppVersion}";
|
||||
|
||||
/// <summary>Unità corrente per il campo importo del pannello Invia.</summary>
|
||||
public string UnitLabel => _config.Unit;
|
||||
|
||||
@@ -309,6 +318,16 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
[RelayCommand]
|
||||
private void CloseSettings() => IsSettingsOpen = false;
|
||||
|
||||
/// <summary>Overlay informazioni sul software (menu Help).</summary>
|
||||
[ObservableProperty]
|
||||
private bool isHelpOpen;
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenHelp() => IsHelpOpen = true;
|
||||
|
||||
[RelayCommand]
|
||||
private void CloseHelp() => IsHelpOpen = false;
|
||||
|
||||
[ObservableProperty]
|
||||
private string connectionStatus = Loc.Tr("conn.none");
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
x:DataType="vm:MainWindowViewModel"
|
||||
Icon="/Assets/logo.ico"
|
||||
Width="900" Height="620"
|
||||
Title="Palladium Wallet">
|
||||
Title="{Binding WindowTitle}">
|
||||
|
||||
<Design.DataContext>
|
||||
<vm:MainWindowViewModel/>
|
||||
@@ -28,6 +28,8 @@
|
||||
</MenuItem>
|
||||
<MenuItem Header="{Binding Loc[menu.settings]}"
|
||||
Command="{Binding OpenSettingsCommand}"/>
|
||||
<MenuItem Header="{Binding Loc[menu.help]}"
|
||||
Command="{Binding OpenHelpCommand}"/>
|
||||
</Menu>
|
||||
|
||||
<!-- ============ WIZARD DI SETUP (§15): un passo alla volta ============ -->
|
||||
@@ -714,5 +716,33 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
<!-- ============ OVERLAY HELP / INFORMAZIONI ============ -->
|
||||
<!-- Stesso pattern dell'overlay impostazioni: apertura/chiusura istantanee. -->
|
||||
<Border Grid.Row="0" Grid.RowSpan="3"
|
||||
Background="#99000000"
|
||||
Tapped="OnHelpOverlayBackdropTapped"
|
||||
IsVisible="{Binding IsHelpOpen}">
|
||||
<Border Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
|
||||
BorderBrush="Gray" BorderThickness="1" CornerRadius="8"
|
||||
Width="460"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<StackPanel Margin="24" Spacing="16">
|
||||
<TextBlock Text="{Binding Loc[help.title]}"
|
||||
FontSize="18" FontWeight="Bold"/>
|
||||
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock Text="Palladium Wallet" FontSize="16" FontWeight="Bold"/>
|
||||
<TextBlock Text="{Binding WindowTitle}" FontSize="12" Foreground="Gray"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Text="{Binding Loc[help.info]}" TextWrapping="Wrap"/>
|
||||
|
||||
<Button Content="{Binding Loc[addr.close]}"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{Binding CloseHelpCommand}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -128,6 +128,13 @@ public partial class MainWindow : Window
|
||||
vm.IsSettingsOpen = false;
|
||||
}
|
||||
|
||||
private void OnHelpOverlayBackdropTapped(object? sender, TappedEventArgs e)
|
||||
{
|
||||
if (!ReferenceEquals(e.Source, sender)) return;
|
||||
if (DataContext is MainWindowViewModel vm)
|
||||
vm.IsHelpOpen = false;
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Escape && DataContext is MainWindowViewModel vm)
|
||||
@@ -136,6 +143,7 @@ public partial class MainWindow : Window
|
||||
if (vm.AddressInfo is not null) { vm.AddressInfo = null; e.Handled = true; return; }
|
||||
if (vm.IsServerSettingsOpen) { vm.IsServerSettingsOpen = false; e.Handled = true; return; }
|
||||
if (vm.IsSettingsOpen) { vm.IsSettingsOpen = false; e.Handled = true; return; }
|
||||
if (vm.IsHelpOpen) { vm.IsHelpOpen = false; e.Handled = true; return; }
|
||||
}
|
||||
base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user