Commit Graph

65 Commits

Author SHA1 Message Date
davide af2fdcc894 fix(gui): route runtime status messages through Loc instead of hardcoded Italian
Send/Donate/Sync/Wizard view models wrote status/error strings
directly in Italian ("Importo non valido.", "Errore: …", the TLS
pin-mismatch message, …), so a user running the app in English/German/
etc. still saw them in Italian despite the 6-language Loc system
covering the rest of the UI.

Added the missing Loc keys and routed every occurrence through
Loc.Tr(...). CertificatePinMismatchException (Core) no longer bakes an
Italian message into .Message; it now exposes Host/Port so the UI can
translate it, with an English fallback for any exception type the UI
doesn't specifically handle. Also drops the now-stale USERGUIDE.md
caveat about a few messages staying in Italian regardless of language.
2026-07-07 14:45:43 +02:00
davide 15978bf564 chore(release): bump version to 0.9.1
Fills in the CHANGELOG.md entry for the test-suite expansion and bug fixes
since 0.9.0 (see previous commits), and bumps <Version>/versionCode across
the App and Android head csproj files ahead of the tag.
2026-07-02 23:25:48 +02:00
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
davide 2d017231eb chore(github): complete issue/PR templates and wire in-app bug report to them
Add a feature request template and a config.yml (blank issues disabled,
security reports routed to SECURITY.md) alongside the existing bug report
template, plus a PR template with a checklist matching the project's
security rules. Point the app's "Report a bug" button at the new GitHub
template now that the repo is hosted there instead of the old self-hosted
tracker.
2026-07-02 21:22:42 +02:00
davide e7797017f6 feat(gui): split server discovery into its own always-usable button
Previously the server settings overlay had only "Connect and sync" and
"Reset certs" — peer discovery had no button and only ran implicitly
when reopening the dialog while connected. Add a dedicated "Sincronizza"
button wired to DiscoverServersCommand, and make it work even without
an active connection by opening a short-lived connection to a candidate
server just to query its peer list, then closing it without touching
the wallet's connection state.
2026-07-02 19:24:16 +02:00
davide 6c24a8bb46 feat(wallet): surface immature balance separately from confirmed
Extract confirmation-threshold logic into UtxoSpendability (shared by
TransactionFactory and WalletSynchronizer) and use it to compute
ImmatureSats, so coinbase/under-confirmed amounts are shown separately
from spendable balance in the GUI and CLI instead of being lumped into
"confirmed".
2026-07-02 18:24:33 +02:00
davide 9aecdb1aaa feat(wallet): enforce confirmation thresholds before spending UTXOs
Coinbase outputs require COINBASE_MATURITY + 1 = 121 confirmations before
they can be spent. The consensus rule (palladiumcore tx_verify.cpp) is
nSpendHeight - nHeight >= 120; the +1 adds one block of safety margin,
matching the Qt wallet (wallet.cpp: COINBASE_MATURITY+1). Regular UTXOs
require MinConfirmations (6 on mainnet, 1 on testnet/regtest — wallet
policy, no consensus rule).

Changes:
- ChainProfile: add CoinbaseMaturity and MinConfirmations fields
- ChainProfiles: mainnet CoinbaseMaturity=120 / MinConfirmations=6;
  testnet/regtest inherit 120, override MinConfirmations=1
- PalladiumNetworks: align NBitcoin Consensus.CoinbaseMaturity to 120
- CachedUtxo: add IsCoinbase flag (ElectrumX listunspent does not expose
  this; derived from Transaction.IsCoinBase on the raw tx)
- WalletSynchronizer: set IsCoinbase during local UTXO reconstruction
- TransactionFactory.Build: add tipHeight param; filter by threshold;
  error reports reason (immature coinbase with X/121 counter, under-
  confirmed regulars with best-seen count, mempool amounts)
- All Build() call sites updated (App, Donate, CLI)
- Two new tests: coinbase maturity boundary (119→220 tip) and mainnet
  min-conf boundary (5→6 confirmations)
