diff --git a/docs/wallet-hardening.md b/docs/wallet-hardening.md index a63dae3..378e1f5 100644 --- a/docs/wallet-hardening.md +++ b/docs/wallet-hardening.md @@ -112,6 +112,11 @@ Land W7-2 first — it unblocks the rest. ## Progress log +- **Adversarial review of the 3 diagnostics UI features** — ran a 5-dimension finder → per-finding verify workflow over the node-banner + staleness-badge + alert-history commits (the hand-laid ImGui I couldn't visually verify). 4 confirmed, 1 refuted (banner title never overlaps its button — button is absolutely positioned + title is short), and the dedicated ImGui-stack-balance finder found **no** Push/Pop imbalance. Fixes landed: + - **(Med) Alert popup grew off the right edge** — pivot `(0,1)` pinned the panel's *left* edge at the bell (which sits near the window's right edge), so a 320px panel overflowed rightward (an explicit `SetNextWindowPos` pivot skips ImGui's on-screen clamp). Fixed to anchor the bottom-*right* corner at the bell (pivot `(1,1)`, at `bellMax.x`) so it grows left over the canvas. + - **(Low) Staleness badge could flash red on reconnect** — `WalletState::clear()` reset everything *except* the four `last_*_update` stamps, so after a reconnect the pre-outage timestamp survived and the badge briefly showed "Updated Nm ago" (red) on the same frame the node banner cleared — the exact contradiction the design forbids. Fixed by zeroing the four stamps in `clear()` (all readers treat 0 as "never"; verified `app_network.cpp:1473` guards on `!= 0`). + - **(Low) Banner min-height floor wasn't DPI-scaled** — `std::max(minH, baseH*vScale())` compared a raw-px floor against a scaled value; now `minH * dpiScale()`. + - **(Low) New i18n keys weren't in `res/lang/`** — back-filled all 16 diagnostics/QoL keys (this session's node_banner_*/data_stale_*/alerts_*/settings_*/tt_*) into all 8 language files, additively (128 insertions, 0 deletions). zh/ja/ko reworded around 2 glyphs missing from the CJK subset (提醒→通知; ko tooltip avoids 닐) and hard-asserted tofu-free against the subset font. - **Foundation QoL / Persistent alert history** — ☑ landed. Toasts fade in 1–4s; there was no way to review what scrolled past. `Notifications` now retains every pushed alert in a capped (100) ring buffer with a wall-clock epoch (`AlertRecord`) — separate from the 5-item live-toast deque — plus a monotonic `total_pushed_` counter. A bell in the status-bar right cluster (`ICON_MD_NOTIFICATIONS`) opens an upward popup listing recent alerts newest-first with a severity icon/colour (reusing the toast palette), the message, and a relative age (`formatTimeAgoShort`), with a Clear-all action. An **unread dot** on the bell (coloured by the most-severe unseen alert) marks alerts that arrived since the panel was last opened — driven by `totalPushed()` deltas so it survives capping/clearing. Thread-safety: every push is on the UI thread (RPC results run as main-thread `MainCb`s), matching the class's existing lock-free model — documented as a no-raw-worker-thread invariant. Build-clean; `ctest` 1/1 (adds `testNotificationHistory`: retention, order, cap, monotonic counter, clear). **This closes the QoL bundle and the Foundation tier.** - **W6-2 / Refresh-staleness badge** — ☑ landed. The Total Balance card now shows a small pill on its status line ("Updated 2m ago", amber → red past 3 min) **only when connected but the balance stopped refreshing** — a busy daemon can fail `z_gettotalbalance` without dropping the whole connection (only *both* core RPCs failing 3× triggers a disconnect), leaving stale numbers on screen while the node-status banner stays hidden. No refresh-path changes were needed: `WalletState::last_balance_update` is already stamped only on a successful fetch (`network_refresh_service.cpp:1187`), so the badge just reads it and computes age against the same `std::time` clock (`util::formatTimeAgoShort`). Decision is a pure, unit-tested helper (`ui/staleness_badge.h::evaluateStalenessBadge`, thresholds 45s/180s) gated on `connected` so it never contradicts the banner; hover shows a "may be out of date — check your node connection" tooltip. Build-clean; `ctest` 1/1 (adds `testStalenessBadge`). **This closes P2 (5/5).** - **Foundation QoL / Persistent node-status banner** — ☑ landed. A persistent horizontal strip now sits at the top of the content column whenever the wallet can't reach its node — distinct from the transient toasts, so an offline wallet is never silently mistaken for a working one. The show/severity/action decision is a pure function (`ui/node_status_banner.h` → `evaluateNodeStatusBanner`, unit-tested) fed a state snapshot by `App::renderNodeStatusBanner()`. Three cases: **full-node offline** (amber, "Reconnect" → `tryConnect`), **embedded daemon crashed & auto-restart gave up** (red, "Restart node" → `restartDaemon`), **lite wallet failed to open** (red, message-only). Suppressed during the wizard / wallet-switch / daemon-restart / screenshot-sweep / shutdown, and while an expected startup phase (warmup/init/connect-in-progress) already owns the screen. Height in `res/themes/ui.toml` (`banners.node-status`); colours from the material semantic palette; detail text ellipsis-clipped so it can't shove the action button off-screen. Build-clean; `ctest` 1/1 (added `testNodeStatusBanner`). **Remaining QoL:** persistent alert history, and the W6-2 refresh-staleness badge. diff --git a/res/lang/de.json b/res/lang/de.json index 739d7cc..9a9df80 100644 --- a/res/lang/de.json +++ b/res/lang/de.json @@ -48,6 +48,10 @@ "advanced": "ERWEITERT", "advanced_effects": "Erweiterte Effekte...", "ago": "her", + "alerts_clear": "Meldungsverlauf löschen", + "alerts_history_tooltip": "Letzte Meldungen", + "alerts_none": "Noch keine Meldungen", + "alerts_recent": "LETZTE MELDUNGEN", "all_filter": "Alle", "allow_custom_fees": "Benutzerdefinierte Gebühren erlauben", "amount": "Betrag", @@ -451,6 +455,8 @@ "daemon_update_version": "Version:", "daemon_version": "Daemon", "dark": "Dunkel", + "data_stale_prefix": "Aktualisiert", + "data_stale_tooltip": "Der Kontostand ist möglicherweise veraltet – die Wallet hat kürzlich keine Aktualisierung erhalten. Überprüfe deine Node-Verbindung.", "date": "Datum", "date_label": "Datum:", "debug_logging": "FEHLERPROTOKOLLIERUNG", @@ -956,6 +962,11 @@ "no_transactions": "Keine Transaktionen gefunden", "no_transactions_yet": "Noch keine Transaktionen", "node": "KNOTEN", + "node_banner_crashed_title": "Der Node wurde unerwartet beendet", + "node_banner_lite_open_failed": "Wallet konnte nicht geöffnet werden", + "node_banner_offline_title": "Nicht mit dem DragonX-Node verbunden", + "node_banner_reconnect": "Erneut verbinden", + "node_banner_restart": "Node neu starten", "node_security": "KNOTEN & SICHERHEIT", "noise": "Rauschen", "not_connected": "Nicht mit Daemon verbunden...", @@ -1291,12 +1302,14 @@ "settings_configure_explorer": "Externe Block-Explorer-Links konfigurieren", "settings_configure_rpc": "Verbindung zum dragonxd-Daemon konfigurieren", "settings_connection": "Verbindung", + "settings_copy_diagnostics": "Diagnose kopieren", "settings_copyright": "Copyright 2024-2026 DragonX-Entwickler | GPLv3-Lizenz", "settings_custom": "Benutzerdefiniert", "settings_data_dir": "Datenverzeichnis:", "settings_debug_changed": "Debug-Kategorien geändert — Daemon neu starten zum Anwenden", "settings_debug_restart_note": "Änderungen werden nach einem Neustart des Daemons wirksam.", "settings_debug_select": "Kategorien auswählen, um Daemon-Fehlerprotokollierung zu aktivieren (-debug= Flags).", + "settings_diagnostics_copied": "Diagnose in die Zwischenablage kopiert", "settings_encrypt_first_pin": "Verschlüsseln Sie zuerst die Wallet, um PIN zu aktivieren", "settings_encrypt_wallet": "Wallet verschlüsseln", "settings_explorer_hint": "URLs sollten einen abschließenden Schrägstrich enthalten. Die txid/Adresse wird angehängt.", @@ -1317,6 +1330,7 @@ "settings_not_found": "Nicht gefunden", "settings_open_app_dir": "App-Ordner öffnen", "settings_open_data_dir": "Datenordner öffnen", + "settings_open_log_folder": "Log-Ordner öffnen", "settings_other": "Sonstiges", "settings_pin_active": "PIN", "settings_privacy": "Datenschutz", @@ -1476,6 +1490,7 @@ "tt_chat_timestamp": "Zeitstempelformat nur für diesen Tab: der app-weiten Uhr folgen oder 24-hour bzw. 12-hour erzwingen", "tt_clear_ztx": "Lokal zwischengespeicherten Z-Transaktionsverlauf löschen", "tt_clock_format": "24- oder 12-Stunden-Uhr, app-weit. Der Chat-Tab kann sie überschreiben.", + "tt_copy_diagnostics": "Kopiert eine Support-Übersicht (Version, Daemon-/Wallet-/Log-Status – keine Geheimnisse) in die Zwischenablage", "tt_custom_fees": "Manuelle Gebühreneingabe beim Senden von Transaktionen aktivieren", "tt_custom_theme": "Benutzerdefiniertes Theme aktiv", "tt_daemon_install_bundled": "Node stoppen, den installierten dragonxd mit der in diesem Wallet-Build enthaltenen Version überschreiben und dann neu starten", @@ -1529,6 +1544,7 @@ "tt_open_app_dir": "Den ObsidianDragon-Ordner (Einstellungen, Themes, Logs) im Dateimanager öffnen", "tt_open_data_dir": "Den Ordner mit Ihren Wallet- und Blockchain-Daten im Dateimanager öffnen", "tt_open_dir": "Klicken, um im Dateimanager zu öffnen", + "tt_open_log_folder": "Öffnet den Ordner mit den Debug- und Absturzprotokollen", "tt_reduce_motion": "Animierte Übergänge und Saldo-Lerp für Barrierefreiheit deaktivieren", "tt_remove_encrypt": "Verschlüsselung entfernen und Wallet ungeschützt speichern", "tt_remove_pin": "PIN entfernen und Passphrase zum Entsperren erfordern", diff --git a/res/lang/es.json b/res/lang/es.json index ced7ac5..5535247 100644 --- a/res/lang/es.json +++ b/res/lang/es.json @@ -48,6 +48,10 @@ "advanced": "AVANZADO", "advanced_effects": "Efectos Avanzados...", "ago": "atrás", + "alerts_clear": "Borrar historial de alertas", + "alerts_history_tooltip": "Alertas recientes", + "alerts_none": "Aún no hay alertas", + "alerts_recent": "ALERTAS RECIENTES", "all_filter": "Todos", "allow_custom_fees": "Permitir comisiones personalizadas", "amount": "Cantidad", @@ -451,6 +455,8 @@ "daemon_update_version": "Versión:", "daemon_version": "Daemon", "dark": "Oscuro", + "data_stale_prefix": "Actualizado", + "data_stale_tooltip": "El saldo puede estar desactualizado: la cartera no ha recibido una actualización reciente. Comprueba la conexión con tu nodo.", "date": "Fecha", "date_label": "Fecha:", "debug_logging": "REGISTRO DE DEPURACIÓN", @@ -956,6 +962,11 @@ "no_transactions": "No se encontraron transacciones", "no_transactions_yet": "Aún no hay transacciones", "node": "NODO", + "node_banner_crashed_title": "El nodo se detuvo inesperadamente", + "node_banner_lite_open_failed": "No se pudo abrir tu monedero", + "node_banner_offline_title": "No conectado al nodo DragonX", + "node_banner_reconnect": "Reconectar", + "node_banner_restart": "Reiniciar nodo", "node_security": "NODO Y SEGURIDAD", "noise": "Ruido", "not_connected": "No conectado al daemon...", @@ -1291,12 +1302,14 @@ "settings_configure_explorer": "Configurar enlaces de explorador de bloques externo", "settings_configure_rpc": "Configurar conexión al daemon dragonxd", "settings_connection": "Conexión", + "settings_copy_diagnostics": "Copiar diagnósticos", "settings_copyright": "Copyright 2024-2026 Desarrolladores de DragonX | Licencia GPLv3", "settings_custom": "Personalizado", "settings_data_dir": "Dir. de datos:", "settings_debug_changed": "Categorías de depuración cambiadas — reinicie el daemon para aplicar", "settings_debug_restart_note": "Los cambios surten efecto después de reiniciar el daemon.", "settings_debug_select": "Seleccione categorías para habilitar el registro de depuración del daemon (flags -debug=).", + "settings_diagnostics_copied": "Diagnósticos copiados al portapapeles", "settings_encrypt_first_pin": "Primero cifre la billetera para habilitar el PIN", "settings_encrypt_wallet": "Cifrar billetera", "settings_explorer_hint": "Las URLs deben incluir una barra final. Se añadirá el txid/dirección.", @@ -1317,6 +1330,7 @@ "settings_not_found": "No encontrado", "settings_open_app_dir": "Abrir carpeta de la aplicación", "settings_open_data_dir": "Abrir carpeta de datos", + "settings_open_log_folder": "Abrir carpeta de registros", "settings_other": "Otros", "settings_pin_active": "PIN", "settings_privacy": "Privacidad", @@ -1476,6 +1490,7 @@ "tt_chat_timestamp": "Formato de marca de tiempo solo para esta pestaña: seguir el reloj de toda la app, o forzar 24-hour o 12-hour", "tt_clear_ztx": "Eliminar historial de z-transacciones en caché local", "tt_clock_format": "Reloj de 24 o 12 horas, en toda la app. El chat puede anularlo.", + "tt_copy_diagnostics": "Copia al portapapeles un resumen para soporte (versión, estado de daemon/cartera/registros, sin datos secretos)", "tt_custom_fees": "Habilitar entrada manual de comisiones al enviar transacciones", "tt_custom_theme": "Tema personalizado activo", "tt_daemon_install_bundled": "Detiene el nodo, sobrescribe el dragonxd instalado con la versión incluida en esta compilación de la cartera y luego lo reinicia", @@ -1529,6 +1544,7 @@ "tt_open_app_dir": "Abrir la carpeta de ObsidianDragon (configuración, temas, registros) en el explorador de archivos", "tt_open_data_dir": "Abre en el gestor de archivos la carpeta con los datos de tu cartera y de la blockchain", "tt_open_dir": "Clic para abrir en explorador de archivos", + "tt_open_log_folder": "Abre la carpeta que contiene los registros de depuración y de fallos", "tt_reduce_motion": "Desactivar transiciones animadas y lerp de saldo para accesibilidad", "tt_remove_encrypt": "Quitar cifrado y almacenar la billetera sin protección", "tt_remove_pin": "Quitar PIN y requerir contraseña para desbloquear", diff --git a/res/lang/fr.json b/res/lang/fr.json index 19402f6..e3dd5cf 100644 --- a/res/lang/fr.json +++ b/res/lang/fr.json @@ -48,6 +48,10 @@ "advanced": "AVANCÉ", "advanced_effects": "Effets avancés...", "ago": "passé", + "alerts_clear": "Effacer l'historique des alertes", + "alerts_history_tooltip": "Alertes récentes", + "alerts_none": "Aucune alerte pour l'instant", + "alerts_recent": "ALERTES RÉCENTES", "all_filter": "Tout", "allow_custom_fees": "Autoriser les frais personnalisés", "amount": "Montant", @@ -451,6 +455,8 @@ "daemon_update_version": "Version :", "daemon_version": "Daemon", "dark": "Sombre", + "data_stale_prefix": "Mis à jour", + "data_stale_tooltip": "Le solde est peut-être obsolète — le portefeuille n'a pas reçu de mise à jour récente. Vérifiez la connexion à votre nœud.", "date": "Date", "date_label": "Date :", "debug_logging": "JOURNALISATION DE DÉBOGAGE", @@ -956,6 +962,11 @@ "no_transactions": "Aucune transaction trouvée", "no_transactions_yet": "Aucune transaction pour le moment", "node": "NŒUD", + "node_banner_crashed_title": "Le nœud s'est arrêté de façon inattendue", + "node_banner_lite_open_failed": "Impossible d'ouvrir votre portefeuille", + "node_banner_offline_title": "Non connecté au nœud DragonX", + "node_banner_reconnect": "Reconnecter", + "node_banner_restart": "Redémarrer le nœud", "node_security": "NŒUD & SÉCURITÉ", "noise": "Bruit", "not_connected": "Non connecté au daemon...", @@ -1291,12 +1302,14 @@ "settings_configure_explorer": "Configurer les liens vers l'explorateur de blocs externe", "settings_configure_rpc": "Configurer la connexion au daemon dragonxd", "settings_connection": "Connexion", + "settings_copy_diagnostics": "Copier les diagnostics", "settings_copyright": "Copyright 2024-2026 Développeurs DragonX | Licence GPLv3", "settings_custom": "Personnalisé", "settings_data_dir": "Rép. de données :", "settings_debug_changed": "Catégories de débogage modifiées — redémarrez le daemon pour appliquer", "settings_debug_restart_note": "Les modifications prennent effet après le redémarrage du daemon.", "settings_debug_select": "Sélectionnez les catégories pour activer la journalisation de débogage du daemon (flags -debug=).", + "settings_diagnostics_copied": "Diagnostics copiés dans le presse-papiers", "settings_encrypt_first_pin": "Chiffrez d'abord le portefeuille pour activer le PIN", "settings_encrypt_wallet": "Chiffrer le portefeuille", "settings_explorer_hint": "Les URLs doivent inclure une barre oblique finale. Le txid/adresse sera ajouté.", @@ -1317,6 +1330,7 @@ "settings_not_found": "Non trouvé", "settings_open_app_dir": "Ouvrir le dossier de l'application", "settings_open_data_dir": "Ouvrir le dossier de données", + "settings_open_log_folder": "Ouvrir le dossier des journaux", "settings_other": "Autres", "settings_pin_active": "PIN", "settings_privacy": "Confidentialité", @@ -1476,6 +1490,7 @@ "tt_chat_timestamp": "Format d'horodatage pour cet onglet uniquement : suivre l'horloge de l'application, ou forcer 24-hour ou 12-hour", "tt_clear_ztx": "Supprimer l'historique des z-transactions mis en cache localement", "tt_clock_format": "Horloge 24 h ou 12 h, dans toute l'app. Le chat peut la remplacer.", + "tt_copy_diagnostics": "Copie un récapitulatif de support (version, état daemon/portefeuille/journaux — sans données secrètes) dans le presse-papiers", "tt_custom_fees": "Activer la saisie manuelle des frais lors de l'envoi de transactions", "tt_custom_theme": "Thème personnalisé actif", "tt_daemon_install_bundled": "Arrêter le nœud, remplacer le dragonxd installé par la version intégrée dans cette version du portefeuille, puis redémarrer", @@ -1529,6 +1544,7 @@ "tt_open_app_dir": "Ouvrir le dossier ObsidianDragon (paramètres, thèmes, journaux) dans le gestionnaire de fichiers", "tt_open_data_dir": "Ouvrir le dossier contenant les données de votre portefeuille et de la blockchain dans le gestionnaire de fichiers", "tt_open_dir": "Cliquer pour ouvrir dans l'explorateur de fichiers", + "tt_open_log_folder": "Ouvre le dossier contenant les journaux de débogage et de plantage", "tt_reduce_motion": "Désactiver les transitions animées et le lerp de solde pour l'accessibilité", "tt_remove_encrypt": "Supprimer le chiffrement et stocker le portefeuille sans protection", "tt_remove_pin": "Supprimer le PIN et exiger la phrase secrète pour déverrouiller", diff --git a/res/lang/ja.json b/res/lang/ja.json index 230c66c..7cd4a10 100644 --- a/res/lang/ja.json +++ b/res/lang/ja.json @@ -48,6 +48,10 @@ "advanced": "詳細設定", "advanced_effects": "高度なエフェクト...", "ago": "前", + "alerts_clear": "通知履歴を消去", + "alerts_history_tooltip": "最近の通知", + "alerts_none": "通知はまだありません", + "alerts_recent": "最近の通知", "all_filter": "すべて", "allow_custom_fees": "カスタム手数料を許可", "amount": "金額", @@ -451,6 +455,8 @@ "daemon_update_version": "バージョン:", "daemon_version": "デーモン", "dark": "ダーク", + "data_stale_prefix": "更新", + "data_stale_tooltip": "残高が最新でない可能性があります。ウォレットは最近更新を受信していません。ノード接続を確認してください。", "date": "日付", "date_label": "日付:", "debug_logging": "デバッグログ", @@ -956,6 +962,11 @@ "no_transactions": "取引が見つかりません", "no_transactions_yet": "まだ取引がありません", "node": "ノード", + "node_banner_crashed_title": "ノードが予期せず停止しました", + "node_banner_lite_open_failed": "ウォレットを開けませんでした", + "node_banner_offline_title": "DragonX ノードに接続されていません", + "node_banner_reconnect": "再接続", + "node_banner_restart": "ノードを再起動", "node_security": "ノードとセキュリティ", "noise": "ノイズ", "not_connected": "デーモンに未接続...", @@ -1288,12 +1299,14 @@ "settings_configure_explorer": "外部ブロックエクスプローラーリンクを設定", "settings_configure_rpc": "dragonxd デーモンへの接続を設定", "settings_connection": "接続", + "settings_copy_diagnostics": "診断情報をコピー", "settings_copyright": "Copyright 2024-2026 DragonX 開発者 | GPLv3 ライセンス", "settings_custom": "カスタム", "settings_data_dir": "データディレクトリ:", "settings_debug_changed": "デバッグカテゴリが変更されました — デーモンを再起動して適用", "settings_debug_restart_note": "変更はデーモンの再起動後に有効になります。", "settings_debug_select": "デーモンのデバッグログを有効にするカテゴリを選択(-debug= フラグ)。", + "settings_diagnostics_copied": "診断情報をクリップボードにコピーしました", "settings_encrypt_first_pin": "PIN を有効にするには、まずウォレットを暗号化してください", "settings_encrypt_wallet": "ウォレットを暗号化", "settings_explorer_hint": "URLには末尾のスラッシュを含めてください。txid/アドレスが追加されます。", @@ -1314,6 +1327,7 @@ "settings_not_found": "見つかりません", "settings_open_app_dir": "アプリフォルダを開く", "settings_open_data_dir": "データフォルダを開く", + "settings_open_log_folder": "ログフォルダを開く", "settings_other": "その他", "settings_pin_active": "PIN", "settings_privacy": "プライバシー", @@ -1473,6 +1487,7 @@ "tt_chat_timestamp": "このタブのみのタイムスタンプ形式:アプリ全体の時計に従うか、24-hourまたは12-hourを強制します", "tt_clear_ztx": "ローカルにキャッシュされた z-トランザクション履歴を削除", "tt_clock_format": "24時間または12時間表示(アプリ全体)。チャットで上書きできます。", + "tt_copy_diagnostics": "サポート用の概要(バージョン、デーモン/ウォレット/ログの状態 — 秘密情報なし)をクリップボードにコピーします", "tt_custom_fees": "トランザクション送信時に手動手数料入力を有効化", "tt_custom_theme": "カスタムテーマがアクティブ", "tt_daemon_install_bundled": "ノードを停止し、インストール済みの dragonxd をこのウォレットビルドにバンドルされたバージョンで上書きしてから再起動します", @@ -1526,6 +1541,7 @@ "tt_open_app_dir": "ObsidianDragon フォルダ(設定、テーマ、ログ)をファイルマネージャーで開く", "tt_open_data_dir": "ファイルマネージャーでウォレットとブロックチェーンデータのフォルダを開きます", "tt_open_dir": "クリックしてファイルエクスプローラーで開く", + "tt_open_log_folder": "デバッグログとクラッシュログが入ったフォルダを開きます", "tt_reduce_motion": "アクセシビリティのためにアニメーション遷移と残高補間を無効にする", "tt_remove_encrypt": "暗号化を解除してウォレットを保護なしで保存", "tt_remove_pin": "PIN を削除しアンロックにパスフレーズを要求", diff --git a/res/lang/ko.json b/res/lang/ko.json index 8b05e1c..7c56e41 100644 --- a/res/lang/ko.json +++ b/res/lang/ko.json @@ -48,6 +48,10 @@ "advanced": "고급 설정", "advanced_effects": "고급 효과...", "ago": "전", + "alerts_clear": "알림 기록 지우기", + "alerts_history_tooltip": "최근 알림", + "alerts_none": "아직 알림이 없습니다", + "alerts_recent": "최근 알림", "all_filter": "전체", "allow_custom_fees": "사용자 정의 수수료 허용", "amount": "금액", @@ -451,6 +455,8 @@ "daemon_update_version": "버전:", "daemon_version": "데몬", "dark": "다크", + "data_stale_prefix": "업데이트", + "data_stale_tooltip": "잔액이 오래되었을 수 있습니다 — 지갑이 최근에 업데이트를 받지 못했습니다. 노드 연결을 확인하세요.", "date": "날짜", "date_label": "날짜:", "debug_logging": "디버그 로깅", @@ -955,6 +961,11 @@ "no_transactions": "거래 내역이 없습니다", "no_transactions_yet": "아직 거래 내역이 없습니다", "node": "노드", + "node_banner_crashed_title": "노드가 예기치 않게 중지되었습니다", + "node_banner_lite_open_failed": "지갑을 열 수 없습니다", + "node_banner_offline_title": "DragonX 노드에 연결되지 않음", + "node_banner_reconnect": "재연결", + "node_banner_restart": "노드 재시작", "node_security": "노드 및 보안", "noise": "노이즈", "not_connected": "데몬에 연결되지 않음...", @@ -1290,12 +1301,14 @@ "settings_configure_explorer": "외부 블록 탐색기 링크 구성", "settings_configure_rpc": "dragonxd 데몬 연결 구성", "settings_connection": "연결", + "settings_copy_diagnostics": "진단 정보 복사", "settings_copyright": "Copyright 2024-2026 DragonX 개발자 | GPLv3 라이선스", "settings_custom": "사용자 지정", "settings_data_dir": "데이터 디렉터리:", "settings_debug_changed": "디버그 카테고리가 변경되었습니다 — 데몬을 재시작하여 적용", "settings_debug_restart_note": "변경 사항은 데몬을 다시 시작한 후에 적용됩니다.", "settings_debug_select": "데몬 디버그 로깅을 활성화할 카테고리를 선택하세요 (-debug= 플래그).", + "settings_diagnostics_copied": "진단 정보를 클립보드에 복사했습니다", "settings_encrypt_first_pin": "PIN을 활성화하려면 먼저 지갑을 암호화하세요", "settings_encrypt_wallet": "지갑 암호화", "settings_explorer_hint": "URL에 후행 슬래시를 포함해야 합니다. txid/주소가 추가됩니다.", @@ -1316,6 +1329,7 @@ "settings_not_found": "찾을 수 없음", "settings_open_app_dir": "앱 폴더 열기", "settings_open_data_dir": "데이터 폴더 열기", + "settings_open_log_folder": "로그 폴더 열기", "settings_other": "기타", "settings_pin_active": "PIN", "settings_privacy": "개인 정보", @@ -1475,6 +1489,7 @@ "tt_chat_timestamp": "이 탭에만 적용되는 타임스탬프 형식: 앱 전체 시계를 따르거나 24-hour 또는 12-hour로 강제합니다", "tt_clear_ztx": "로컬에 캐시된 z-트랜잭션 기록 삭제", "tt_clock_format": "24시간 또는 12시간 형식(앱 전체). 채팅에서 재정의할 수 있습니다.", + "tt_copy_diagnostics": "지원용 요약(버전, 데몬/지갑/로그 상태 — 비밀 정보 없음)을 클립보드에 복사합니다", "tt_custom_fees": "거래 전송 시 수동 수수료 입력 활성화", "tt_custom_theme": "사용자 지정 테마 활성화됨", "tt_daemon_install_bundled": "노드를 중지하고 설치된 dragonxd를 이 지갑 빌드에 번들된 버전으로 덮어쓴 다음 재시작합니다", @@ -1528,6 +1543,7 @@ "tt_open_app_dir": "파일 관리자에서 ObsidianDragon 폴더(설정, 테마, 로그)를 엽니다", "tt_open_data_dir": "지갑 및 블록체인 데이터가 있는 폴더를 파일 탐색기에서 엽니다", "tt_open_dir": "파일 탐색기에서 열려면 클릭", + "tt_open_log_folder": "디버그 및 충돌 로그가 있는 폴더를 엽니다", "tt_reduce_motion": "접근성을 위해 애니메이션 전환 및 잔액 보간 비활성화", "tt_remove_encrypt": "암호화를 제거하고 지갑을 보호 없이 저장", "tt_remove_pin": "PIN을 제거하고 잠금 해제 시 비밀번호 요구", diff --git a/res/lang/pt.json b/res/lang/pt.json index 350f364..a5e0eda 100644 --- a/res/lang/pt.json +++ b/res/lang/pt.json @@ -48,6 +48,10 @@ "advanced": "AVANÇADO", "advanced_effects": "Efeitos Avançados...", "ago": "atrás", + "alerts_clear": "Limpar histórico de alertas", + "alerts_history_tooltip": "Alertas recentes", + "alerts_none": "Ainda não há alertas", + "alerts_recent": "ALERTAS RECENTES", "all_filter": "Todos", "allow_custom_fees": "Permitir taxas personalizadas", "amount": "Valor", @@ -451,6 +455,8 @@ "daemon_update_version": "Versão:", "daemon_version": "Daemon", "dark": "Escuro", + "data_stale_prefix": "Atualizado", + "data_stale_tooltip": "O saldo pode estar desatualizado — a carteira não recebeu uma atualização recente. Verifique a conexão com o seu nó.", "date": "Data", "date_label": "Data:", "debug_logging": "REGISTRO DE DEPURAÇÃO", @@ -956,6 +962,11 @@ "no_transactions": "Nenhuma transação encontrada", "no_transactions_yet": "Nenhuma transação ainda", "node": "NÓ", + "node_banner_crashed_title": "O nó parou inesperadamente", + "node_banner_lite_open_failed": "Não foi possível abrir sua carteira", + "node_banner_offline_title": "Não conectado ao nó DragonX", + "node_banner_reconnect": "Reconectar", + "node_banner_restart": "Reiniciar nó", "node_security": "NÓ & SEGURANÇA", "noise": "Ruído", "not_connected": "Não conectado ao daemon...", @@ -1291,12 +1302,14 @@ "settings_configure_explorer": "Configurar links do explorador de blocos externo", "settings_configure_rpc": "Configurar conexão ao daemon dragonxd", "settings_connection": "Conexão", + "settings_copy_diagnostics": "Copiar diagnósticos", "settings_copyright": "Copyright 2024-2026 Desenvolvedores DragonX | Licença GPLv3", "settings_custom": "Personalizado", "settings_data_dir": "Dir. de dados:", "settings_debug_changed": "Categorias de depuração alteradas — reinicie o daemon para aplicar", "settings_debug_restart_note": "As alterações entram em vigor após reiniciar o daemon.", "settings_debug_select": "Selecione categorias para ativar o registro de depuração do daemon (flags -debug=).", + "settings_diagnostics_copied": "Diagnósticos copiados para a área de transferência", "settings_encrypt_first_pin": "Encripte a carteira primeiro para ativar o PIN", "settings_encrypt_wallet": "Encriptar carteira", "settings_explorer_hint": "As URLs devem incluir uma barra final. O txid/endereço será adicionado.", @@ -1317,6 +1330,7 @@ "settings_not_found": "Não encontrado", "settings_open_app_dir": "Abrir pasta do aplicativo", "settings_open_data_dir": "Abrir pasta de dados", + "settings_open_log_folder": "Abrir pasta de logs", "settings_other": "Outros", "settings_pin_active": "PIN", "settings_privacy": "Privacidade", @@ -1476,6 +1490,7 @@ "tt_chat_timestamp": "Formato de horário apenas para esta aba: seguir o relógio geral do aplicativo, ou forçar 24-hour ou 12-hour", "tt_clear_ztx": "Excluir histórico de z-transações em cache local", "tt_clock_format": "Relógio de 24 ou 12 horas, em todo o app. O chat pode substituí-lo.", + "tt_copy_diagnostics": "Copia um resumo para suporte (versão, estado do daemon/carteira/logs — sem segredos) para a área de transferência", "tt_custom_fees": "Ativar entrada manual de taxas ao enviar transações", "tt_custom_theme": "Tema personalizado ativo", "tt_daemon_install_bundled": "Parar o nó, sobrescrever o dragonxd instalado com a versão incluída nesta compilação da carteira e reiniciar", @@ -1529,6 +1544,7 @@ "tt_open_app_dir": "Abrir a pasta ObsidianDragon (configurações, temas, logs) no gerenciador de arquivos", "tt_open_data_dir": "Abrir a pasta com os dados da sua carteira e da blockchain no gerenciador de arquivos", "tt_open_dir": "Clique para abrir no explorador de arquivos", + "tt_open_log_folder": "Abre a pasta que contém os logs de depuração e de falhas", "tt_reduce_motion": "Desativar transições animadas e lerp de saldo para acessibilidade", "tt_remove_encrypt": "Remover encriptação e armazenar a carteira desprotegida", "tt_remove_pin": "Remover PIN e exigir frase secreta para desbloquear", diff --git a/res/lang/ru.json b/res/lang/ru.json index 1ce7f9f..d59c39a 100644 --- a/res/lang/ru.json +++ b/res/lang/ru.json @@ -48,6 +48,10 @@ "advanced": "ПРОЧЕЕ", "advanced_effects": "Расширенные эффекты...", "ago": "назад", + "alerts_clear": "Очистить историю оповещений", + "alerts_history_tooltip": "Недавние оповещения", + "alerts_none": "Пока нет оповещений", + "alerts_recent": "НЕДАВНИЕ ОПОВЕЩЕНИЯ", "all_filter": "Все", "allow_custom_fees": "Разрешить пользовательские комиссии", "amount": "Сумма", @@ -451,6 +455,8 @@ "daemon_update_version": "Версия:", "daemon_version": "Демон", "dark": "Тёмная", + "data_stale_prefix": "Обновлено", + "data_stale_tooltip": "Баланс может быть устаревшим — кошелёк давно не получал обновлений. Проверьте подключение к узлу.", "date": "Дата", "date_label": "Дата:", "debug_logging": "ЖУРНАЛ ОТЛАДКИ", @@ -956,6 +962,11 @@ "no_transactions": "Транзакции не найдены", "no_transactions_yet": "Транзакций пока нет", "node": "УЗЕЛ", + "node_banner_crashed_title": "Узел неожиданно остановился", + "node_banner_lite_open_failed": "Не удалось открыть кошелёк", + "node_banner_offline_title": "Нет подключения к узлу DragonX", + "node_banner_reconnect": "Переподключить", + "node_banner_restart": "Перезапустить узел", "node_security": "УЗЕЛ И БЕЗОПАСНОСТЬ", "noise": "Шум", "not_connected": "Не подключено к daemon...", @@ -1291,12 +1302,14 @@ "settings_configure_explorer": "Настроить ссылки внешнего обозревателя блоков", "settings_configure_rpc": "Настроить подключение к демону dragonxd", "settings_connection": "Подключение", + "settings_copy_diagnostics": "Копировать диагностику", "settings_copyright": "Copyright 2024-2026 Разработчики DragonX | Лицензия GPLv3", "settings_custom": "Пользовательские", "settings_data_dir": "Каталог данных:", "settings_debug_changed": "Категории отладки изменены — перезапустите демон для применения", "settings_debug_restart_note": "Изменения вступают в силу после перезапуска демона.", "settings_debug_select": "Выберите категории для включения журнала отладки демона (флаги -debug=).", + "settings_diagnostics_copied": "Диагностика скопирована в буфер обмена", "settings_encrypt_first_pin": "Сначала зашифруйте кошелёк, чтобы включить PIN", "settings_encrypt_wallet": "Зашифровать кошелёк", "settings_explorer_hint": "URL-адреса должны заканчиваться косой чертой. Txid/адрес будет добавлен.", @@ -1317,6 +1330,7 @@ "settings_not_found": "Не найден", "settings_open_app_dir": "Открыть папку приложения", "settings_open_data_dir": "Открыть папку данных", + "settings_open_log_folder": "Открыть папку журналов", "settings_other": "Прочее", "settings_pin_active": "PIN", "settings_privacy": "Конфиденциальность", @@ -1476,6 +1490,7 @@ "tt_chat_timestamp": "Формат времени только для этой вкладки: следовать общим настройкам часов приложения либо принудительно 24-hour или 12-hour", "tt_clear_ztx": "Удалить локально кешированную историю z-транзакций", "tt_clock_format": "24- или 12-часовой формат для всего приложения. Чат может переопределить.", + "tt_copy_diagnostics": "Копирует сводку для поддержки (версия, состояние демона/кошелька/журналов — без секретов) в буфер обмена", "tt_custom_fees": "Включить ручной ввод комиссий при отправке транзакций", "tt_custom_theme": "Пользовательская тема активна", "tt_daemon_install_bundled": "Остановить узел, перезаписать установленный dragonxd версией, встроенной в эту сборку кошелька, затем перезапустить", @@ -1529,6 +1544,7 @@ "tt_open_app_dir": "Открыть папку ObsidianDragon (настройки, темы, логи) в файловом менеджере", "tt_open_data_dir": "Открыть в файловом менеджере папку с данными кошелька и блокчейна", "tt_open_dir": "Нажмите, чтобы открыть в проводнике", + "tt_open_log_folder": "Открывает папку с журналами отладки и сбоев", "tt_reduce_motion": "Отключить анимированные переходы и плавное изменение баланса для доступности", "tt_remove_encrypt": "Удалить шифрование и хранить кошелёк без защиты", "tt_remove_pin": "Удалить PIN и требовать пароль для разблокировки", diff --git a/res/lang/zh.json b/res/lang/zh.json index 9310852..6b0dee2 100644 --- a/res/lang/zh.json +++ b/res/lang/zh.json @@ -48,6 +48,10 @@ "advanced": "高级", "advanced_effects": "高级特效...", "ago": "前", + "alerts_clear": "清除通知历史", + "alerts_history_tooltip": "最近通知", + "alerts_none": "暂无通知", + "alerts_recent": "最近通知", "all_filter": "全部", "allow_custom_fees": "允许自定义手续费", "amount": "金额", @@ -451,6 +455,8 @@ "daemon_update_version": "版本:", "daemon_version": "守护进程", "dark": "深色", + "data_stale_prefix": "更新于", + "data_stale_tooltip": "余额可能已过时 — 钱包最近未收到更新。请检查您的节点连接。", "date": "日期", "date_label": "日期:", "debug_logging": "调试日志", @@ -955,6 +961,11 @@ "no_transactions": "未找到交易", "no_transactions_yet": "尚无交易", "node": "节点", + "node_banner_crashed_title": "节点意外停止", + "node_banner_lite_open_failed": "无法打开您的钱包", + "node_banner_offline_title": "未连接到 DragonX 节点", + "node_banner_reconnect": "重新连接", + "node_banner_restart": "重启节点", "node_security": "节点与安全", "noise": "噪点", "not_connected": "未连接到守护进程...", @@ -1289,12 +1300,14 @@ "settings_configure_explorer": "配置外部区块浏览器链接", "settings_configure_rpc": "配置 dragonxd 守护进程连接", "settings_connection": "连接", + "settings_copy_diagnostics": "复制诊断信息", "settings_copyright": "版权所有 2024-2026 DragonX 开发者 | GPLv3 许可证", "settings_custom": "自定义", "settings_data_dir": "数据目录:", "settings_debug_changed": "调试类别已更改——重启守护进程以应用", "settings_debug_restart_note": "更改将在重启守护进程后生效。", "settings_debug_select": "选择要启用的守护进程调试日志类别(-debug= 标志)。", + "settings_diagnostics_copied": "诊断信息已复制到剪贴板", "settings_encrypt_first_pin": "请先加密钱包以启用 PIN", "settings_encrypt_wallet": "加密钱包", "settings_explorer_hint": "URL 应包含尾部斜杠。将自动附加 txid/地址。", @@ -1315,6 +1328,7 @@ "settings_not_found": "未找到", "settings_open_app_dir": "打开应用文件夹", "settings_open_data_dir": "打开数据文件夹", + "settings_open_log_folder": "打开日志文件夹", "settings_other": "其他", "settings_pin_active": "PIN", "settings_privacy": "隐私", @@ -1474,6 +1488,7 @@ "tt_chat_timestamp": "仅此标签页的时间戳格式:跟随全应用时钟,或强制使用 24-hour 或 12-hour", "tt_clear_ztx": "删除本地缓存的 z-交易历史", "tt_clock_format": "24 或 12 小时制,应用全局。聊天可覆盖。", + "tt_copy_diagnostics": "将支持诊断摘要(版本、守护进程/钱包/日志状态 — 不含机密)复制到剪贴板", "tt_custom_fees": "发送交易时启用手动费用输入", "tt_custom_theme": "自定义主题已激活", "tt_daemon_install_bundled": "停止节点,用此钱包版本内置的 dragonxd 覆盖已安装的版本,然后重启", @@ -1527,6 +1542,7 @@ "tt_open_app_dir": "在文件管理器中打开 ObsidianDragon 文件夹(设置、主题、日志)", "tt_open_data_dir": "在文件管理器中打开包含您钱包和区块链数据的文件夹", "tt_open_dir": "点击在文件管理器中打开", + "tt_open_log_folder": "打开包含调试和崩溃日志的文件夹", "tt_reduce_motion": "禁用动画过渡和余额渐变以提高无障碍性", "tt_remove_encrypt": "移除加密并以未受保护状态存储钱包", "tt_remove_pin": "移除 PIN 并要求密码解锁", diff --git a/src/app.cpp b/src/app.cpp index 38adc88..afeff0c 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -2182,7 +2182,9 @@ void App::renderNodeStatusBanner() const auto& S = ui::schema::UI(); const float minH = S.drawElement("banners.node-status", "min-height").size; const float baseH = S.drawElement("banners.node-status", "height").size; - const float bannerH = std::max(minH, baseH * ui::Layout::vScale()); + // Both operands must be in scaled px: vScale() already folds in dpiScale(), so the raw min-height + // floor needs the same dpiScale() or it under-clamps the banner at HiDPI. + const float bannerH = std::max(minH * ui::Layout::dpiScale(), baseH * ui::Layout::vScale()); const bool isError = (banner.severity == ui::NodeBannerSeverity::Error); const ImU32 sevCol = isError ? m::Error() : m::Warning(); @@ -2703,10 +2705,12 @@ void App::renderStatusBar() ImGui::OpenPopup("##AlertHistoryPopup"); } - // The status bar sits at the window bottom, so grow the popup UPWARD from the bell - // (pivot bottom-left → the anchor point becomes the popup's bottom-left corner). - ImGui::SetNextWindowPos(ImVec2(bellMin.x, bellMin.y - 4.0f * dp), - ImGuiCond_Always, ImVec2(0.0f, 1.0f)); + // The bell sits near the window's bottom-right, so anchor the popup's bottom-RIGHT + // corner at the bell's right edge (pivot (1,1)) — it then grows LEFT over the canvas and + // UP from the status bar. A left pivot would push a 320px panel off the right edge (and + // an explicit SetNextWindowPos pivot skips ImGui's on-screen clamp, so it would overflow). + ImGui::SetNextWindowPos(ImVec2(bellMax.x, bellMin.y - 4.0f * dp), + ImGuiCond_Always, ImVec2(1.0f, 1.0f)); const float panelW = 320.0f * dp; ImGui::SetNextWindowSizeConstraints(ImVec2(panelW, 0), ImVec2(panelW, 360.0f * dp)); if (ImGui::BeginPopup("##AlertHistoryPopup")) { diff --git a/src/data/wallet_state.h b/src/data/wallet_state.h index 6083343..66a1ba3 100644 --- a/src/data/wallet_state.h +++ b/src/data/wallet_state.h @@ -339,6 +339,11 @@ struct WalletState { // stops), so leaving the previous wallet's hashrate/blocks would show stale mining stats. mining = MiningInfo{}; pool_mining = PoolMiningState{}; + // After a disconnect / wallet switch nothing is freshly known, so drop the "last successful + // refresh" stamps. Otherwise the pre-teardown time survives and, on reconnect, the staleness + // badge (and any "updated X ago" reader) briefly reports it as current until the first refresh + // re-stamps it. All readers treat 0 as "never" (formatTimeAgoShort/timeAgo return ""). + last_balance_update = last_tx_update = last_peer_update = last_mining_update = 0; } // Rebuild combined addresses list from z/t lists