2026-07-02 16:25:42 +02:00
davide 2be43d3177 fix(sync): allow server change at any time, even during an active sync
When IsSyncing and the user requests a different server, cancel the
running CancellationTokenSource instead of silently ignoring the click.
OperationCanceledException is caught separately (not an error), and
ConnectAndSync restarts automatically with the new server after the
cancelled task unwinds.

The "Connect" button no longer has IsEnabled="{Binding !IsSyncing}":
it is always clickable, and clicking it with the same server sets
_resyncRequested as before. CancellationToken is now passed to both
ElectrumClient.ConnectAsync and WalletSynchronizer.SyncOnceAsync.
2026-07-02 14:56:58 +02:00
davide ef60ec0330 loc: expand About description to include non-custodial and local security details
All six languages updated: the one-liner was replaced with a two-sentence
description that names the network (PLM), the lightweight SPV design,
the non-custodial model, and the local encryption guarantee.
2026-07-02 12:29:38 +02:00
davide 4d730d7860 feat(ui): add bug report button in Help and GitHub issue template
Adds a "Report a bug" / "Segnala un bug" button (6 languages) in
Help → Info that opens the issue tracker in the system browser via
TopLevel.Launcher (cross-platform: desktop + Android).
URL points to the current self-hosted repo; a TODO comment marks
the constant for easy update once the project moves to GitHub.

Adds .github/ISSUE_TEMPLATE/bug_report.yml — GitHub issue form with
structured fields for version, platform, network, repro steps,
expected behavior, and logs.
2026-06-24 15:41:37 +02:00
davide 068d85e0e2 feat(ui): show full tx inputs/outputs in detail overlay without truncation
Input rows switch to a two-line layout (full prev-txid:index on the first
line, address + amount on the second) so nothing is cut by a fixed 160px
column. Output address column gets TextWrapping instead of TextTrimming.
Overlay grows to MaxWidth=660/MaxHeight=800 for better desktop use; the
same XAML wraps naturally on narrow mobile screens.
Removes the Shorten() helper that was masking input references.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 15:37:43 +02:00
davide 1f857ddf52 refactor(ui): remove SPV badge from tx detail and history
SPV is the only verification method the wallet uses, so showing it as a
badge adds no information — if a transaction appears confirmed, it has
already passed Merkle verification.

- Remove the SPV chip from the transaction detail overlay
- Remove the Verificata column from the history list (desktop and mobile)
- Drop VerifiedText property from TransactionDetailsViewModel
- Drop the Verificata field from HistoryRow
- Remove the orphaned tx.verified loc key
2026-06-22 00:02:47 +02:00
davide 2953ba35ba refactor(ui): remove redundant "Open wallet from file" menu item
The wizard already covers opening existing wallets; a separate file-picker
entry in the menu duplicates that flow without adding value. Remove the menu
item, its click handler, the OpenFromPath VM method, and the loc key.
2026-06-21 22:46:23 +02:00
davide c0193ae440 feat(ui): add Quit to File menu; hide Wallet item when no wallet is open
- File → Esci (separator + Quit at the bottom) calls IClassicDesktopStyleApplicationLifetime.Shutdown()
- Wallet menu item and mobile button now use IsVisible instead of IsEnabled:
  they disappear entirely on the wizard/setup screen and reappear once a
  wallet is open, avoiding a confusing greyed-out entry
2026-06-21 22:34:59 +02:00
davide affeaccae8 feat(ui): password-gated private key reveal in address detail overlay
Replace the direct private-key display in the address detail overlay with a
two-step reveal flow:

- Unencrypted wallet: clicking Show reveals the WIF key immediately.
- Encrypted wallet: clicking Show opens a modal password-prompt overlay; the
  key is displayed only if the entered password matches the wallet file
  password; a wrong password shows an inline error without closing the prompt.

Hiding the key (Hide button) clears the revealed state. Closing the address
overlay or opening a different one always resets both the reveal state and
the password prompt, so no sensitive material lingers between sessions.
2026-06-21 22:28:33 +02:00
davide 4a97172346 feat(ui): Wallet menu overlay — info, xpub, and password-protected seed reveal
Add a Wallet menu item (between File and Settings) that opens an in-app
overlay showing wallet metadata: file name, network, type (HD seed / xprv /
WIF / watch-only), script kind, derivation path, master fingerprint, and
account xpub.

Includes a seed section: if the wallet has no seed, a note is shown; if it
has one and is unencrypted the seed is revealed directly; if encrypted the
user must enter the wallet file password before the mnemonic is displayed.
The revealed seed is shown in a danger-coloured bordered box with a warning.
Closing the overlay (backdrop, Close button, or Esc) always clears the
password input and hides the seed.

Also adds the private-key password-prompt overlay and its localization keys
(shared UI scaffolding used by the following commit).
2026-06-21 22:27:55 +02:00
davide c5290a1796 feat(ui): persist last server and split connection status text
- AppConfig stores LastServerHost/Port/UseSsl; restored on next launch
  so the wallet reconnects to the last-used server instead of defaulting
  to the first bootstrap entry
- Added ConnectionStatusShort for the status bar (short label only);
  ConnectionStatus retains the full "Connesso a ip:porta" shown in the
  server settings overlay
2026-06-18 19:34:41 +02:00
davide 227ec62d53 feat(ui): simplify server panel — remove TLS/Discover, show full address in status
- Removed TLS checkbox (SSL still used internally, just not exposed in UI)
- Removed "Discover servers" button (discovery now runs automatically on panel open)
- ConnectionStatus now includes host:port when connected ("Connesso a ip:porta")
- Replaced status ellipse+text with a single TextBlock
2026-06-18 19:29:33 +02:00
davide 96b6a7e291 feat(ui): Help overlay with Info/Donate tabs, fixed dimensions
Help button now shows a fixed-size two-tab overlay:

Info tab: app name + version, one-line description, thin separator,
  © 2026 Davide Grilli — MIT License, Built with .NET 10 · Avalonia 12 · NBitcoin.

Donate tab: dev address (plm1qdq3…) read-only display, free amount
  input, prepare-preview row, confirm-and-broadcast button.

Overlay dimensions are stable across tabs and between desktop/mobile:
- TabControl Height=320 (fixed, never resizes on tab switch)
- HorizontalAlignment=Stretch + MaxWidth=440 on the card (always fills
  available width; previously Center caused the card to shrink to content
  width, which differed between Info and Donate)
- Each tab content wrapped in ScrollViewer for safe overflow handling

New MainWindowViewModel.Donate.cs: PrepareDonate / ConfirmDonate /
ResetDonate (called on overlay close). Six-language Loc keys added for
all donate strings.
2026-06-17 09:01:06 +02:00
davide b13b66160c feat(ui): donate tab in Help overlay
Help button now shows a two-tab overlay:
- Info: existing about/version content
- Donate: donate flow to the dev address (plm1qdq3…) with free amount
  input, prepare preview, and confirm-and-broadcast button

New MainWindowViewModel.Donate.cs partial: DonateAmount / DonatePreview
/ HasPendingDonate observable properties, PrepareDonateCommand (builds
the tx with hardcoded dev address via the existing TransactionFactory),
ConfirmDonateCommand (broadcasts and syncs). Donate state is reset on
overlay close. Six-language Loc keys added (help.tab.info/donate,
donate.desc/dev.address/amount/prepare/confirm).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 08:40:02 +02:00
davide 9744619eb4 fix(ui): mobile tab bar — uniform sizing, indicator overlap, spacing
- UniformGrid Columns="5" on TabStrip so all 5 tabs always stay on one
  row on Android (Rows="1" alone did not auto-calculate columns at
  measure time on the Android Avalonia renderer)
- Contacts tab header was using hardcoded FontSize=13/Width=24/Spacing=4
  instead of the TabFontSize/TabIconSize/TabSpacing ViewModel bindings,
  making it visually larger than the other four tabs on mobile
- ContentPresenter Margin="0,0,0,4" in Controls.axaml pushes the tab
  header content above the 2 px selection indicator, which is drawn at
  the absolute bottom of the template Panel and was overlapping the label
- TabItem horizontal padding 4→8 px and TabSpacing 2→4 for both
  desktop and mobile to give more breathing room between tab items
2026-06-17 08:17:56 +02:00
davide 5d2d0ad312 feat(ui): two-column desktop layout for Send/Receive, fixed window size
Send tab: Recipient and Amount cards side-by-side on desktop using a
two-column Grid, making full use of the available window width.
Summary card and action buttons remain full-width below both columns.
Mobile retains the original vertical stack.

Receive tab: QR card on the left (auto-width), address card expands
to fill the right column on desktop — standard fintech wallet pattern.
Mobile retains QR above, address below.

Window enlarged to 1020×680 (from 900×620) and fixed with CanResize=False;
Min/Max dimensions match so the resize handle never appears.
2026-06-16 15:04:10 +02:00
davide 3d5a226a5a docs: translate all code comments to English (language policy)
Translate every Italian /// XML doc comment, <!-- --> XAML comment, and
// inline comment to English across all source files (Core, App, tests).
Add the language policy to CLAUDE.md (conversation Italian; all code
and docs English). Update one test assertion that checked an Italian
exception message that was also translated.
2026-06-16 14:40:11 +02:00
davide 4b82a0852c feat(ui): restructure transaction details + clearer coinbase identification
Transaction details overlay redesigned from a flat 12-row grid into a
scannable layout: a header with the net amount in focus plus SPV/status
badges, then three labeled cards (Overview, Amounts & fees, Technical
details), with inputs/outputs as carded rows. Adds a .section heading
style and tx.sect.* localization keys (6 languages).

Coinbase transactions are now explicitly identified: an amber "Coinbase"
badge in the header, and the misleading "From: —" / "coinbase —" replaced
with "Newly generated (mining)" (localized). Exposes IsCoinbase on the
details view model; no consensus/logic change, presentation only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 10:21:50 +02:00
davide 41eb1bb788 feat(ui): centralized design system — blue/green tokens, gradient hero, SVG tab icons
Replace raw Fluent defaults + inline hex with a semantic design system,
themed for both light/dark (follows system variant).

- Styles/Theme.axaml: semantic brushes (surface/text/border/status) per
  variant via ThemeDictionaries; override Fluent SystemAccentColor to brand
  blue (#2563EB) and ControlCornerRadius=8; blue->indigo balance gradient.
- Styles/Controls.axaml: reusable classes (card, balance-hero, chip, ghost),
  type scale (amount with tabular figures, mono, h1, label), button/input
  polish, tab selected-in-blue, list hover/selected tints, card hover-lift.
- MainView: balance shown as gradient hero card; dashboard tabs use Material
  SVG Path icons (Fill tracks TabItem selection) instead of Unicode glyphs;
  all hardcoded colors (LimeGreen/IndianRed/Orange/Gray/#99000000) and
  generic Fluent surface brushes mapped to semantic tokens; app background,
  stronger modal scrim, status bar top border.

Palette derived from the logo (blue=trust, green=money). Build clean, app
starts with no binding errors.
2026-06-16 10:08:39 +02:00
davide 5ff2075a45 perf(spv): incremental discovery, parallel chains, persistent header cache
Speed up wallet synchronization, especially re-syncs of large wallets:

- Incremental address discovery: known indices from the previous sync
  (NextReceiveIndex/NextChangeIndex) are fetched in a single parallel
  burst instead of sequential gap-limit rounds, cutting round-trips from
  O(used/gapLimit) to ~O(1). Receive and change chains scan in parallel.
- Merge tx download and Merkle verification into one parallel phase
  (no barrier between them); the per-call semaphore is dropped in favor
  of the transport-level in-flight gate.
- Persist verified block headers (SyncCache.BlockHeaders, height -> hex):
  immutable once stored, so blockchain.block.header round-trips for
  already-verified proofs are eliminated on later syncs.
- RetryOnBusyAsync with exponential backoff around server calls, treating
  -101/-102 and "server busy"/"excessive resource usage" as transient.

Wire the new PreloadCaches/ExportCaches signatures through the desktop
view model and the CLI sync command.
2026-06-16 09:27:48 +02:00
davide 14ae39c5aa feat(ui): wallet name field in creation wizard
The last wizard step (password) now shows an optional name field.
The name becomes the filename: <name>.wallet.json in the wallets
directory. Invalid filename characters are replaced with '_'.

If left empty, the existing auto-name logic applies (default.wallet.json,
wallet-2.wallet.json, …). If the chosen name already exists the wizard
shows an error instead of overwriting.

The name field is visually separated from the encryption section by a
distinct background panel and a horizontal divider.
2026-06-15 15:05:53 +02:00
davide 3054a9baaa fix(spv): resilient sync — GetHistory discovery, tx cache, auto-connect fallback
Three interrelated fixes for sync reliability on large wallets:

1. Prevent -101 "excessive resource usage":
   ScanChainAsync uses GetHistoryAsync for discovery instead of
   SubscribeScripthashAsync. Subscriptions are limited to the gap window
   only (≤ 2×gapLimit addresses), so the session subscription count stays
   constant regardless of wallet history size — eliminating -101 on
   wallets with 1000+ historical addresses.

2. Persist and resume tx cache across sessions and disconnects:
   WalletSynchronizer gains ExportCaches() and PreloadCaches(). On every
   successful sync (and on partial failure) the downloaded raw tx hex and
   verified Merkle heights are written to SyncCache.RawTxHex/VerifiedAt
   in the wallet file. On reconnect or app restart the new synchronizer
   is pre-populated from disk so only new transactions are downloaded.
   _synchronizer is always nulled on reconnect (new client = new instance)
   while caches survive via the wallet file.

3. Auto-connect with multi-server fallback and retry:
   ConnectAndSync iterates BuildServerCandidates() (current server first,
   then all KnownServers in order) and stops at the first that responds.
   _autoReconnect and _syncFailed are set in OpenLoaded() so the keepalive
   timer (20 s) retries automatically on any failure — connection error,
   -101 mid-download, or timeout. PersistPartialTxCache() saves whatever
   was downloaded so each retry resumes from its checkpoint.
2026-06-15 14:23:31 +02:00
davide 4dc37f1b42 feat(ui): wizard flows for xpub/xprv and WIF import
Add two new import paths to the setup wizard:

- "Importa xpub / xprv": accepts any SLIP-132 key (xpub/ypub/zpub or
  xprv/yprv/zprv), auto-detects script type from the version prefix,
  and saves a watch-only or fully-spendable HD account accordingly.

- "Importa chiave WIF": accepts one or more WIF private keys (one per
  line), goes through script-type selection, and saves an ImportedKeyAccount.

WizardFlowKind enum (New, Restore, ImportXkey, ImportWif) governs which
steps are shown; WizardBack() updated for all four flows. OpenLoaded()
updated to accept IWalletAccount and display wallet kind tags
(watch-only / imported) in the network info line.

Localization keys added for all six languages (it/en/es/fr/pt/de).
ViewModel._account changed from HdAccount? to IWalletAccount?.
2026-06-15 14:23:08 +02:00
davide 002c854497 feat(wallet): add P2PKH, P2SH-P2WPKH, and P2TR (Taproot/BIP86) address types
- ScriptKind.Taproot (BIP86, purpose 86', P2WPKH → witness v1 bech32m plm1p)
  derivation wired in DerivationPaths and ChainProfiles (xprv/xpub headers,
  no SLIP-132 standard for P2TR); NBitcoin ScriptPubKeyType.TaprootBIP86
- Wizard step StepScriptType inserted between passphrase and password: 4 radio
  buttons (Legacy / Wrapped SegWit / Native SegWit★ / Taproot), localized in
  6 languages; CreateOrRestore now uses SelectedScriptKind instead of hardcoded
  NativeSegwit; back navigation updated accordingly
- BIP86 golden-vector tests (Bip86TaprootTests): output key verified against
  official Bitcoin BIP86 addresses, plm1p prefix asserted
2026-06-15 12:03:52 +02:00
davide 8b960458ee feat(ui): server overlay full-screen on mobile, larger touch targets
On mobile the server settings panel now fills the entire screen
(no MaxWidth/MaxHeight/Margin constraints, no rounded corners) using
a desktop-only Border.desktop-overlay style so inline values never
fight the style system. Input font sizes increased (11→13/15px),
known-server list taller (200→300px) and server hostname larger (13→15px).
Desktop layout unchanged.
2026-06-15 10:59:12 +02:00
davide 1b784a6c73 fix(ui): prevent text overflow on mobile
UnconfirmedText (pending/unconfirmed balance) now wraps instead of
clipping horizontally. Contact name in mobile list gets CharacterEllipsis
trimming for arbitrarily long user-defined names.
2026-06-15 10:58:42 +02:00
davide 200c12651b feat(ui): hardcode mainnet, remove network selector
Drop the mainnet/testnet/regtest ComboBox from the wizard and fix
Net to always return NetKind.Mainnet. Core profiles and CLI --net
flag are untouched; the wallet file still records the network name.
2026-06-15 10:35:40 +02:00
davide 5f983ca84e feat(ui): replace emoji camera button with vector path icon
Uses a Material Design outline camera path (body + lens) inside a Viewbox
so the icon scales cleanly and inherits the theme foreground color.
2026-06-15 10:25:46 +02:00
davide 53ecd701c0 feat(ui): unified tab bar with icons on desktop and mobile
Replace the mobile-only custom bottom Grid nav bar with a single styled
TabStrip that works the same on both platforms: icon + label headers,
gray background, UniformGrid for equal-width columns.
Also adds the 📷 scan button in the Send address field (visible on mobile,
triggers ScanQrCommand wired in the previous commit).
2026-06-15 10:19:41 +02:00
davide bfee0dde03 feat(android): QR code scanner for Send address field
Camera2 + ZXing.Net 0.16.9 activity (ScannerActivity) that captures JPEG
frames at ~2.5 fps and decodes QR codes without Xamarin dependencies.
Result is returned to MainActivity via OnActivityResult/TaskCompletionSource.
PlatformServices.ScanQrAsync seam wires Android head to shared App layer;
ScanQrCommand in the Send ViewModel strips BIP21 URI scheme/query parameters.
CAMERA permission and uses-feature added to AndroidManifest.
2026-06-15 10:19:29 +02:00
davide 1914d9462b feat(ui): replace mobile tab strip with custom full-width nav bar
Avalonia's TabStrip ignores HorizontalAlignment overrides when placed at
Bottom, causing the items to cluster left. Replace it with a dedicated
Grid (ColumnDefinitions="*,*,*,*,*") that guarantees 5 equal columns
across the full width. The built-in TabStrip is hidden on mobile; buttons
in the custom bar call SelectTabCommand to drive SelectedTabIndex on the
TabControl.
2026-06-15 08:49:44 +02:00
davide 4c0edde4f7 feat(ui): mobile polish — tab bar icons, server overlay, status text
Tab bar: replace plain Header strings with icon+label StackPanels
(≡ ↑ ↓ ⊙ ⊕); style TabStrip with UniformGrid so all five tabs share
the full width equally; center content and add touch padding. Icons
are visible only on mobile (IsMobile binding); desktop keeps text only.

Server settings overlay: dual layout for host/port/TLS input (vertical
on mobile: host full-width, port+TLS side by side) and for action
buttons (stacked full-width on mobile). Known-server list item shows
host on one line and tcp/ssl ports on the line below on mobile.

Connection status bar: simplify to "connesso" / "non connesso" —
remove the "a host:port" suffix from the connected state and
consolidate all disconnected/error/cert-changed states to conn.none.
2026-06-14 22:05:30 +02:00
davide cfc48ff86f feat(ui): responsive layout for portrait mobile (Android)
Add IsMobile property and BoolToTabPlacementConverter; wire
TabStripPlacement to move tabs to the bottom on mobile (standard
Android pattern). Hide the desktop menu bar on mobile and expose
Settings/Help via a compact header row instead.

Replace fixed-width multi-column grids with dual desktop/mobile
templates in History, Addresses and Contacts lists; adapt Send,
Receive and the add-contact form for narrow screens. Remove hard-coded
Width from all five overlays (560–640 px) in favour of MaxWidth +
Margin="16" so they never overflow a 360 px screen; add ScrollViewer
to Address info, Server settings and Settings overlays.

Desktop layout is unchanged: all differences are gated on the
IsMobile/IsDesktop bool, which is a platform constant (false on desktop).
2026-06-14 19:54:11 +02:00
davide 2ddc0f920c refactor(app): split MainWindowViewModel into partial files by area
1300-line monolith → 7 focused files (max 359 lines each):
  MainWindowViewModel.cs         core fields, constructor, lifecycle
  MainWindowViewModel.Wizard.cs  setup wizard and wallet open/close flows
  MainWindowViewModel.Settings.cs language, unit, overlay flags
  MainWindowViewModel.Sync.cs    server config, connection, sync loop
  MainWindowViewModel.Send.cs    send panel
  MainWindowViewModel.Contacts.cs contact management
  MainWindowViewModel.Receive.cs balance display, QR, addresses, tx details

No behaviour change, no XAML change, 127/127 tests pass.
2026-06-13 21:15:07 +02:00
davide 58645bd7a7 fix(storage): acquire wallet lock before load and before CloseWallet
Three improvements to WalletLock integration:

1. OpenExisting acquires the lock before decrypting the file — no point
   attempting a potentially expensive PBKDF2 derivation for a wallet
   already held by another instance.

2. OpenFromPath acquires the lock before CloseWallet — if the new wallet
   is unavailable, the current session stays intact instead of being
   silently destroyed.

3. OpenLoaded now receives an already-acquired WalletLock from the caller
   instead of acquiring it internally; TryAcquireWalletLock is removed.
   Callers that fail to open (wrong password, I/O error) dispose the lock
   in their catch blocks.

Also fix CoinAmount.Of() to throw ArgumentException for unknown units
instead of silently falling back to PLM.
2026-06-13 20:50:22 +02:00
davide 0f8a764a44 feat(storage): add WalletLock to prevent concurrent wallet access
Introduces an exclusive file lock (FileShare.None on wallet.json.lock)
held for the duration of a wallet session. A second instance trying to
open the same file receives null from TryAcquire and sees a localized
error message; UnauthorizedAccessException (OS permission issues)
propagates separately so the UI can show a distinct message.
The .lock file is deleted on Dispose (best-effort); the real guard is
the open FileStream, which the OS releases automatically on crash.
2026-06-13 20:34:06 +02:00
davide b00c5821f2 refactor(app): clear status bar during wizard steps
Remove the wizard-step status messages (welcome, password info,
seed instructions, etc.) — the XAML headings for each step already
communicate context. StatusMessage is now reserved for actionable
feedback (errors, sync progress, connection state).
2026-06-12 16:16:37 +02:00
davide e94eaf7700 refactor(arch): split App into shared library + Desktop + Android heads
The Avalonia UI code (App, Views, ViewModels, Localization, Assets) now
lives in src/App as a plain library (no OutputType). Two thin heads
reference it:

- src/App.Desktop/ — WinExe, Avalonia.Desktop, hosts MainView in a
  MainWindow; carries Program.cs and app.manifest (moved from src/App)
- src/App.Android/ — net10.0-android, Avalonia.Android, MainActivity/
  MainApplication; targets API 23+, EmbedAssembliesIntoApk=true so the
  apk is self-contained for sideloading

All event handlers and TopLevel-dependent calls (file picker, clipboard,
folder picker) moved from MainWindow.axaml.cs into the new shared
MainView.axaml.cs (UserControl), using TopLevel.GetTopLevel(this) so
they work on both platforms. Esc/Back key handling is also in MainView.
MainWindow becomes a thin shell that hosts MainView.

Framework bump: all projects move to net10.0; Cli and Tests follow.
2026-06-12 16:06:46 +02:00
davide 865daa137d feat(app): version in window title and Help overlay
- csproj: add <Version>0.9.0</Version> as single source of truth
- AppVersion reads Major.Minor.Build from the assembly; WindowTitle
  exposes "Palladium Wallet 0.9.0" bound to Window.Title
- Menu Help → in-app overlay (same pattern as settings/server overlays)
  showing app name, version and a short description; backdrop tap and
  Esc close it
- i18n: menu.help, help.title, help.info in all 6 languages
2026-06-12 12:01:25 +02:00
davide 4735490759 feat(app): transaction detail overlay with full on-chain data
Double-clicking a history row opens an in-app overlay (same pattern as
address/server/settings overlays) that shows all available data for a
transaction. The overlay appears immediately with a spinner; data is
fetched from the server in the background and rendered once ready.
Backdrop tap and Esc close it; a pending fetch is cancelled.

New Core type — TransactionInspector (Core/Wallet/):
- FetchAsync() downloads the raw tx, all parent txs (parallel requests,
  one round-trip instead of N sequential), and the block header
- Reconstructs inputs (amounts, addresses, is-mine), outputs, fee,
  virtual size, RBF flag, block timestamp, confirmations

New App types:
- TransactionDetails record: full parsed data (fee, net, I/O lists,
  counterparty addresses, coinbase detection…)
- TransactionDetailsViewModel: pre-formats all strings in the current
  locale and unit (status, date, signed amounts, sat/vB rate…)
- TxIoRow record: one row in the input/output tables
- MineColorConverter: bool → brush (MediumSeaGreen for own addresses)

ViewModel changes:
- ShowTransactionDetailsAsync / CloseTransactionDetailsCommand
- BuildTransactionDetailsAsync (also public for future RBF/CPFP use)
- CancellationTokenSource so opening a new tx cancels the previous fetch
- All server work runs on Task.Run to keep the UI thread free

XAML: history rows get DoubleTapped handler, Hand cursor, hint label,
txid now uses TextTrimming instead of SelectableTextBlock.

i18n: 24 new tx.* / history.hint keys in all 6 languages.
2026-06-12 11:48:33 +02:00
davide 4c7e8696cb feat(app): copy-to-clipboard button for receive address
Adds a "Copy" button next to the receive address in the Receive tab.
Uses Avalonia's Clipboard API (async, code-behind) and calls
NotifyAddressCopied() to show the existing addr.copied status message.
New i18n key receive.copy in all 6 languages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 10:47:01 +02:00
davide 7727dbfddc feat(app): replace placeholder icon with Palladium Wallet logo
Add logo.png (source) and logo.ico (multi-size: 256/128/64/48/32/16)
generated with Pillow. Remove avalonia-logo.ico (Avalonia template
placeholder). Update MainWindow.axaml Icon and csproj ApplicationIcon
to point to the new files.
2026-06-12 10:40:29 +02:00
davide 58b86ad1af feat(app): QR code for receive address
Adds QRCoder 1.8.0 and generates a PNG QR code in-memory each time
the receive address changes (OnReceiveAddressChanged). The bitmap is
displayed in the Receive tab inside a white-background border with
pixel-perfect scaling (BitmapInterpolationMode=None). Previous bitmap
is disposed to avoid memory leaks.
2026-06-12 10:21:18 +02:00
davide 7f2759b2fc feat(app): multi-wallet chooser, confirm-password, encrypt toggle
Multi-wallet (§8):
- AppPaths.WalletFiles(net) enumerates all *.wallet.json in the wallets
  dir; WizardStartOpen shows a chooser step when more than one is found
- New StepChooseWallet + ChooseWalletCommand; Back from StepOpen returns
  to chooser (or to StepStart when only one wallet)
- WalletFileEntry record for the chooser ItemsControl

Password step (Electrum style):
- ConfirmPasswordInput: password must be typed twice before creating
- EncryptWallet checkbox (default true); when unchecked, password is
  null (plaintext file) with an explicit warning
- Validation: empty password with encryption on → msg.password.required;
  mismatch → msg.password.mismatch
- ConfirmPasswordInput cleared on wallet open and wizard back

AppPaths:
- DefaultDataRoot() is now platform-aware: %APPDATA%\PalladiumWallet on
  Windows, ~/.palladium-wallet on Linux/macOS (matches §8 convention)
- Legacy root fallback removed (no prior releases to migrate from)

i18n: wiz.choose.title, wiz.password.confirm, wiz.password.encrypt,
wiz.password.encrypt.hint, msg.choose.wallet, msg.password.required,
msg.password.mismatch — all 6 languages
2026-06-12 10:13:17 +02:00