Compare commits
17 Commits
203967411a
...
fix/audit-
| Author | SHA1 | Date | |
|---|---|---|---|
| 02554d523d | |||
| c53b7f771e | |||
| bcee4bfe72 | |||
| e1870c3b23 | |||
| 7cf0bb8bc7 | |||
| 541a9e1fd5 | |||
| 07b8e0b2cb | |||
| de48414c65 | |||
| f0c681b0b5 | |||
| fdf2502ca8 | |||
| e393b0d847 | |||
| 3b041f20c1 | |||
| 77a0ad64b0 | |||
| d0ca2fdf52 | |||
| 9c07b6d33d | |||
| fedfe3d60c | |||
| 95ff9ce9ae |
@@ -53,7 +53,6 @@ set_property(CACHE DRAGONX_LITE_BACKEND_LINK_MODE PROPERTY STRINGS imported)
|
|||||||
set(DRAGONX_LITE_BACKEND_ABI "sdxl-c-v1" CACHE STRING "Expected lite backend C ABI version")
|
set(DRAGONX_LITE_BACKEND_ABI "sdxl-c-v1" CACHE STRING "Expected lite backend C ABI version")
|
||||||
set(DRAGONX_LITE_BACKEND_SYMBOLS_FILE "" CACHE FILEPATH "Path to generated lite backend exported-symbol inventory")
|
set(DRAGONX_LITE_BACKEND_SYMBOLS_FILE "" CACHE FILEPATH "Path to generated lite backend exported-symbol inventory")
|
||||||
set(DRAGONX_LITE_BACKEND_MANIFEST "" CACHE FILEPATH "Optional path to generated lite backend artifact manifest")
|
set(DRAGONX_LITE_BACKEND_MANIFEST "" CACHE FILEPATH "Optional path to generated lite backend artifact manifest")
|
||||||
option(DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE "Require verified signature metadata in the lite backend artifact manifest" OFF)
|
|
||||||
set(DRAGONX_LITE_BACKEND_REQUIRED_SYMBOLS
|
set(DRAGONX_LITE_BACKEND_REQUIRED_SYMBOLS
|
||||||
litelib_wallet_exists
|
litelib_wallet_exists
|
||||||
litelib_initialize_new
|
litelib_initialize_new
|
||||||
@@ -126,31 +125,12 @@ if(DRAGONX_ENABLE_LITE_BACKEND)
|
|||||||
if(DRAGONX_LITE_BACKEND_MANIFEST AND NOT EXISTS "${DRAGONX_LITE_BACKEND_MANIFEST}")
|
if(DRAGONX_LITE_BACKEND_MANIFEST AND NOT EXISTS "${DRAGONX_LITE_BACKEND_MANIFEST}")
|
||||||
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_MANIFEST does not exist: ${DRAGONX_LITE_BACKEND_MANIFEST}")
|
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_MANIFEST does not exist: ${DRAGONX_LITE_BACKEND_MANIFEST}")
|
||||||
endif()
|
endif()
|
||||||
if(DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE)
|
# Note (F15-1): the former signature-metadata gate was removed. It trusted a
|
||||||
if(NOT DRAGONX_LITE_BACKEND_MANIFEST)
|
# "verification_status: verified" field that scripts/build-lite-backend-artifact.sh
|
||||||
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE requires DRAGONX_LITE_BACKEND_MANIFEST")
|
# self-attested with no cryptographic check (the "verified" SHA was just the artifact's
|
||||||
endif()
|
# own SHA). The trust root is now build-from-source: that script builds the backend from
|
||||||
file(READ "${DRAGONX_LITE_BACKEND_MANIFEST}" DRAGONX_LITE_BACKEND_MANIFEST_JSON)
|
# the vendored in-tree source and refuses prebuilt artifacts, so the library linked here
|
||||||
string(JSON DRAGONX_LITE_SIGNATURE_STATUS ERROR_VARIABLE DRAGONX_LITE_SIGNATURE_STATUS_ERROR GET "${DRAGONX_LITE_BACKEND_MANIFEST_JSON}" signature_verification verification_status)
|
# is the one built from reviewed source. The required-symbol inventory check above stays.
|
||||||
if(DRAGONX_LITE_SIGNATURE_STATUS_ERROR)
|
|
||||||
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_MANIFEST is missing signature verification status")
|
|
||||||
endif()
|
|
||||||
if(NOT DRAGONX_LITE_SIGNATURE_STATUS STREQUAL "verified")
|
|
||||||
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE requires verified signature metadata")
|
|
||||||
endif()
|
|
||||||
string(JSON DRAGONX_LITE_SIGNATURE_VERIFIED_SHA ERROR_VARIABLE DRAGONX_LITE_SIGNATURE_VERIFIED_SHA_ERROR GET "${DRAGONX_LITE_BACKEND_MANIFEST_JSON}" signature_verification verified_artifact_sha256)
|
|
||||||
string(JSON DRAGONX_LITE_ARTIFACT_SHA ERROR_VARIABLE DRAGONX_LITE_ARTIFACT_SHA_ERROR GET "${DRAGONX_LITE_BACKEND_MANIFEST_JSON}" artifact sha256)
|
|
||||||
if(DRAGONX_LITE_SIGNATURE_VERIFIED_SHA_ERROR OR DRAGONX_LITE_ARTIFACT_SHA_ERROR)
|
|
||||||
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_MANIFEST is missing artifact/signature SHA-256 metadata")
|
|
||||||
endif()
|
|
||||||
if(NOT DRAGONX_LITE_SIGNATURE_VERIFIED_SHA STREQUAL DRAGONX_LITE_ARTIFACT_SHA)
|
|
||||||
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_MANIFEST signature metadata does not verify the artifact SHA-256")
|
|
||||||
endif()
|
|
||||||
string(JSON DRAGONX_LITE_SIGNATURE_PERFORMED ERROR_VARIABLE DRAGONX_LITE_SIGNATURE_PERFORMED_ERROR GET "${DRAGONX_LITE_BACKEND_MANIFEST_JSON}" signature_verification verification_performed)
|
|
||||||
if(DRAGONX_LITE_SIGNATURE_PERFORMED_ERROR OR NOT DRAGONX_LITE_SIGNATURE_PERFORMED)
|
|
||||||
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE requires verification_performed=true")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(dragonx_lite_backend UNKNOWN IMPORTED)
|
add_library(dragonx_lite_backend UNKNOWN IMPORTED)
|
||||||
set_target_properties(dragonx_lite_backend PROPERTIES
|
set_target_properties(dragonx_lite_backend PROPERTIES
|
||||||
@@ -1204,5 +1184,5 @@ message(STATUS " Lite backend: ${DRAGONX_LITE_BACKEND_READY}")
|
|||||||
message(STATUS " Lite lib: ${DRAGONX_LITE_BACKEND_LIBRARY}")
|
message(STATUS " Lite lib: ${DRAGONX_LITE_BACKEND_LIBRARY}")
|
||||||
message(STATUS " Lite symbols: ${DRAGONX_LITE_BACKEND_SYMBOLS_FILE}")
|
message(STATUS " Lite symbols: ${DRAGONX_LITE_BACKEND_SYMBOLS_FILE}")
|
||||||
message(STATUS " Lite manifest: ${DRAGONX_LITE_BACKEND_MANIFEST}")
|
message(STATUS " Lite manifest: ${DRAGONX_LITE_BACKEND_MANIFEST}")
|
||||||
message(STATUS " Lite signature: ${DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE}")
|
message(STATUS " Lite trust: built-from-source (vendored third_party/silentdragonxlite)")
|
||||||
message(STATUS "")
|
message(STATUS "")
|
||||||
|
|||||||
34
build.sh
34
build.sh
@@ -478,18 +478,28 @@ APPRUN
|
|||||||
done
|
done
|
||||||
[[ -f "$bd/_deps/sdl3-build/libSDL3.so" ]] && cp "$bd/_deps/sdl3-build/libSDL3.so"* "$APPDIR/usr/lib/" 2>/dev/null || true
|
[[ -f "$bd/_deps/sdl3-build/libSDL3.so" ]] && cp "$bd/_deps/sdl3-build/libSDL3.so"* "$APPDIR/usr/lib/" 2>/dev/null || true
|
||||||
|
|
||||||
# appimagetool
|
# appimagetool — pinned to a tagged release and SHA-256 verified before we exec it.
|
||||||
|
# The old "continuous" tag is a MOVING build fetched over the network and run on the release
|
||||||
|
# builder; a compromised/MITM'd artifact would execute here. Verify, or refuse to package.
|
||||||
|
local APPIMAGETOOL_URL="https://github.com/AppImage/appimagetool/releases/download/1.9.0/appimagetool-x86_64.AppImage"
|
||||||
|
local APPIMAGETOOL_SHA256="46fdd785094c7f6e545b61afcfb0f3d98d8eab243f644b4b17698c01d06083d1"
|
||||||
local APPIMAGETOOL=""
|
local APPIMAGETOOL=""
|
||||||
if command -v appimagetool &>/dev/null; then
|
if command -v appimagetool &>/dev/null; then
|
||||||
APPIMAGETOOL="appimagetool"
|
APPIMAGETOOL="appimagetool" # maintainer's own trusted system install
|
||||||
elif [[ -f "$bd/appimagetool-x86_64.AppImage" ]]; then
|
|
||||||
APPIMAGETOOL="$bd/appimagetool-x86_64.AppImage"
|
|
||||||
else
|
else
|
||||||
info "Downloading appimagetool ..."
|
local at="$bd/appimagetool-x86_64.AppImage"
|
||||||
wget -q -O "$bd/appimagetool-x86_64.AppImage" \
|
# Re-verify any cached copy too; a stale unverified download must not be trusted.
|
||||||
"https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
|
if [[ ! -f "$at" ]] || ! echo "${APPIMAGETOOL_SHA256} ${at}" | sha256sum -c --status; then
|
||||||
chmod +x "$bd/appimagetool-x86_64.AppImage"
|
info "Downloading appimagetool 1.9.0 (pinned) ..."
|
||||||
APPIMAGETOOL="$bd/appimagetool-x86_64.AppImage"
|
wget -q -O "$at" "$APPIMAGETOOL_URL"
|
||||||
|
if ! echo "${APPIMAGETOOL_SHA256} ${at}" | sha256sum -c --status; then
|
||||||
|
err "appimagetool SHA-256 verification failed — refusing to use it"
|
||||||
|
rm -f "$at"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
chmod +x "$at"
|
||||||
|
fi
|
||||||
|
APPIMAGETOOL="$at"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local ARCH
|
local ARCH
|
||||||
@@ -1311,3 +1321,9 @@ if $DO_LINUX || $DO_WIN || $DO_MAC; then
|
|||||||
[[ -d "$SCRIPT_DIR/release/windows" ]] && echo -e " ${CYAN}windows/${NC} — .exe + .zip"
|
[[ -d "$SCRIPT_DIR/release/windows" ]] && echo -e " ${CYAN}windows/${NC} — .exe + .zip"
|
||||||
[[ -d "$SCRIPT_DIR/release/mac" ]] && echo -e " ${CYAN}mac/${NC} — .app + .dmg"
|
[[ -d "$SCRIPT_DIR/release/mac" ]] && echo -e " ${CYAN}mac/${NC} — .app + .dmg"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Reaching here means the build completed (real failures exit 1 at their point of failure).
|
||||||
|
# Exit 0 explicitly: the final `[[ -d release/mac ]] && echo` above returns non-zero on a
|
||||||
|
# non-mac build — and since set -e exempts the left side of an &&, that status would otherwise
|
||||||
|
# become the script's exit code and make a successful build report failure (e.g. to CI).
|
||||||
|
exit 0
|
||||||
|
|||||||
Binary file not shown.
@@ -1460,11 +1460,20 @@
|
|||||||
"tt_blur": "Unschärfe-Stärke (0%% = aus, 100%% = maximum)",
|
"tt_blur": "Unschärfe-Stärke (0%% = aus, 100%% = maximum)",
|
||||||
"tt_change_pass": "Die Wallet-Verschlüsselungspassphrase ändern",
|
"tt_change_pass": "Die Wallet-Verschlüsselungspassphrase ändern",
|
||||||
"tt_change_pin": "Ihre Entsperr-PIN ändern",
|
"tt_change_pin": "Ihre Entsperr-PIN ändern",
|
||||||
|
"tt_chat_bubble_accent": "Akzentfarbe für deine ausgehenden Nachrichtenblasen (oder dem aktuellen Theme folgen)",
|
||||||
|
"tt_chat_bubble_style": "Form der Nachrichtenblase: abgerundet, eckig oder minimal (flach, randlos)",
|
||||||
|
"tt_chat_density": "Abstand zwischen Nachrichten: Komfortabel fügt mehr Abstand hinzu; Kompakt zeigt mehr auf dem Bildschirm",
|
||||||
|
"tt_chat_emoji_style": "Emoji als einfarbige Umrisse oder in voller Farbe darstellen",
|
||||||
|
"tt_chat_enter_sends": "Wenn aktiviert, sendet Enter die Nachricht und Shift+Enter fügt einen Zeilenumbruch ein; wenn deaktiviert, fügt Enter einen Zeilenumbruch ein",
|
||||||
|
"tt_chat_font_size": "Skaliere den Chat-Nachrichtentext von 0.8x bis 1.5x. Betrifft nur den Chat-Tab, nicht den Rest der App",
|
||||||
|
"tt_chat_poll_rate": "Wie oft auf neue und 0-conf-Nachrichten geprüft wird (0.5-15 s). Schneller ist reaktionsfreudiger, verbraucht aber mehr CPU",
|
||||||
|
"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_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_clock_format": "24- oder 12-Stunden-Uhr, app-weit. Der Chat-Tab kann sie überschreiben.",
|
||||||
"tt_custom_fees": "Manuelle Gebühreneingabe beim Senden von Transaktionen aktivieren",
|
"tt_custom_fees": "Manuelle Gebühreneingabe beim Senden von Transaktionen aktivieren",
|
||||||
"tt_custom_theme": "Benutzerdefiniertes Theme aktiv",
|
"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",
|
"tt_daemon_install_bundled": "Node stoppen, den installierten dragonxd mit der in diesem Wallet-Build enthaltenen Version überschreiben und dann neu starten",
|
||||||
|
"tt_daemon_refresh": "Version, Größe und Datum des installierten und mitgelieferten dragonxd (oben angezeigt) erneut einlesen",
|
||||||
"tt_daemon_update_check": "Den neuesten dragonxd-Full-Node vom Projekt-Gitea herunterladen und verifizieren, dann zum Anwenden neu starten",
|
"tt_daemon_update_check": "Den neuesten dragonxd-Full-Node vom Projekt-Gitea herunterladen und verifizieren, dann zum Anwenden neu starten",
|
||||||
"tt_debug_collapse": "Debug-Protokollierungsoptionen einklappen",
|
"tt_debug_collapse": "Debug-Protokollierungsoptionen einklappen",
|
||||||
"tt_debug_expand": "Debug-Protokollierungsoptionen ausklappen",
|
"tt_debug_expand": "Debug-Protokollierungsoptionen ausklappen",
|
||||||
@@ -1482,7 +1491,30 @@
|
|||||||
"tt_keep_daemon": "Der Daemon wird beim Ausführen des Einrichtungsassistenten gestoppt",
|
"tt_keep_daemon": "Der Daemon wird beim Ausführen des Einrichtungsassistenten gestoppt",
|
||||||
"tt_language": "Schnittstellensprache der Wallet-UI",
|
"tt_language": "Schnittstellensprache der Wallet-UI",
|
||||||
"tt_layout_hotkey": "Hotkey: Links-/Rechts-Pfeiltasten zum Wechseln der Balance-Layouts",
|
"tt_layout_hotkey": "Hotkey: Links-/Rechts-Pfeiltasten zum Wechseln der Balance-Layouts",
|
||||||
|
"tt_lite_copy": "Das angezeigte Geheimnis in die Zwischenablage kopieren",
|
||||||
|
"tt_lite_decrypt_pass": "Gib deine Passphrase ein, um die Verschlüsselung von der Wallet zu entfernen",
|
||||||
|
"tt_lite_encrypt": "Die Wallet mit der obigen Passphrase verschlüsseln; sie wird sofort gesperrt und benötigt die Passphrase zum Entsperren",
|
||||||
|
"tt_lite_encrypt_pass": "Passphrase, mit der die Wallet verschlüsselt wird. Geht sie verloren, kann die Wallet nicht mehr entsperrt oder wiederhergestellt werden",
|
||||||
|
"tt_lite_hide_wipe": "Das angezeigte Geheimnis ausblenden und sicher aus dem Speicher löschen",
|
||||||
|
"tt_lite_import_key": "Einen privaten Ausgabe- oder Ansichtsschlüssel zum Importieren einfügen; dessen Verlauf erscheint nach der nächsten Synchronisierung",
|
||||||
|
"tt_lite_import_key_btn": "Den eingegebenen privaten Schlüssel in diese Wallet importieren; Guthaben und Verlauf erscheinen nach der nächsten Synchronisierung",
|
||||||
|
"tt_lite_lifecycle_op": "Wähle, ob eine neue Wallet erstellt, eine vorhandene geöffnet oder eine aus einer Seed-Phrase wiederhergestellt werden soll",
|
||||||
|
"tt_lite_lifecycle_pass": "Passphrase, um die Wallet bei diesem Erstellen- / Öffnen- / Wiederherstellen-Vorgang zu entsperren oder zu setzen",
|
||||||
|
"tt_lite_lifecycle_run": "Den ausgewählten Erstellen- / Öffnen- / Wiederherstellen-Vorgang mit den obigen Werten ausführen",
|
||||||
|
"tt_lite_lifecycle_toggle": "Die Bedienelemente zum Erstellen / Öffnen / Wiederherstellen zur Verwaltung deiner Lite-Wallet-Datei ein- oder ausblenden",
|
||||||
|
"tt_lite_lock": "Die Wallet jetzt sperren; zum Entsperren ist eine Passphrase erforderlich und jede Chat-Sitzung wird beendet",
|
||||||
"tt_lite_redownload": "Alle Blöcke erneut vom Lite-Server herunterladen und neu scannen",
|
"tt_lite_redownload": "Alle Blöcke erneut vom Lite-Server herunterladen und neu scannen",
|
||||||
|
"tt_lite_remove_encrypt": "Verschlüsselung entfernen und die Wallet ungeschützt speichern; zum Öffnen ist dann keine Passphrase mehr erforderlich",
|
||||||
|
"tt_lite_restore_account": "HD-Konto-Index zum Wiederherstellen; belasse 0, sofern du nicht mehrere Konten unter diesem Seed verwendet hast",
|
||||||
|
"tt_lite_restore_birthday": "Blockhöhe, bei der die Wallet erstellt wurde; das Scannen beginnt hier. Verwende 0 oder die früheste Höhe, falls unsicher",
|
||||||
|
"tt_lite_restore_overwrite": "Eine vorhandene Wallet-Datei durch diese Wiederherstellung ersetzen. Warnung: überschreibt die aktuellen Wallet-Daten",
|
||||||
|
"tt_lite_restore_seed": "Die 24-word-Wiederherstellungs-Seed-Phrase, aus der diese Wallet wiederhergestellt wird; bei der Eingabe ausgeblendet",
|
||||||
|
"tt_lite_save_seed_file": "Seed und Erstellungsdatum in eine nur für den Eigentümer lesbare Datei (lite-seed-backup.txt) im Konfigurationsordner schreiben",
|
||||||
|
"tt_lite_show_keys": "Die privaten Ausgabeschlüssel dieser Wallet anzeigen. Wer einen Schlüssel besitzt, kann das von ihm kontrollierte Guthaben ausgeben",
|
||||||
|
"tt_lite_show_seed": "Die Wiederherstellungs-Seed-Phrase und das Erstellungsdatum dieser Wallet anzeigen. Wer den Seed besitzt, kann dein Guthaben ausgeben",
|
||||||
|
"tt_lite_unlock": "Die verschlüsselte Wallet mit der obigen Passphrase entsperren",
|
||||||
|
"tt_lite_unlock_pass": "Gib deine Passphrase ein, um die verschlüsselte Wallet zu entsperren",
|
||||||
|
"tt_lite_wallet_path": "Pfad oder Name der Wallet-Datei, die geöffnet oder in die wiederhergestellt werden soll",
|
||||||
"tt_lock": "Die Wallet sofort sperren",
|
"tt_lock": "Die Wallet sofort sperren",
|
||||||
"tt_low_spec": "Alle aufwendigen visuellen Effekte deaktivieren\\nHotkey: Ctrl+Shift+Down",
|
"tt_low_spec": "Alle aufwendigen visuellen Effekte deaktivieren\\nHotkey: Ctrl+Shift+Down",
|
||||||
"tt_merge": "Mehrere UTXOs einer Adresse zusammenführen",
|
"tt_merge": "Mehrere UTXOs einer Adresse zusammenführen",
|
||||||
@@ -1503,12 +1535,17 @@
|
|||||||
"tt_rpc_host": "Hostname des DragonX-Daemons",
|
"tt_rpc_host": "Hostname des DragonX-Daemons",
|
||||||
"tt_rpc_pass": "RPC-Authentifizierungspasswort",
|
"tt_rpc_pass": "RPC-Authentifizierungspasswort",
|
||||||
"tt_rpc_port": "Port für RPC-Verbindungen des Daemons",
|
"tt_rpc_port": "Port für RPC-Verbindungen des Daemons",
|
||||||
|
"tt_rpc_toggle": "Die schreibgeschützten RPC-Verbindungsdaten (Host, Port, Benutzer, Passwort) für den Daemon ein- oder ausblenden",
|
||||||
"tt_rpc_user": "RPC-Authentifizierungsbenutzername",
|
"tt_rpc_user": "RPC-Authentifizierungsbenutzername",
|
||||||
"tt_save_settings": "Alle Einstellungen auf der Festplatte speichern",
|
"tt_save_settings": "Alle Einstellungen auf der Festplatte speichern",
|
||||||
"tt_save_ztx": "Z-Adresse-Transaktionsverlauf lokal für schnelleres Laden speichern",
|
"tt_save_ztx": "Z-Adresse-Transaktionsverlauf lokal für schnelleres Laden speichern",
|
||||||
"tt_scan_themes": "Nach neuen Themes suchen.\\nTheme-Ordner ablegen in:\\n%s",
|
"tt_scan_themes": "Nach neuen Themes suchen.\\nTheme-Ordner ablegen in:\\n%s",
|
||||||
"tt_scanline": "CRT-Scanlinieneffekt in der Konsole",
|
"tt_scanline": "CRT-Scanlinieneffekt in der Konsole",
|
||||||
|
"tt_screenshot_open_dir": "Den Screenshots-Ordner (unter dem Konfigurationsverzeichnis) im Dateimanager öffnen",
|
||||||
|
"tt_screenshot_sweep": "Jedes Theme über jeden Tab durchlaufen und von jedem einen Screenshot in den Screenshots-Ordner der Konfiguration speichern (überschreibt den letzten Durchlauf)",
|
||||||
|
"tt_screenshot_sweep_full": "Wie der Theme-Durchlauf, erfasst aber auch jedes Modal / jeden Dialog / jeden Ablauf mit temporären Offline-Demo-Wallet-Daten",
|
||||||
"tt_seed_backup": "Die 24-Wort-Wiederherstellungsphrase Ihrer Wallet anzeigen und sichern",
|
"tt_seed_backup": "Die 24-Wort-Wiederherstellungsphrase Ihrer Wallet anzeigen und sichern",
|
||||||
|
"tt_seed_demo_chat": "Beispielunterhaltungen in den Chat-Tab einfügen, damit ein Durchlauf dessen UI erfasst; nur im Speicher, beim Neustart verschwunden",
|
||||||
"tt_seed_migrate": "Eine neue Wallet mit Wiederherstellungsphrase erstellen und Ihre Gelder dorthin übertragen",
|
"tt_seed_migrate": "Eine neue Wallet mit Wiederherstellungsphrase erstellen und Ihre Gelder dorthin übertragen",
|
||||||
"tt_set_pin": "Eine 4-8-stellige PIN für schnelles Entsperren festlegen",
|
"tt_set_pin": "Eine 4-8-stellige PIN für schnelles Entsperren festlegen",
|
||||||
"tt_shield_mining": "Transparente Mining-Belohnungen an eine geschirmte Adresse verschieben",
|
"tt_shield_mining": "Transparente Mining-Belohnungen an eine geschirmte Adresse verschieben",
|
||||||
|
|||||||
@@ -1460,11 +1460,20 @@
|
|||||||
"tt_blur": "Cantidad de desenfoque (0%% = apagado, 100%% = máximo)",
|
"tt_blur": "Cantidad de desenfoque (0%% = apagado, 100%% = máximo)",
|
||||||
"tt_change_pass": "Cambiar la contraseña de cifrado de la billetera",
|
"tt_change_pass": "Cambiar la contraseña de cifrado de la billetera",
|
||||||
"tt_change_pin": "Cambiar su PIN de desbloqueo",
|
"tt_change_pin": "Cambiar su PIN de desbloqueo",
|
||||||
|
"tt_chat_bubble_accent": "Color de acento para tus burbujas de mensaje salientes (o sigue el tema actual)",
|
||||||
|
"tt_chat_bubble_style": "Forma de la burbuja de mensaje: redondeada, cuadrada o mínima (plana, sin borde)",
|
||||||
|
"tt_chat_density": "Espaciado entre mensajes: Cómodo añade más relleno; Compacto muestra más en pantalla",
|
||||||
|
"tt_chat_emoji_style": "Muestra los emoji con contorno monocromo o a todo color",
|
||||||
|
"tt_chat_enter_sends": "Si está activado, Enter envía el mensaje y Shift+Enter añade un salto de línea; si está desactivado, Enter añade un salto de línea",
|
||||||
|
"tt_chat_font_size": "Escala el texto de los mensajes de chat de 0.8x a 1.5x. Solo afecta a la pestaña de Chat, no al resto de la app",
|
||||||
|
"tt_chat_poll_rate": "Con qué frecuencia se comprueban mensajes nuevos y de 0-conf (0.5-15 s). Más rápido responde mejor pero usa más CPU",
|
||||||
|
"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_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_clock_format": "Reloj de 24 o 12 horas, en toda la app. El chat puede anularlo.",
|
||||||
"tt_custom_fees": "Habilitar entrada manual de comisiones al enviar transacciones",
|
"tt_custom_fees": "Habilitar entrada manual de comisiones al enviar transacciones",
|
||||||
"tt_custom_theme": "Tema personalizado activo",
|
"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",
|
"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",
|
||||||
|
"tt_daemon_refresh": "Vuelve a leer la versión, el tamaño y la fecha de dragonxd instalado y del incluido que se muestran arriba",
|
||||||
"tt_daemon_update_check": "Descarga y verifica el nodo completo dragonxd más reciente desde el Gitea del proyecto, y luego reinicia para aplicarlo",
|
"tt_daemon_update_check": "Descarga y verifica el nodo completo dragonxd más reciente desde el Gitea del proyecto, y luego reinicia para aplicarlo",
|
||||||
"tt_debug_collapse": "Colapsar opciones de registro de depuración",
|
"tt_debug_collapse": "Colapsar opciones de registro de depuración",
|
||||||
"tt_debug_expand": "Expandir opciones de registro de depuración",
|
"tt_debug_expand": "Expandir opciones de registro de depuración",
|
||||||
@@ -1482,7 +1491,30 @@
|
|||||||
"tt_keep_daemon": "El daemon se detendrá cuando ejecute el asistente de configuración",
|
"tt_keep_daemon": "El daemon se detendrá cuando ejecute el asistente de configuración",
|
||||||
"tt_language": "Idioma de la interfaz de la billetera",
|
"tt_language": "Idioma de la interfaz de la billetera",
|
||||||
"tt_layout_hotkey": "Atajo: teclas de flecha izquierda/derecha para cambiar diseños de Balance",
|
"tt_layout_hotkey": "Atajo: teclas de flecha izquierda/derecha para cambiar diseños de Balance",
|
||||||
|
"tt_lite_copy": "Copia el secreto revelado al portapapeles",
|
||||||
|
"tt_lite_decrypt_pass": "Introduce tu frase de contraseña para quitar el cifrado de la cartera",
|
||||||
|
"tt_lite_encrypt": "Cifra la cartera con la frase de contraseña de arriba; se bloquea de inmediato y requiere la frase para desbloquearse",
|
||||||
|
"tt_lite_encrypt_pass": "Frase de contraseña con la que cifrar la cartera. Si se pierde, la cartera no se puede desbloquear ni recuperar",
|
||||||
|
"tt_lite_hide_wipe": "Oculta el secreto revelado y lo borra de la memoria de forma segura",
|
||||||
|
"tt_lite_import_key": "Pega una clave privada de gasto o de visualización para importar; su historial aparece tras la próxima sincronización",
|
||||||
|
"tt_lite_import_key_btn": "Importa la clave privada introducida en esta cartera; los fondos y el historial aparecen tras la próxima sincronización",
|
||||||
|
"tt_lite_lifecycle_op": "Elige si crear una cartera nueva, abrir una existente o restaurar una desde una frase de recuperación",
|
||||||
|
"tt_lite_lifecycle_pass": "Frase de contraseña para desbloquear o establecer en la cartera durante esta operación de crear / abrir / restaurar",
|
||||||
|
"tt_lite_lifecycle_run": "Ejecuta la operación de crear / abrir / restaurar seleccionada con los valores de arriba",
|
||||||
|
"tt_lite_lifecycle_toggle": "Muestra u oculta los controles de crear / abrir / restaurar para gestionar tu archivo de cartera lite",
|
||||||
|
"tt_lite_lock": "Bloquea la cartera ahora; se necesita una frase de contraseña para desbloquearla y se cierra cualquier sesión de chat",
|
||||||
"tt_lite_redownload": "Volver a descargar y re-escanear todos los bloques del servidor lite",
|
"tt_lite_redownload": "Volver a descargar y re-escanear todos los bloques del servidor lite",
|
||||||
|
"tt_lite_remove_encrypt": "Quita el cifrado y guarda la cartera sin protección; no se requerirá ninguna frase de contraseña para abrirla",
|
||||||
|
"tt_lite_restore_account": "Índice de cuenta HD a restaurar; deja 0 salvo que hayas usado varias cuentas con esta semilla",
|
||||||
|
"tt_lite_restore_birthday": "Altura de bloque en la que se creó la cartera; el escaneo empieza aquí. Usa 0 o la altura más temprana si no estás seguro",
|
||||||
|
"tt_lite_restore_overwrite": "Reemplaza un archivo de cartera existente con esta restauración. Advertencia: sobrescribe los datos de la cartera actual",
|
||||||
|
"tt_lite_restore_seed": "La frase de recuperación de 24-word para restaurar esta cartera; se oculta mientras escribes",
|
||||||
|
"tt_lite_save_seed_file": "Escribe la semilla y la fecha de creación en un archivo solo para el propietario (lite-seed-backup.txt) en la carpeta de configuración",
|
||||||
|
"tt_lite_show_keys": "Revela las claves privadas de gasto de esta cartera. Cualquiera con una clave puede gastar los fondos que controla",
|
||||||
|
"tt_lite_show_seed": "Revela la frase de recuperación y la fecha de creación de esta cartera. Cualquiera con la semilla puede gastar tus fondos",
|
||||||
|
"tt_lite_unlock": "Desbloquea la cartera cifrada con la frase de contraseña de arriba",
|
||||||
|
"tt_lite_unlock_pass": "Introduce tu frase de contraseña para desbloquear la cartera cifrada",
|
||||||
|
"tt_lite_wallet_path": "Ruta o nombre del archivo de cartera que se abrirá o en el que se restaurará",
|
||||||
"tt_lock": "Bloquear la billetera inmediatamente",
|
"tt_lock": "Bloquear la billetera inmediatamente",
|
||||||
"tt_low_spec": "Desactivar todos los efectos visuales pesados\\nAtajo: Ctrl+Shift+Down",
|
"tt_low_spec": "Desactivar todos los efectos visuales pesados\\nAtajo: Ctrl+Shift+Down",
|
||||||
"tt_merge": "Consolidar múltiples UTXOs en una dirección",
|
"tt_merge": "Consolidar múltiples UTXOs en una dirección",
|
||||||
@@ -1503,12 +1535,17 @@
|
|||||||
"tt_rpc_host": "Nombre de host del daemon DragonX",
|
"tt_rpc_host": "Nombre de host del daemon DragonX",
|
||||||
"tt_rpc_pass": "Contraseña de autenticación RPC",
|
"tt_rpc_pass": "Contraseña de autenticación RPC",
|
||||||
"tt_rpc_port": "Puerto para conexiones RPC del daemon",
|
"tt_rpc_port": "Puerto para conexiones RPC del daemon",
|
||||||
|
"tt_rpc_toggle": "Muestra u oculta los datos de conexión RPC de solo lectura (host, puerto, usuario, contraseña) del daemon",
|
||||||
"tt_rpc_user": "Nombre de usuario de autenticación RPC",
|
"tt_rpc_user": "Nombre de usuario de autenticación RPC",
|
||||||
"tt_save_settings": "Guardar todas las configuraciones en disco",
|
"tt_save_settings": "Guardar todas las configuraciones en disco",
|
||||||
"tt_save_ztx": "Almacenar historial de transacciones de z-address localmente para carga más rápida",
|
"tt_save_ztx": "Almacenar historial de transacciones de z-address localmente para carga más rápida",
|
||||||
"tt_scan_themes": "Buscar nuevos temas.\\nColoque carpetas de temas en:\\n%s",
|
"tt_scan_themes": "Buscar nuevos temas.\\nColoque carpetas de temas en:\\n%s",
|
||||||
"tt_scanline": "Efecto de líneas de escaneo CRT en la consola",
|
"tt_scanline": "Efecto de líneas de escaneo CRT en la consola",
|
||||||
|
"tt_screenshot_open_dir": "Abre la carpeta de capturas (dentro del directorio de configuración) en tu explorador de archivos",
|
||||||
|
"tt_screenshot_sweep": "Recorre cada tema en cada pestaña y guarda una captura de cada uno en la carpeta de capturas de la configuración (sobrescribe el último recorrido)",
|
||||||
|
"tt_screenshot_sweep_full": "Como el recorrido de temas, pero además captura cada modal / diálogo / flujo usando datos de cartera de demostración temporales y sin conexión",
|
||||||
"tt_seed_backup": "Muestra y respalda la frase de recuperación de 24 palabras de tu cartera",
|
"tt_seed_backup": "Muestra y respalda la frase de recuperación de 24 palabras de tu cartera",
|
||||||
|
"tt_seed_demo_chat": "Inserta conversaciones de ejemplo en la pestaña de Chat para que un recorrido capture su interfaz; solo en memoria, se pierde al reiniciar",
|
||||||
"tt_seed_migrate": "Crea una nueva cartera con frase de recuperación y traslada tus fondos a ella",
|
"tt_seed_migrate": "Crea una nueva cartera con frase de recuperación y traslada tus fondos a ella",
|
||||||
"tt_set_pin": "Establecer un PIN de 4-8 dígitos para desbloqueo rápido",
|
"tt_set_pin": "Establecer un PIN de 4-8 dígitos para desbloqueo rápido",
|
||||||
"tt_shield_mining": "Mover recompensas de minería transparentes a una dirección blindada",
|
"tt_shield_mining": "Mover recompensas de minería transparentes a una dirección blindada",
|
||||||
|
|||||||
@@ -1460,11 +1460,20 @@
|
|||||||
"tt_blur": "Quantité de flou (0%% = désactivé, 100%% = maximum)",
|
"tt_blur": "Quantité de flou (0%% = désactivé, 100%% = maximum)",
|
||||||
"tt_change_pass": "Changer la phrase secrète de chiffrement du portefeuille",
|
"tt_change_pass": "Changer la phrase secrète de chiffrement du portefeuille",
|
||||||
"tt_change_pin": "Changer votre PIN de déverrouillage",
|
"tt_change_pin": "Changer votre PIN de déverrouillage",
|
||||||
|
"tt_chat_bubble_accent": "Couleur d'accent de vos bulles de message sortantes (ou suivre le thème actuel)",
|
||||||
|
"tt_chat_bubble_style": "Forme de la bulle de message : arrondie, carrée ou minimale (plate, sans bordure)",
|
||||||
|
"tt_chat_density": "Espacement entre les messages : Confortable ajoute plus de marge ; Compact en affiche davantage à l'écran",
|
||||||
|
"tt_chat_emoji_style": "Affiche les emoji en contour monochrome ou en couleur",
|
||||||
|
"tt_chat_enter_sends": "Si activé, Enter envoie le message et Shift+Enter ajoute un saut de ligne ; si désactivé, Enter ajoute un saut de ligne",
|
||||||
|
"tt_chat_font_size": "Met à l'échelle le texte des messages de chat de 0.8x à 1.5x. N'affecte que l'onglet Chat, pas le reste de l'application",
|
||||||
|
"tt_chat_poll_rate": "Fréquence de vérification des messages nouveaux et 0-conf (0.5-15 s). Plus rapide est plus réactif mais utilise plus de CPU",
|
||||||
|
"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_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_clock_format": "Horloge 24 h ou 12 h, dans toute l'app. Le chat peut la remplacer.",
|
||||||
"tt_custom_fees": "Activer la saisie manuelle des frais lors de l'envoi de transactions",
|
"tt_custom_fees": "Activer la saisie manuelle des frais lors de l'envoi de transactions",
|
||||||
"tt_custom_theme": "Thème personnalisé actif",
|
"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",
|
"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",
|
||||||
|
"tt_daemon_refresh": "Relit la version, la taille et la date de dragonxd installé et fourni affichées ci-dessus",
|
||||||
"tt_daemon_update_check": "Télécharger et vérifier le dernier nœud complet dragonxd depuis le Gitea du projet, puis redémarrer pour l'appliquer",
|
"tt_daemon_update_check": "Télécharger et vérifier le dernier nœud complet dragonxd depuis le Gitea du projet, puis redémarrer pour l'appliquer",
|
||||||
"tt_debug_collapse": "Réduire les options de journalisation de débogage",
|
"tt_debug_collapse": "Réduire les options de journalisation de débogage",
|
||||||
"tt_debug_expand": "Développer les options de journalisation de débogage",
|
"tt_debug_expand": "Développer les options de journalisation de débogage",
|
||||||
@@ -1482,7 +1491,30 @@
|
|||||||
"tt_keep_daemon": "Le daemon s'arrêtera lors de l'exécution de l'assistant de configuration",
|
"tt_keep_daemon": "Le daemon s'arrêtera lors de l'exécution de l'assistant de configuration",
|
||||||
"tt_language": "Langue de l'interface du portefeuille",
|
"tt_language": "Langue de l'interface du portefeuille",
|
||||||
"tt_layout_hotkey": "Raccourci : touches fléchées gauche/droite pour changer les dispositions de Balance",
|
"tt_layout_hotkey": "Raccourci : touches fléchées gauche/droite pour changer les dispositions de Balance",
|
||||||
|
"tt_lite_copy": "Copie le secret révélé dans le presse-papiers",
|
||||||
|
"tt_lite_decrypt_pass": "Saisissez votre phrase de passe pour retirer le chiffrement du portefeuille",
|
||||||
|
"tt_lite_encrypt": "Chiffre le portefeuille avec la phrase de passe ci-dessus ; il se verrouille immédiatement et requiert la phrase pour se déverrouiller",
|
||||||
|
"tt_lite_encrypt_pass": "Phrase de passe pour chiffrer le portefeuille. En cas de perte, le portefeuille ne peut être ni déverrouillé ni récupéré",
|
||||||
|
"tt_lite_hide_wipe": "Masque le secret révélé et l'efface de la mémoire de façon sécurisée",
|
||||||
|
"tt_lite_import_key": "Collez une clé privée de dépense ou de lecture à importer ; son historique apparaît après la prochaine synchronisation",
|
||||||
|
"tt_lite_import_key_btn": "Importe la clé privée saisie dans ce portefeuille ; les fonds et l'historique apparaissent après la prochaine synchronisation",
|
||||||
|
"tt_lite_lifecycle_op": "Choisissez de créer un nouveau portefeuille, d'en ouvrir un existant ou d'en restaurer un à partir d'une phrase de récupération",
|
||||||
|
"tt_lite_lifecycle_pass": "Phrase de passe pour déverrouiller ou définir sur le portefeuille lors de cette opération de création / ouverture / restauration",
|
||||||
|
"tt_lite_lifecycle_run": "Exécute l'opération de création / ouverture / restauration sélectionnée avec les valeurs ci-dessus",
|
||||||
|
"tt_lite_lifecycle_toggle": "Affiche ou masque les commandes de création / ouverture / restauration pour gérer votre fichier de portefeuille lite",
|
||||||
|
"tt_lite_lock": "Verrouille le portefeuille maintenant ; une phrase de passe est requise pour le déverrouiller et toute session de chat est fermée",
|
||||||
"tt_lite_redownload": "Re-télécharger et re-scanner tous les blocs depuis le serveur lite",
|
"tt_lite_redownload": "Re-télécharger et re-scanner tous les blocs depuis le serveur lite",
|
||||||
|
"tt_lite_remove_encrypt": "Retire le chiffrement et stocke le portefeuille sans protection ; aucune phrase de passe ne sera requise pour l'ouvrir",
|
||||||
|
"tt_lite_restore_account": "Index de compte HD à restaurer ; laissez 0 sauf si vous avez utilisé plusieurs comptes avec cette graine",
|
||||||
|
"tt_lite_restore_birthday": "Hauteur de bloc à laquelle le portefeuille a été créé ; l'analyse commence ici. Utilisez 0 ou la hauteur la plus ancienne en cas de doute",
|
||||||
|
"tt_lite_restore_overwrite": "Remplace un fichier de portefeuille existant par cette restauration. Attention : écrase les données du portefeuille actuel",
|
||||||
|
"tt_lite_restore_seed": "La phrase de récupération de 24-word pour restaurer ce portefeuille ; masquée pendant la saisie",
|
||||||
|
"tt_lite_save_seed_file": "Écrit la graine et la date de création dans un fichier réservé au propriétaire (lite-seed-backup.txt) dans le dossier de configuration",
|
||||||
|
"tt_lite_show_keys": "Révèle les clés privées de dépense de ce portefeuille. Quiconque possède une clé peut dépenser les fonds qu'elle contrôle",
|
||||||
|
"tt_lite_show_seed": "Révèle la phrase de récupération et la date de création de ce portefeuille. Quiconque possède la graine peut dépenser vos fonds",
|
||||||
|
"tt_lite_unlock": "Déverrouille le portefeuille chiffré à l'aide de la phrase de passe ci-dessus",
|
||||||
|
"tt_lite_unlock_pass": "Saisissez votre phrase de passe pour déverrouiller le portefeuille chiffré",
|
||||||
|
"tt_lite_wallet_path": "Chemin ou nom du fichier de portefeuille à ouvrir ou dans lequel restaurer",
|
||||||
"tt_lock": "Verrouiller le portefeuille immédiatement",
|
"tt_lock": "Verrouiller le portefeuille immédiatement",
|
||||||
"tt_low_spec": "Désactiver tous les effets visuels lourds\\nRaccourci : Ctrl+Shift+Down",
|
"tt_low_spec": "Désactiver tous les effets visuels lourds\\nRaccourci : Ctrl+Shift+Down",
|
||||||
"tt_merge": "Consolider plusieurs UTXOs vers une adresse",
|
"tt_merge": "Consolider plusieurs UTXOs vers une adresse",
|
||||||
@@ -1503,12 +1535,17 @@
|
|||||||
"tt_rpc_host": "Nom d'hôte du daemon DragonX",
|
"tt_rpc_host": "Nom d'hôte du daemon DragonX",
|
||||||
"tt_rpc_pass": "Mot de passe d'authentification RPC",
|
"tt_rpc_pass": "Mot de passe d'authentification RPC",
|
||||||
"tt_rpc_port": "Port pour les connexions RPC du daemon",
|
"tt_rpc_port": "Port pour les connexions RPC du daemon",
|
||||||
|
"tt_rpc_toggle": "Affiche ou masque les informations de connexion RPC en lecture seule (hôte, port, utilisateur, mot de passe) du daemon",
|
||||||
"tt_rpc_user": "Nom d'utilisateur d'authentification RPC",
|
"tt_rpc_user": "Nom d'utilisateur d'authentification RPC",
|
||||||
"tt_save_settings": "Enregistrer tous les paramètres sur le disque",
|
"tt_save_settings": "Enregistrer tous les paramètres sur le disque",
|
||||||
"tt_save_ztx": "Stocker l'historique des transactions z-address localement pour un chargement plus rapide",
|
"tt_save_ztx": "Stocker l'historique des transactions z-address localement pour un chargement plus rapide",
|
||||||
"tt_scan_themes": "Rechercher de nouveaux thèmes.\\nPlacez les dossiers de thèmes dans :\\n%s",
|
"tt_scan_themes": "Rechercher de nouveaux thèmes.\\nPlacez les dossiers de thèmes dans :\\n%s",
|
||||||
"tt_scanline": "Effet de lignes de balayage CRT dans la console",
|
"tt_scanline": "Effet de lignes de balayage CRT dans la console",
|
||||||
|
"tt_screenshot_open_dir": "Ouvre le dossier de captures (sous le répertoire de configuration) dans votre gestionnaire de fichiers",
|
||||||
|
"tt_screenshot_sweep": "Parcourt chaque thème sur chaque onglet et enregistre une capture de chacun dans le dossier de captures de la configuration (écrase le dernier parcours)",
|
||||||
|
"tt_screenshot_sweep_full": "Comme le parcours des thèmes, mais capture aussi chaque modale / boîte de dialogue / flux à l'aide de données de portefeuille de démonstration temporaires et hors ligne",
|
||||||
"tt_seed_backup": "Afficher et sauvegarder la phrase de récupération de 24 mots de votre portefeuille",
|
"tt_seed_backup": "Afficher et sauvegarder la phrase de récupération de 24 mots de votre portefeuille",
|
||||||
|
"tt_seed_demo_chat": "Injecte des conversations d'exemple dans l'onglet Chat pour qu'un parcours capture son interface ; en mémoire uniquement, perdu au redémarrage",
|
||||||
"tt_seed_migrate": "Créer un nouveau portefeuille à phrase de récupération et y transférer vos fonds",
|
"tt_seed_migrate": "Créer un nouveau portefeuille à phrase de récupération et y transférer vos fonds",
|
||||||
"tt_set_pin": "Définir un PIN de 4-8 chiffres pour un déverrouillage rapide",
|
"tt_set_pin": "Définir un PIN de 4-8 chiffres pour un déverrouillage rapide",
|
||||||
"tt_shield_mining": "Déplacer les récompenses de minage transparentes vers une adresse blindée",
|
"tt_shield_mining": "Déplacer les récompenses de minage transparentes vers une adresse blindée",
|
||||||
|
|||||||
@@ -1460,11 +1460,20 @@
|
|||||||
"tt_blur": "ぼかし量(0%% = オフ、100%% = 最大)",
|
"tt_blur": "ぼかし量(0%% = オフ、100%% = 最大)",
|
||||||
"tt_change_pass": "ウォレットの暗号化パスフレーズを変更",
|
"tt_change_pass": "ウォレットの暗号化パスフレーズを変更",
|
||||||
"tt_change_pin": "アンロック PIN を変更",
|
"tt_change_pin": "アンロック PIN を変更",
|
||||||
|
"tt_chat_bubble_accent": "送信メッセージの吹き出しのアクセントカラー(または現在のテーマに従う)",
|
||||||
|
"tt_chat_bubble_style": "メッセージの吹き出しの形:角丸、四角、またはミニマル(フラット、枠なし)",
|
||||||
|
"tt_chat_density": "メッセージ間の間隔:ゆったりは余白を増やし、コンパクトは画面に多く表示します",
|
||||||
|
"tt_chat_emoji_style": "絵文字をモノクロの輪郭またはフルカラーで表示します",
|
||||||
|
"tt_chat_enter_sends": "オンのとき、Enterでメッセージを送信し、Shift+Enterで改行します。オフのとき、Enterで改行します",
|
||||||
|
"tt_chat_font_size": "チャットのメッセージ文字を0.8xから1.5xで拡大縮小します。チャットタブのみに影響し、アプリの他の部分には影響しません",
|
||||||
|
"tt_chat_poll_rate": "新着および0-confメッセージを確認する頻度(0.5-15 s)。速いほど反応が良くなりますが、CPUをより多く使います",
|
||||||
|
"tt_chat_timestamp": "このタブのみのタイムスタンプ形式:アプリ全体の時計に従うか、24-hourまたは12-hourを強制します",
|
||||||
"tt_clear_ztx": "ローカルにキャッシュされた z-トランザクション履歴を削除",
|
"tt_clear_ztx": "ローカルにキャッシュされた z-トランザクション履歴を削除",
|
||||||
"tt_clock_format": "24時間または12時間表示(アプリ全体)。チャットで上書きできます。",
|
"tt_clock_format": "24時間または12時間表示(アプリ全体)。チャットで上書きできます。",
|
||||||
"tt_custom_fees": "トランザクション送信時に手動手数料入力を有効化",
|
"tt_custom_fees": "トランザクション送信時に手動手数料入力を有効化",
|
||||||
"tt_custom_theme": "カスタムテーマがアクティブ",
|
"tt_custom_theme": "カスタムテーマがアクティブ",
|
||||||
"tt_daemon_install_bundled": "ノードを停止し、インストール済みの dragonxd をこのウォレットビルドにバンドルされたバージョンで上書きしてから再起動します",
|
"tt_daemon_install_bundled": "ノードを停止し、インストール済みの dragonxd をこのウォレットビルドにバンドルされたバージョンで上書きしてから再起動します",
|
||||||
|
"tt_daemon_refresh": "上に表示されているインストール済みおよび同梱のdragonxdのバージョン、サイズ、日付を再読み込みします",
|
||||||
"tt_daemon_update_check": "プロジェクトの Gitea から最新の dragonxd フルノードをダウンロードして検証し、再起動して適用します",
|
"tt_daemon_update_check": "プロジェクトの Gitea から最新の dragonxd フルノードをダウンロードして検証し、再起動して適用します",
|
||||||
"tt_debug_collapse": "デバッグログオプションを折りたたむ",
|
"tt_debug_collapse": "デバッグログオプションを折りたたむ",
|
||||||
"tt_debug_expand": "デバッグログオプションを展開",
|
"tt_debug_expand": "デバッグログオプションを展開",
|
||||||
@@ -1482,7 +1491,30 @@
|
|||||||
"tt_keep_daemon": "セットアップウィザード実行時にデーモンは停止します",
|
"tt_keep_daemon": "セットアップウィザード実行時にデーモンは停止します",
|
||||||
"tt_language": "ウォレット UI のインターフェース言語",
|
"tt_language": "ウォレット UI のインターフェース言語",
|
||||||
"tt_layout_hotkey": "ホットキー:左右矢印キーでバランスレイアウトを切り替え",
|
"tt_layout_hotkey": "ホットキー:左右矢印キーでバランスレイアウトを切り替え",
|
||||||
|
"tt_lite_copy": "表示された秘密情報をクリップボードにコピーします",
|
||||||
|
"tt_lite_decrypt_pass": "ウォレットの暗号化を解除するためにパスフレーズを入力します",
|
||||||
|
"tt_lite_encrypt": "上のパスフレーズでウォレットを暗号化します。すぐにロックされ、解除にはパスフレーズが必要です",
|
||||||
|
"tt_lite_encrypt_pass": "ウォレットを暗号化するパスフレーズ。失うとウォレットのロック解除も復元もできなくなります",
|
||||||
|
"tt_lite_hide_wipe": "表示された秘密情報を隠し、メモリから安全に消去します",
|
||||||
|
"tt_lite_import_key": "インポートする秘密鍵(送金用または閲覧用)を貼り付けます。その履歴は次回の同期後に表示されます",
|
||||||
|
"tt_lite_import_key_btn": "入力した秘密鍵をこのウォレットにインポートします。資金と履歴は次回の同期後に表示されます",
|
||||||
|
"tt_lite_lifecycle_op": "新しいウォレットを作成するか、既存のものを開くか、シードフレーズから復元するかを選びます",
|
||||||
|
"tt_lite_lifecycle_pass": "この作成/開く/復元の操作でウォレットのロック解除または設定に使うパスフレーズ",
|
||||||
|
"tt_lite_lifecycle_run": "上の値で、選択した作成/開く/復元の操作を実行します",
|
||||||
|
"tt_lite_lifecycle_toggle": "ライトウォレットファイルを管理する作成/開く/復元のコントロールを表示または非表示にします",
|
||||||
|
"tt_lite_lock": "ウォレットを今すぐロックします。解除にはパスフレーズが必要で、チャットセッションはすべて終了します",
|
||||||
"tt_lite_redownload": "ライトサーバーからすべてのブロックを再ダウンロードして再スキャン",
|
"tt_lite_redownload": "ライトサーバーからすべてのブロックを再ダウンロードして再スキャン",
|
||||||
|
"tt_lite_remove_encrypt": "暗号化を解除し、ウォレットを保護なしで保存します。開くのにパスフレーズは不要になります",
|
||||||
|
"tt_lite_restore_account": "復元するHDアカウントのインデックス。このシードで複数のアカウントを使っていない限り0のままにします",
|
||||||
|
"tt_lite_restore_birthday": "ウォレットが作成されたブロック高。スキャンはここから始まります。不明な場合は0または最も古い高さを使ってください",
|
||||||
|
"tt_lite_restore_overwrite": "既存のウォレットファイルをこの復元で置き換えます。警告:現在のウォレットデータを上書きします",
|
||||||
|
"tt_lite_restore_seed": "このウォレットを復元するための24-wordのリカバリーシードフレーズ。入力中は非表示になります",
|
||||||
|
"tt_lite_save_seed_file": "シードと作成時期を、設定フォルダ内の所有者のみが読めるファイル(lite-seed-backup.txt)に書き出します",
|
||||||
|
"tt_lite_show_keys": "このウォレットの秘密鍵(送金用)を表示します。鍵を持つ人は誰でもそれが管理する資金を使えます",
|
||||||
|
"tt_lite_show_seed": "このウォレットのリカバリーシードフレーズと作成時期を表示します。シードを持つ人は誰でもあなたの資金を使えます",
|
||||||
|
"tt_lite_unlock": "上のパスフレーズを使って暗号化されたウォレットのロックを解除します",
|
||||||
|
"tt_lite_unlock_pass": "暗号化されたウォレットのロックを解除するためにパスフレーズを入力します",
|
||||||
|
"tt_lite_wallet_path": "開く、または復元先となるウォレットファイルのパスまたは名前",
|
||||||
"tt_lock": "ウォレットを即座にロック",
|
"tt_lock": "ウォレットを即座にロック",
|
||||||
"tt_low_spec": "すべての重い視覚効果を無効化\\nホットキー:Ctrl+Shift+Down",
|
"tt_low_spec": "すべての重い視覚効果を無効化\\nホットキー:Ctrl+Shift+Down",
|
||||||
"tt_merge": "複数の UTXO を一つのアドレスに統合",
|
"tt_merge": "複数の UTXO を一つのアドレスに統合",
|
||||||
@@ -1503,12 +1535,17 @@
|
|||||||
"tt_rpc_host": "DragonX デーモンのホスト名",
|
"tt_rpc_host": "DragonX デーモンのホスト名",
|
||||||
"tt_rpc_pass": "RPC 認証パスワード",
|
"tt_rpc_pass": "RPC 認証パスワード",
|
||||||
"tt_rpc_port": "デーモン RPC 接続用ポート",
|
"tt_rpc_port": "デーモン RPC 接続用ポート",
|
||||||
|
"tt_rpc_toggle": "デーモンの読み取り専用のRPC接続情報(ホスト、ポート、ユーザー、パスワード)を表示または非表示にします",
|
||||||
"tt_rpc_user": "RPC 認証ユーザー名",
|
"tt_rpc_user": "RPC 認証ユーザー名",
|
||||||
"tt_save_settings": "すべての設定をディスクに保存",
|
"tt_save_settings": "すべての設定をディスクに保存",
|
||||||
"tt_save_ztx": "z-address トランザクション履歴をローカルに保存して高速読み込み",
|
"tt_save_ztx": "z-address トランザクション履歴をローカルに保存して高速読み込み",
|
||||||
"tt_scan_themes": "新しいテーマをスキャン。\\nテーマフォルダーをここに配置:\\n%s",
|
"tt_scan_themes": "新しいテーマをスキャン。\\nテーマフォルダーをここに配置:\\n%s",
|
||||||
"tt_scanline": "コンソールでの CRT スキャンライン効果",
|
"tt_scanline": "コンソールでの CRT スキャンライン効果",
|
||||||
|
"tt_screenshot_open_dir": "スクリーンショットフォルダ(設定ディレクトリ内)をファイルマネージャーで開きます",
|
||||||
|
"tt_screenshot_sweep": "すべてのタブですべてのテーマを順に切り替え、それぞれのスクリーンショットを設定のスクリーンショットフォルダに保存します(前回の実行を上書きします)",
|
||||||
|
"tt_screenshot_sweep_full": "テーマの実行と同様ですが、一時的なオフラインのデモウォレットデータを使って、すべてのモーダル/ダイアログ/フローも撮影します",
|
||||||
"tt_seed_backup": "ウォレットの24単語の復元シードフレーズを表示してバックアップします",
|
"tt_seed_backup": "ウォレットの24単語の復元シードフレーズを表示してバックアップします",
|
||||||
|
"tt_seed_demo_chat": "実行でUIを撮影できるように、チャットタブにサンプルの会話を挿入します。メモリ上のみで、再起動で消えます",
|
||||||
"tt_seed_migrate": "新しいシードフレーズウォレットを作成し、資金をそこへ移動します",
|
"tt_seed_migrate": "新しいシードフレーズウォレットを作成し、資金をそこへ移動します",
|
||||||
"tt_set_pin": "クイックアンロック用の 4-8 桁 PIN を設定",
|
"tt_set_pin": "クイックアンロック用の 4-8 桁 PIN を設定",
|
||||||
"tt_shield_mining": "透明マイニング報酬をシールドアドレスに移動",
|
"tt_shield_mining": "透明マイニング報酬をシールドアドレスに移動",
|
||||||
|
|||||||
@@ -1460,11 +1460,20 @@
|
|||||||
"tt_blur": "블러 양 (0%% = 끔, 100%% = 최대)",
|
"tt_blur": "블러 양 (0%% = 끔, 100%% = 최대)",
|
||||||
"tt_change_pass": "지갑 암호화 비밀번호 변경",
|
"tt_change_pass": "지갑 암호화 비밀번호 변경",
|
||||||
"tt_change_pin": "잠금 해제 PIN 변경",
|
"tt_change_pin": "잠금 해제 PIN 변경",
|
||||||
|
"tt_chat_bubble_accent": "보내는 메시지 말풍선의 강조 색상(또는 현재 테마를 따름)",
|
||||||
|
"tt_chat_bubble_style": "메시지 말풍선 모양: 둥근형, 사각형 또는 미니멀(평면, 테두리 없음)",
|
||||||
|
"tt_chat_density": "메시지 간 간격: 편안함은 여백을 더 추가하고; 촘촘함은 화면에 더 많이 표시합니다",
|
||||||
|
"tt_chat_emoji_style": "이모지를 단색 윤곽선 또는 전체 색상으로 렌더링합니다",
|
||||||
|
"tt_chat_enter_sends": "켜면 Enter가 메시지를 보내고 Shift+Enter가 줄바꿈을 추가합니다; 끄면 Enter가 줄바꿈을 추가합니다",
|
||||||
|
"tt_chat_font_size": "채팅 메시지 텍스트를 0.8x에서 1.5x까지 조정합니다. 채팅 탭에만 적용되며 앱의 나머지 부분에는 영향을 주지 않습니다",
|
||||||
|
"tt_chat_poll_rate": "새 메시지 및 0-conf 메시지를 확인하는 빈도(0.5-15 s). 빠를수록 반응성이 좋지만 CPU를 더 사용합니다",
|
||||||
|
"tt_chat_timestamp": "이 탭에만 적용되는 타임스탬프 형식: 앱 전체 시계를 따르거나 24-hour 또는 12-hour로 강제합니다",
|
||||||
"tt_clear_ztx": "로컬에 캐시된 z-트랜잭션 기록 삭제",
|
"tt_clear_ztx": "로컬에 캐시된 z-트랜잭션 기록 삭제",
|
||||||
"tt_clock_format": "24시간 또는 12시간 형식(앱 전체). 채팅에서 재정의할 수 있습니다.",
|
"tt_clock_format": "24시간 또는 12시간 형식(앱 전체). 채팅에서 재정의할 수 있습니다.",
|
||||||
"tt_custom_fees": "거래 전송 시 수동 수수료 입력 활성화",
|
"tt_custom_fees": "거래 전송 시 수동 수수료 입력 활성화",
|
||||||
"tt_custom_theme": "사용자 지정 테마 활성화됨",
|
"tt_custom_theme": "사용자 지정 테마 활성화됨",
|
||||||
"tt_daemon_install_bundled": "노드를 중지하고 설치된 dragonxd를 이 지갑 빌드에 번들된 버전으로 덮어쓴 다음 재시작합니다",
|
"tt_daemon_install_bundled": "노드를 중지하고 설치된 dragonxd를 이 지갑 빌드에 번들된 버전으로 덮어쓴 다음 재시작합니다",
|
||||||
|
"tt_daemon_refresh": "위에 표시된 설치 및 번들 dragonxd의 버전, 크기, 날짜를 다시 읽어옵니다",
|
||||||
"tt_daemon_update_check": "프로젝트 Gitea에서 최신 dragonxd 풀 노드를 다운로드하고 검증한 다음, 재시작하여 적용합니다",
|
"tt_daemon_update_check": "프로젝트 Gitea에서 최신 dragonxd 풀 노드를 다운로드하고 검증한 다음, 재시작하여 적용합니다",
|
||||||
"tt_debug_collapse": "디버그 로깅 옵션 접기",
|
"tt_debug_collapse": "디버그 로깅 옵션 접기",
|
||||||
"tt_debug_expand": "디버그 로깅 옵션 펼치기",
|
"tt_debug_expand": "디버그 로깅 옵션 펼치기",
|
||||||
@@ -1482,7 +1491,30 @@
|
|||||||
"tt_keep_daemon": "설정 마법사를 실행하면 데몬이 여전히 중지됩니다",
|
"tt_keep_daemon": "설정 마법사를 실행하면 데몬이 여전히 중지됩니다",
|
||||||
"tt_language": "지갑 UI 인터페이스 언어",
|
"tt_language": "지갑 UI 인터페이스 언어",
|
||||||
"tt_layout_hotkey": "단축키: 좌/우 화살표 키로 잔액 레이아웃 전환",
|
"tt_layout_hotkey": "단축키: 좌/우 화살표 키로 잔액 레이아웃 전환",
|
||||||
|
"tt_lite_copy": "표시된 비밀을 클립보드에 복사합니다",
|
||||||
|
"tt_lite_decrypt_pass": "지갑에서 암호화를 제거하려면 암호를 입력하세요",
|
||||||
|
"tt_lite_encrypt": "위 암호로 지갑을 암호화합니다; 즉시 잠기며 잠금 해제하려면 암호가 필요합니다",
|
||||||
|
"tt_lite_encrypt_pass": "지갑을 암호화할 암호. 분실하면 지갑을 잠금 해제하거나 복구할 수 없습니다",
|
||||||
|
"tt_lite_hide_wipe": "표시된 비밀을 숨기고 메모리에서 안전하게 지웁니다",
|
||||||
|
"tt_lite_import_key": "가져올 개인 지출 또는 조회 키를 붙여넣으세요; 다음 동기화 후 해당 내역이 나타납니다",
|
||||||
|
"tt_lite_import_key_btn": "입력한 개인 키를 이 지갑으로 가져옵니다; 자금과 내역은 다음 동기화 후 나타납니다",
|
||||||
|
"tt_lite_lifecycle_op": "새 지갑을 생성할지, 기존 지갑을 열지, 시드 문구로 복구할지 선택합니다",
|
||||||
|
"tt_lite_lifecycle_pass": "이 생성 / 열기 / 복구 작업 중 지갑을 잠금 해제하거나 설정할 암호",
|
||||||
|
"tt_lite_lifecycle_run": "위 값으로 선택한 생성 / 열기 / 복구 작업을 실행합니다",
|
||||||
|
"tt_lite_lifecycle_toggle": "라이트 지갑 파일을 관리하기 위한 생성 / 열기 / 복구 컨트롤을 표시하거나 숨깁니다",
|
||||||
|
"tt_lite_lock": "지금 지갑을 잠급니다; 잠금 해제하려면 암호가 필요하며 모든 채팅 세션이 종료됩니다",
|
||||||
"tt_lite_redownload": "라이트 서버에서 모든 블록을 다시 다운로드하고 다시 스캔합니다",
|
"tt_lite_redownload": "라이트 서버에서 모든 블록을 다시 다운로드하고 다시 스캔합니다",
|
||||||
|
"tt_lite_remove_encrypt": "암호화를 제거하고 지갑을 보호되지 않은 상태로 저장합니다; 지갑을 열 때 암호가 필요하지 않습니다",
|
||||||
|
"tt_lite_restore_account": "복구할 HD 계정 인덱스; 이 시드로 여러 계정을 사용하지 않았다면 0으로 두세요",
|
||||||
|
"tt_lite_restore_birthday": "지갑이 생성된 블록 높이; 여기서부터 스캔이 시작됩니다. 확실하지 않으면 0 또는 가장 이른 높이를 사용하세요",
|
||||||
|
"tt_lite_restore_overwrite": "기존 지갑 파일을 이 복구본으로 대체합니다. 경고: 현재 지갑 데이터를 덮어씁니다",
|
||||||
|
"tt_lite_restore_seed": "이 지갑을 복구할 24-word 복구 시드 문구; 입력하는 동안 숨겨집니다",
|
||||||
|
"tt_lite_save_seed_file": "시드와 생성 높이를 설정 폴더의 소유자 전용 파일(lite-seed-backup.txt)에 기록합니다",
|
||||||
|
"tt_lite_show_keys": "이 지갑의 개인 지출 키를 표시합니다. 키를 가진 사람은 누구나 그 키가 제어하는 자금을 사용할 수 있습니다",
|
||||||
|
"tt_lite_show_seed": "이 지갑의 복구 시드 문구와 생성 높이를 표시합니다. 시드를 가진 사람은 누구나 자금을 사용할 수 있습니다",
|
||||||
|
"tt_lite_unlock": "위 암호를 사용하여 암호화된 지갑을 잠금 해제합니다",
|
||||||
|
"tt_lite_unlock_pass": "암호화된 지갑을 잠금 해제하려면 암호를 입력하세요",
|
||||||
|
"tt_lite_wallet_path": "열거나 복구할 지갑 파일의 경로 또는 이름",
|
||||||
"tt_lock": "지갑 즉시 잠금",
|
"tt_lock": "지갑 즉시 잠금",
|
||||||
"tt_low_spec": "모든 고부하 시각 효과 비활성화\\n단축키: Ctrl+Shift+Down",
|
"tt_low_spec": "모든 고부하 시각 효과 비활성화\\n단축키: Ctrl+Shift+Down",
|
||||||
"tt_merge": "여러 UTXO를 하나의 주소로 통합",
|
"tt_merge": "여러 UTXO를 하나의 주소로 통합",
|
||||||
@@ -1503,12 +1535,17 @@
|
|||||||
"tt_rpc_host": "DragonX 데몬 호스트 이름",
|
"tt_rpc_host": "DragonX 데몬 호스트 이름",
|
||||||
"tt_rpc_pass": "RPC 인증 비밀번호",
|
"tt_rpc_pass": "RPC 인증 비밀번호",
|
||||||
"tt_rpc_port": "데몬 RPC 연결 포트",
|
"tt_rpc_port": "데몬 RPC 연결 포트",
|
||||||
|
"tt_rpc_toggle": "데몬의 읽기 전용 RPC 연결 정보(호스트, 포트, 사용자, 비밀번호)를 표시하거나 숨깁니다",
|
||||||
"tt_rpc_user": "RPC 인증 사용자 이름",
|
"tt_rpc_user": "RPC 인증 사용자 이름",
|
||||||
"tt_save_settings": "모든 설정을 디스크에 저장",
|
"tt_save_settings": "모든 설정을 디스크에 저장",
|
||||||
"tt_save_ztx": "z-address 거래 기록을 로컬에 저장하여 빠른 로딩",
|
"tt_save_ztx": "z-address 거래 기록을 로컬에 저장하여 빠른 로딩",
|
||||||
"tt_scan_themes": "새 테마 검색.\\n테마 폴더를 여기에 배치:\\n%s",
|
"tt_scan_themes": "새 테마 검색.\\n테마 폴더를 여기에 배치:\\n%s",
|
||||||
"tt_scanline": "콘솔에서 CRT 스캔라인 효과",
|
"tt_scanline": "콘솔에서 CRT 스캔라인 효과",
|
||||||
|
"tt_screenshot_open_dir": "파일 관리자에서 screenshots 폴더(설정 디렉터리 아래)를 엽니다",
|
||||||
|
"tt_screenshot_sweep": "모든 탭에서 모든 테마를 순환하며 각각의 스크린샷을 설정 screenshots 폴더에 저장합니다(마지막 스윕을 덮어씀)",
|
||||||
|
"tt_screenshot_sweep_full": "테마 스윕과 유사하지만 임시 오프라인 데모 지갑 데이터를 사용하여 모든 모달 / 대화 상자 / 흐름도 캡처합니다",
|
||||||
"tt_seed_backup": "지갑의 24단어 복구 시드 문구를 표시하고 백업합니다",
|
"tt_seed_backup": "지갑의 24단어 복구 시드 문구를 표시하고 백업합니다",
|
||||||
|
"tt_seed_demo_chat": "스윕이 UI를 캡처하도록 샘플 대화를 채팅 탭에 삽입합니다; 메모리에만 저장되며 재시작 시 사라집니다",
|
||||||
"tt_seed_migrate": "새 시드 문구 지갑을 만들고 자금을 그곳으로 옮깁니다",
|
"tt_seed_migrate": "새 시드 문구 지갑을 만들고 자금을 그곳으로 옮깁니다",
|
||||||
"tt_set_pin": "빠른 잠금 해제를 위한 4-8자리 PIN 설정",
|
"tt_set_pin": "빠른 잠금 해제를 위한 4-8자리 PIN 설정",
|
||||||
"tt_shield_mining": "투명 채굴 보상을 차폐 주소로 이동",
|
"tt_shield_mining": "투명 채굴 보상을 차폐 주소로 이동",
|
||||||
|
|||||||
@@ -1460,11 +1460,20 @@
|
|||||||
"tt_blur": "Quantidade de desfoque (0%% = desligado, 100%% = máximo)",
|
"tt_blur": "Quantidade de desfoque (0%% = desligado, 100%% = máximo)",
|
||||||
"tt_change_pass": "Alterar a frase secreta de encriptação da carteira",
|
"tt_change_pass": "Alterar a frase secreta de encriptação da carteira",
|
||||||
"tt_change_pin": "Alterar seu PIN de desbloqueio",
|
"tt_change_pin": "Alterar seu PIN de desbloqueio",
|
||||||
|
"tt_chat_bubble_accent": "Cor de destaque para seus balões de mensagem enviados (ou seguir o tema atual)",
|
||||||
|
"tt_chat_bubble_style": "Formato do balão de mensagem: arredondado, quadrado ou minimalista (plano, sem borda)",
|
||||||
|
"tt_chat_density": "Espaçamento entre mensagens: Confortável adiciona mais espaçamento; Compacto exibe mais na tela",
|
||||||
|
"tt_chat_emoji_style": "Renderizar emojis em contorno monocromático ou colorido",
|
||||||
|
"tt_chat_enter_sends": "Quando ativado, Enter envia a mensagem e Shift+Enter adiciona uma nova linha; quando desativado, Enter adiciona uma nova linha",
|
||||||
|
"tt_chat_font_size": "Dimensionar o texto das mensagens de chat de 0.8x a 1.5x. Afeta apenas a aba Chat, não o restante do aplicativo",
|
||||||
|
"tt_chat_poll_rate": "Com que frequência verificar mensagens novas e 0-conf (0.5-15 s). Mais rápido é mais responsivo, mas usa mais CPU",
|
||||||
|
"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_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_clock_format": "Relógio de 24 ou 12 horas, em todo o app. O chat pode substituí-lo.",
|
||||||
"tt_custom_fees": "Ativar entrada manual de taxas ao enviar transações",
|
"tt_custom_fees": "Ativar entrada manual de taxas ao enviar transações",
|
||||||
"tt_custom_theme": "Tema personalizado ativo",
|
"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",
|
"tt_daemon_install_bundled": "Parar o nó, sobrescrever o dragonxd instalado com a versão incluída nesta compilação da carteira e reiniciar",
|
||||||
|
"tt_daemon_refresh": "Reler a versão, o tamanho e a data do dragonxd instalado e do incorporado, mostrados acima",
|
||||||
"tt_daemon_update_check": "Baixe e verifique o nó completo dragonxd mais recente do Gitea do projeto e, em seguida, reinicie para aplicar",
|
"tt_daemon_update_check": "Baixe e verifique o nó completo dragonxd mais recente do Gitea do projeto e, em seguida, reinicie para aplicar",
|
||||||
"tt_debug_collapse": "Recolher opções de registro de depuração",
|
"tt_debug_collapse": "Recolher opções de registro de depuração",
|
||||||
"tt_debug_expand": "Expandir opções de registro de depuração",
|
"tt_debug_expand": "Expandir opções de registro de depuração",
|
||||||
@@ -1482,7 +1491,30 @@
|
|||||||
"tt_keep_daemon": "O daemon será parado ao executar o assistente de configuração",
|
"tt_keep_daemon": "O daemon será parado ao executar o assistente de configuração",
|
||||||
"tt_language": "Idioma da interface da carteira",
|
"tt_language": "Idioma da interface da carteira",
|
||||||
"tt_layout_hotkey": "Atalho: teclas de seta esquerda/direita para alternar layouts de Saldo",
|
"tt_layout_hotkey": "Atalho: teclas de seta esquerda/direita para alternar layouts de Saldo",
|
||||||
|
"tt_lite_copy": "Copiar o segredo revelado para a área de transferência",
|
||||||
|
"tt_lite_decrypt_pass": "Digite sua frase-senha para remover a criptografia da carteira",
|
||||||
|
"tt_lite_encrypt": "Criptografar a carteira com a frase-senha acima; ela é bloqueada imediatamente e exige a frase-senha para desbloquear",
|
||||||
|
"tt_lite_encrypt_pass": "Frase-senha com a qual criptografar a carteira. Se perdida, a carteira não pode ser desbloqueada nem recuperada",
|
||||||
|
"tt_lite_hide_wipe": "Ocultar o segredo revelado e apagá-lo com segurança da memória",
|
||||||
|
"tt_lite_import_key": "Cole uma chave privada de gasto ou de visualização para importar; seu histórico aparece após a próxima sincronização",
|
||||||
|
"tt_lite_import_key_btn": "Importar a chave privada informada para esta carteira; os fundos e o histórico aparecem após a próxima sincronização",
|
||||||
|
"tt_lite_lifecycle_op": "Escolha entre criar uma nova carteira, abrir uma existente ou restaurar uma a partir de uma frase de recuperação",
|
||||||
|
"tt_lite_lifecycle_pass": "Frase-senha para desbloquear ou definir na carteira durante esta operação de criar / abrir / restaurar",
|
||||||
|
"tt_lite_lifecycle_run": "Executar a operação selecionada de criar / abrir / restaurar com os valores acima",
|
||||||
|
"tt_lite_lifecycle_toggle": "Mostrar ou ocultar os controles de criar / abrir / restaurar para gerenciar o arquivo da sua carteira lite",
|
||||||
|
"tt_lite_lock": "Bloquear a carteira agora; uma frase-senha é necessária para desbloquear e qualquer sessão de chat é encerrada",
|
||||||
"tt_lite_redownload": "Rebaixar e reescanear todos os blocos do servidor lite",
|
"tt_lite_redownload": "Rebaixar e reescanear todos os blocos do servidor lite",
|
||||||
|
"tt_lite_remove_encrypt": "Remover a criptografia e armazenar a carteira desprotegida; nenhuma frase-senha será necessária para abri-la",
|
||||||
|
"tt_lite_restore_account": "Índice da conta HD a restaurar; deixe 0 a menos que você tenha usado várias contas sob esta frase de recuperação",
|
||||||
|
"tt_lite_restore_birthday": "Altura de bloco em que a carteira foi criada; a varredura começa aqui. Use 0 ou a altura mais antiga se não tiver certeza",
|
||||||
|
"tt_lite_restore_overwrite": "Substituir um arquivo de carteira existente por esta restauração. Aviso: sobrescreve os dados da carteira atual",
|
||||||
|
"tt_lite_restore_seed": "A frase de recuperação de 24-word para restaurar esta carteira; oculta enquanto você digita",
|
||||||
|
"tt_lite_save_seed_file": "Gravar a frase de recuperação e a data de criação em um arquivo restrito ao dono (lite-seed-backup.txt) na pasta de configuração",
|
||||||
|
"tt_lite_show_keys": "Revelar as chaves privadas de gasto desta carteira. Qualquer pessoa com uma chave pode gastar os fundos que ela controla",
|
||||||
|
"tt_lite_show_seed": "Revelar a frase de recuperação e a data de criação desta carteira. Qualquer pessoa com a frase de recuperação pode gastar seus fundos",
|
||||||
|
"tt_lite_unlock": "Desbloquear a carteira criptografada usando a frase-senha acima",
|
||||||
|
"tt_lite_unlock_pass": "Digite sua frase-senha para desbloquear a carteira criptografada",
|
||||||
|
"tt_lite_wallet_path": "Caminho ou nome do arquivo da carteira a abrir ou para o qual restaurar",
|
||||||
"tt_lock": "Bloquear a carteira imediatamente",
|
"tt_lock": "Bloquear a carteira imediatamente",
|
||||||
"tt_low_spec": "Desativar todos os efeitos visuais pesados\\nAtalho: Ctrl+Shift+Down",
|
"tt_low_spec": "Desativar todos os efeitos visuais pesados\\nAtalho: Ctrl+Shift+Down",
|
||||||
"tt_merge": "Consolidar múltiplos UTXOs em um endereço",
|
"tt_merge": "Consolidar múltiplos UTXOs em um endereço",
|
||||||
@@ -1503,12 +1535,17 @@
|
|||||||
"tt_rpc_host": "Nome do host do daemon DragonX",
|
"tt_rpc_host": "Nome do host do daemon DragonX",
|
||||||
"tt_rpc_pass": "Senha de autenticação RPC",
|
"tt_rpc_pass": "Senha de autenticação RPC",
|
||||||
"tt_rpc_port": "Porta para conexões RPC do daemon",
|
"tt_rpc_port": "Porta para conexões RPC do daemon",
|
||||||
|
"tt_rpc_toggle": "Mostrar ou ocultar os detalhes de conexão RPC somente leitura (host, porta, usuário, senha) do daemon",
|
||||||
"tt_rpc_user": "Nome de usuário de autenticação RPC",
|
"tt_rpc_user": "Nome de usuário de autenticação RPC",
|
||||||
"tt_save_settings": "Salvar todas as configurações no disco",
|
"tt_save_settings": "Salvar todas as configurações no disco",
|
||||||
"tt_save_ztx": "Armazenar histórico de transações z-address localmente para carregamento mais rápido",
|
"tt_save_ztx": "Armazenar histórico de transações z-address localmente para carregamento mais rápido",
|
||||||
"tt_scan_themes": "Procurar novos temas.\\nColoque pastas de temas em:\\n%s",
|
"tt_scan_themes": "Procurar novos temas.\\nColoque pastas de temas em:\\n%s",
|
||||||
"tt_scanline": "Efeito de linhas de varredura CRT no console",
|
"tt_scanline": "Efeito de linhas de varredura CRT no console",
|
||||||
|
"tt_screenshot_open_dir": "Abrir a pasta screenshots (dentro do diretório de configuração) no seu gerenciador de arquivos",
|
||||||
|
"tt_screenshot_sweep": "Percorrer cada tema em cada aba, salvando uma captura de tela de cada uma na pasta screenshots de configuração (sobrescreve a última varredura)",
|
||||||
|
"tt_screenshot_sweep_full": "Como a varredura de temas, mas também captura cada modal / caixa de diálogo / fluxo usando dados temporários de carteira de demonstração offline",
|
||||||
"tt_seed_backup": "Mostrar e fazer backup da frase de recuperação de 24 palavras da sua carteira",
|
"tt_seed_backup": "Mostrar e fazer backup da frase de recuperação de 24 palavras da sua carteira",
|
||||||
|
"tt_seed_demo_chat": "Injetar conversas de exemplo na aba Chat para que uma varredura capture sua interface; apenas na memória, some ao reiniciar",
|
||||||
"tt_seed_migrate": "Criar uma nova carteira com frase de recuperação e mover seus fundos para ela",
|
"tt_seed_migrate": "Criar uma nova carteira com frase de recuperação e mover seus fundos para ela",
|
||||||
"tt_set_pin": "Definir um PIN de 4-8 dígitos para desbloqueio rápido",
|
"tt_set_pin": "Definir um PIN de 4-8 dígitos para desbloqueio rápido",
|
||||||
"tt_shield_mining": "Mover recompensas de mineração transparentes para um endereço blindado",
|
"tt_shield_mining": "Mover recompensas de mineração transparentes para um endereço blindado",
|
||||||
|
|||||||
@@ -1460,11 +1460,20 @@
|
|||||||
"tt_blur": "Степень размытия (0%% = выкл., 100%% = максимум)",
|
"tt_blur": "Степень размытия (0%% = выкл., 100%% = максимум)",
|
||||||
"tt_change_pass": "Сменить пароль шифрования кошелька",
|
"tt_change_pass": "Сменить пароль шифрования кошелька",
|
||||||
"tt_change_pin": "Изменить PIN-код разблокировки",
|
"tt_change_pin": "Изменить PIN-код разблокировки",
|
||||||
|
"tt_chat_bubble_accent": "Акцентный цвет для ваших исходящих пузырьков сообщений (или следовать текущей теме)",
|
||||||
|
"tt_chat_bubble_style": "Форма пузырька сообщения: скруглённая, квадратная или минимальная (плоская, без границы)",
|
||||||
|
"tt_chat_density": "Интервал между сообщениями: Комфортный добавляет больше отступов; Компактный вмещает больше на экране",
|
||||||
|
"tt_chat_emoji_style": "Отображать эмодзи в монохромном контуре или полноцветно",
|
||||||
|
"tt_chat_enter_sends": "Когда включено, Enter отправляет сообщение, а Shift+Enter добавляет новую строку; когда выключено, Enter добавляет новую строку",
|
||||||
|
"tt_chat_font_size": "Масштаб текста сообщений чата от 0.8x до 1.5x. Влияет только на вкладку «Чат», не на остальную часть приложения",
|
||||||
|
"tt_chat_poll_rate": "Как часто проверять новые и 0-conf сообщения (0.5-15 s). Быстрее — отзывчивее, но использует больше CPU",
|
||||||
|
"tt_chat_timestamp": "Формат времени только для этой вкладки: следовать общим настройкам часов приложения либо принудительно 24-hour или 12-hour",
|
||||||
"tt_clear_ztx": "Удалить локально кешированную историю z-транзакций",
|
"tt_clear_ztx": "Удалить локально кешированную историю z-транзакций",
|
||||||
"tt_clock_format": "24- или 12-часовой формат для всего приложения. Чат может переопределить.",
|
"tt_clock_format": "24- или 12-часовой формат для всего приложения. Чат может переопределить.",
|
||||||
"tt_custom_fees": "Включить ручной ввод комиссий при отправке транзакций",
|
"tt_custom_fees": "Включить ручной ввод комиссий при отправке транзакций",
|
||||||
"tt_custom_theme": "Пользовательская тема активна",
|
"tt_custom_theme": "Пользовательская тема активна",
|
||||||
"tt_daemon_install_bundled": "Остановить узел, перезаписать установленный dragonxd версией, встроенной в эту сборку кошелька, затем перезапустить",
|
"tt_daemon_install_bundled": "Остановить узел, перезаписать установленный dragonxd версией, встроенной в эту сборку кошелька, затем перезапустить",
|
||||||
|
"tt_daemon_refresh": "Перечитать версию, размер и дату установленного и встроенного dragonxd, показанные выше",
|
||||||
"tt_daemon_update_check": "Скачать и проверить последний полный узел dragonxd из проектного Gitea, затем перезапустить для применения",
|
"tt_daemon_update_check": "Скачать и проверить последний полный узел dragonxd из проектного Gitea, затем перезапустить для применения",
|
||||||
"tt_debug_collapse": "Свернуть параметры журнала отладки",
|
"tt_debug_collapse": "Свернуть параметры журнала отладки",
|
||||||
"tt_debug_expand": "Развернуть параметры журнала отладки",
|
"tt_debug_expand": "Развернуть параметры журнала отладки",
|
||||||
@@ -1482,7 +1491,30 @@
|
|||||||
"tt_keep_daemon": "Демон будет остановлен при запуске мастера настройки",
|
"tt_keep_daemon": "Демон будет остановлен при запуске мастера настройки",
|
||||||
"tt_language": "Язык интерфейса кошелька",
|
"tt_language": "Язык интерфейса кошелька",
|
||||||
"tt_layout_hotkey": "Горячая клавиша: стрелки влево/вправо для переключения раскладок Баланса",
|
"tt_layout_hotkey": "Горячая клавиша: стрелки влево/вправо для переключения раскладок Баланса",
|
||||||
|
"tt_lite_copy": "Скопировать показанный секрет в буфер обмена",
|
||||||
|
"tt_lite_decrypt_pass": "Введите пароль, чтобы снять шифрование с кошелька",
|
||||||
|
"tt_lite_encrypt": "Зашифровать кошелёк паролем выше; он блокируется сразу же и требует пароль для разблокировки",
|
||||||
|
"tt_lite_encrypt_pass": "Пароль для шифрования кошелька. При утрате кошелёк невозможно разблокировать или восстановить",
|
||||||
|
"tt_lite_hide_wipe": "Скрыть показанный секрет и безопасно стереть его из памяти",
|
||||||
|
"tt_lite_import_key": "Вставьте приватный ключ расходования или просмотра для импорта; его история появится после следующей синхронизации",
|
||||||
|
"tt_lite_import_key_btn": "Импортировать введённый приватный ключ в этот кошелёк; средства и история появятся после следующей синхронизации",
|
||||||
|
"tt_lite_lifecycle_op": "Выберите, создать новый кошелёк, открыть существующий или восстановить его из seed-фразы",
|
||||||
|
"tt_lite_lifecycle_pass": "Пароль для разблокировки или установки на кошелёк во время этой операции создания / открытия / восстановления",
|
||||||
|
"tt_lite_lifecycle_run": "Выполнить выбранную операцию создания / открытия / восстановления со значениями выше",
|
||||||
|
"tt_lite_lifecycle_toggle": "Показать или скрыть элементы управления создания / открытия / восстановления для управления файлом лёгкого кошелька",
|
||||||
|
"tt_lite_lock": "Заблокировать кошелёк сейчас; для разблокировки потребуется пароль, а любая сессия чата будет прервана",
|
||||||
"tt_lite_redownload": "Заново загрузить и пересканировать все блоки с лёгкого сервера",
|
"tt_lite_redownload": "Заново загрузить и пересканировать все блоки с лёгкого сервера",
|
||||||
|
"tt_lite_remove_encrypt": "Снять шифрование и хранить кошелёк без защиты; пароль для его открытия не потребуется",
|
||||||
|
"tt_lite_restore_account": "Индекс HD-аккаунта для восстановления; оставьте 0, если только вы не использовали несколько аккаунтов с этой seed-фразой",
|
||||||
|
"tt_lite_restore_birthday": "Высота блока, на которой был создан кошелёк; отсюда начинается сканирование. Если не уверены, используйте 0 или самую раннюю высоту",
|
||||||
|
"tt_lite_restore_overwrite": "Заменить существующий файл кошелька этим восстановлением. Внимание: перезаписывает текущие данные кошелька",
|
||||||
|
"tt_lite_restore_seed": "24-word seed-фраза для восстановления этого кошелька; скрывается по мере ввода",
|
||||||
|
"tt_lite_save_seed_file": "Записать seed-фразу и дату создания в файл, доступный только владельцу (lite-seed-backup.txt), в папке конфигурации",
|
||||||
|
"tt_lite_show_keys": "Показать приватные ключи расходования этого кошелька. Любой, у кого есть ключ, может потратить контролируемые им средства",
|
||||||
|
"tt_lite_show_seed": "Показать seed-фразу восстановления и дату создания этого кошелька. Любой, у кого есть seed-фраза, может потратить ваши средства",
|
||||||
|
"tt_lite_unlock": "Разблокировать зашифрованный кошелёк с помощью пароля выше",
|
||||||
|
"tt_lite_unlock_pass": "Введите пароль для разблокировки зашифрованного кошелька",
|
||||||
|
"tt_lite_wallet_path": "Путь или имя файла кошелька для открытия или восстановления",
|
||||||
"tt_lock": "Немедленно заблокировать кошелёк",
|
"tt_lock": "Немедленно заблокировать кошелёк",
|
||||||
"tt_low_spec": "Отключить все тяжёлые визуальные эффекты\\nГорячая клавиша: Ctrl+Shift+Down",
|
"tt_low_spec": "Отключить все тяжёлые визуальные эффекты\\nГорячая клавиша: Ctrl+Shift+Down",
|
||||||
"tt_merge": "Объединить несколько UTXO в один адрес",
|
"tt_merge": "Объединить несколько UTXO в один адрес",
|
||||||
@@ -1503,12 +1535,17 @@
|
|||||||
"tt_rpc_host": "Имя хоста демона DragonX",
|
"tt_rpc_host": "Имя хоста демона DragonX",
|
||||||
"tt_rpc_pass": "Пароль аутентификации RPC",
|
"tt_rpc_pass": "Пароль аутентификации RPC",
|
||||||
"tt_rpc_port": "Порт для RPC-подключений демона",
|
"tt_rpc_port": "Порт для RPC-подключений демона",
|
||||||
|
"tt_rpc_toggle": "Показать или скрыть параметры RPC-подключения только для чтения (хост, порт, пользователь, пароль) для демона",
|
||||||
"tt_rpc_user": "Имя пользователя аутентификации RPC",
|
"tt_rpc_user": "Имя пользователя аутентификации RPC",
|
||||||
"tt_save_settings": "Сохранить все настройки на диск",
|
"tt_save_settings": "Сохранить все настройки на диск",
|
||||||
"tt_save_ztx": "Хранить историю транзакций z-адреса локально для более быстрой загрузки",
|
"tt_save_ztx": "Хранить историю транзакций z-адреса локально для более быстрой загрузки",
|
||||||
"tt_scan_themes": "Поиск новых тем.\\nРазместите папки тем в:\\n%s",
|
"tt_scan_themes": "Поиск новых тем.\\nРазместите папки тем в:\\n%s",
|
||||||
"tt_scanline": "Эффект развёртки ЭЛТ в консоли",
|
"tt_scanline": "Эффект развёртки ЭЛТ в консоли",
|
||||||
|
"tt_screenshot_open_dir": "Открыть папку скриншотов (в каталоге конфигурации) в вашем файловом менеджере",
|
||||||
|
"tt_screenshot_sweep": "Перебрать каждую тему по всем вкладкам, сохраняя скриншот каждой в папку скриншотов конфигурации (перезаписывает предыдущий проход)",
|
||||||
|
"tt_screenshot_sweep_full": "Как проход по темам, но также захватывает каждое модальное окно / диалог / поток, используя временные офлайн-данные демонстрационного кошелька",
|
||||||
"tt_seed_backup": "Показать и создать резервную копию сид-фразы восстановления вашего кошелька из 24 слов",
|
"tt_seed_backup": "Показать и создать резервную копию сид-фразы восстановления вашего кошелька из 24 слов",
|
||||||
|
"tt_seed_demo_chat": "Добавить примеры переписок во вкладку «Чат», чтобы проход захватил её интерфейс; только в памяти, исчезает при перезапуске",
|
||||||
"tt_seed_migrate": "Создать новый кошелёк с сид-фразой и перевести в него ваши средства",
|
"tt_seed_migrate": "Создать новый кошелёк с сид-фразой и перевести в него ваши средства",
|
||||||
"tt_set_pin": "Установить 4-8-значный PIN для быстрой разблокировки",
|
"tt_set_pin": "Установить 4-8-значный PIN для быстрой разблокировки",
|
||||||
"tt_shield_mining": "Перевести прозрачные вознаграждения за майнинг на экранированный адрес",
|
"tt_shield_mining": "Перевести прозрачные вознаграждения за майнинг на экранированный адрес",
|
||||||
|
|||||||
@@ -1460,11 +1460,20 @@
|
|||||||
"tt_blur": "模糊程度(0%% = 关闭,100%% = 最大)",
|
"tt_blur": "模糊程度(0%% = 关闭,100%% = 最大)",
|
||||||
"tt_change_pass": "更改钱包加密密码",
|
"tt_change_pass": "更改钱包加密密码",
|
||||||
"tt_change_pin": "更改您的解锁 PIN",
|
"tt_change_pin": "更改您的解锁 PIN",
|
||||||
|
"tt_chat_bubble_accent": "你发出的消息气泡的强调色(或跟随当前主题)",
|
||||||
|
"tt_chat_bubble_style": "消息气泡形状:圆角、方形或极简(扁平、无边框)",
|
||||||
|
"tt_chat_density": "消息之间的间距:宽松增加更多留白;紧凑在屏幕上容纳更多内容",
|
||||||
|
"tt_chat_emoji_style": "以单色轮廓或全彩渲染表情符号",
|
||||||
|
"tt_chat_enter_sends": "开启时,Enter 发送消息,Shift+Enter 换行;关闭时,Enter 换行",
|
||||||
|
"tt_chat_font_size": "将聊天消息文字从 0.8x 缩放到 1.5x。仅影响聊天标签页,不影响应用的其余部分",
|
||||||
|
"tt_chat_poll_rate": "检查新消息和 0-conf 消息的频率(0.5-15 s)。越快响应越及时,但占用更多 CPU",
|
||||||
|
"tt_chat_timestamp": "仅此标签页的时间戳格式:跟随全应用时钟,或强制使用 24-hour 或 12-hour",
|
||||||
"tt_clear_ztx": "删除本地缓存的 z-交易历史",
|
"tt_clear_ztx": "删除本地缓存的 z-交易历史",
|
||||||
"tt_clock_format": "24 或 12 小时制,应用全局。聊天可覆盖。",
|
"tt_clock_format": "24 或 12 小时制,应用全局。聊天可覆盖。",
|
||||||
"tt_custom_fees": "发送交易时启用手动费用输入",
|
"tt_custom_fees": "发送交易时启用手动费用输入",
|
||||||
"tt_custom_theme": "自定义主题已激活",
|
"tt_custom_theme": "自定义主题已激活",
|
||||||
"tt_daemon_install_bundled": "停止节点,用此钱包版本内置的 dragonxd 覆盖已安装的版本,然后重启",
|
"tt_daemon_install_bundled": "停止节点,用此钱包版本内置的 dragonxd 覆盖已安装的版本,然后重启",
|
||||||
|
"tt_daemon_refresh": "重新读取上方显示的已安装及内置 dragonxd 版本、大小和日期",
|
||||||
"tt_daemon_update_check": "从项目 Gitea 下载并验证最新的 dragonxd 全节点,然后重启以应用",
|
"tt_daemon_update_check": "从项目 Gitea 下载并验证最新的 dragonxd 全节点,然后重启以应用",
|
||||||
"tt_debug_collapse": "折叠调试日志选项",
|
"tt_debug_collapse": "折叠调试日志选项",
|
||||||
"tt_debug_expand": "展开调试日志选项",
|
"tt_debug_expand": "展开调试日志选项",
|
||||||
@@ -1482,7 +1491,30 @@
|
|||||||
"tt_keep_daemon": "运行设置向导时守护进程仍会停止",
|
"tt_keep_daemon": "运行设置向导时守护进程仍会停止",
|
||||||
"tt_language": "钱包界面语言",
|
"tt_language": "钱包界面语言",
|
||||||
"tt_layout_hotkey": "快捷键:左/右箭头键切换余额布局",
|
"tt_layout_hotkey": "快捷键:左/右箭头键切换余额布局",
|
||||||
|
"tt_lite_copy": "将显示的机密复制到剪贴板",
|
||||||
|
"tt_lite_decrypt_pass": "输入你的密码以移除钱包的加密",
|
||||||
|
"tt_lite_encrypt": "用上方的密码加密钱包;加密后立即锁定,需要该密码才能解锁",
|
||||||
|
"tt_lite_encrypt_pass": "用于加密钱包的密码。若丢失,钱包将无法解锁或恢复",
|
||||||
|
"tt_lite_hide_wipe": "隐藏显示的机密并将其从内存中安全擦除",
|
||||||
|
"tt_lite_import_key": "粘贴要导入的私有花费或查看密钥;其历史记录会在下次同步后出现",
|
||||||
|
"tt_lite_import_key_btn": "将输入的私钥导入此钱包;资金和历史记录会在下次同步后出现",
|
||||||
|
"tt_lite_lifecycle_op": "选择是创建新钱包、打开现有钱包,还是从助记词恢复钱包",
|
||||||
|
"tt_lite_lifecycle_pass": "在此次创建 / 打开 / 恢复操作中用于解锁或设置钱包的密码",
|
||||||
|
"tt_lite_lifecycle_run": "使用上方的值执行所选的创建 / 打开 / 恢复操作",
|
||||||
|
"tt_lite_lifecycle_toggle": "显示或隐藏用于管理轻钱包文件的创建 / 打开 / 恢复控件",
|
||||||
|
"tt_lite_lock": "立即锁定钱包;解锁需要密码,任何聊天会话都会被中断",
|
||||||
"tt_lite_redownload": "从轻钱包服务器重新下载并重新扫描所有区块",
|
"tt_lite_redownload": "从轻钱包服务器重新下载并重新扫描所有区块",
|
||||||
|
"tt_lite_remove_encrypt": "移除加密并以未受保护的方式存储钱包;打开它将不再需要密码",
|
||||||
|
"tt_lite_restore_account": "要恢复的 HD 账户索引;除非你在此助记词下使用了多个账户,否则保持为 0",
|
||||||
|
"tt_lite_restore_birthday": "钱包创建时的区块高度;扫描从此处开始。不确定时请填 0 或最早的高度",
|
||||||
|
"tt_lite_restore_overwrite": "用此次恢复替换现有的钱包文件。警告:这会覆盖当前的钱包数据",
|
||||||
|
"tt_lite_restore_seed": "用于恢复此钱包的 24-word 助记词恢复短语;输入时会隐藏",
|
||||||
|
"tt_lite_save_seed_file": "将助记词和创建高度写入配置文件夹中一个仅所有者可读的文件(lite-seed-backup.txt)",
|
||||||
|
"tt_lite_show_keys": "显示此钱包的私有花费密钥。任何拥有密钥的人都能动用它所控制的资金",
|
||||||
|
"tt_lite_show_seed": "显示此钱包的助记词恢复短语和创建高度。任何拥有助记词的人都能动用你的资金",
|
||||||
|
"tt_lite_unlock": "使用上方的密码解锁已加密的钱包",
|
||||||
|
"tt_lite_unlock_pass": "输入你的密码以解锁已加密的钱包",
|
||||||
|
"tt_lite_wallet_path": "要打开或恢复到的钱包文件路径或名称",
|
||||||
"tt_lock": "立即锁定钱包",
|
"tt_lock": "立即锁定钱包",
|
||||||
"tt_low_spec": "禁用所有重度视觉效果\\n快捷键:Ctrl+Shift+Down",
|
"tt_low_spec": "禁用所有重度视觉效果\\n快捷键:Ctrl+Shift+Down",
|
||||||
"tt_merge": "将多个 UTXO 合并到一个地址",
|
"tt_merge": "将多个 UTXO 合并到一个地址",
|
||||||
@@ -1503,12 +1535,17 @@
|
|||||||
"tt_rpc_host": "DragonX 守护进程主机名",
|
"tt_rpc_host": "DragonX 守护进程主机名",
|
||||||
"tt_rpc_pass": "RPC 认证密码",
|
"tt_rpc_pass": "RPC 认证密码",
|
||||||
"tt_rpc_port": "守护进程 RPC 连接端口",
|
"tt_rpc_port": "守护进程 RPC 连接端口",
|
||||||
|
"tt_rpc_toggle": "显示或隐藏守护进程的只读 RPC 连接详情(主机、端口、用户、密码)",
|
||||||
"tt_rpc_user": "RPC 认证用户名",
|
"tt_rpc_user": "RPC 认证用户名",
|
||||||
"tt_save_settings": "将所有设置保存到磁盘",
|
"tt_save_settings": "将所有设置保存到磁盘",
|
||||||
"tt_save_ztx": "将 z-address 交易历史存储在本地以加快加载速度",
|
"tt_save_ztx": "将 z-address 交易历史存储在本地以加快加载速度",
|
||||||
"tt_scan_themes": "扫描新主题。\\n将主题文件夹放在:\\n%s",
|
"tt_scan_themes": "扫描新主题。\\n将主题文件夹放在:\\n%s",
|
||||||
"tt_scanline": "控制台中的 CRT 扫描线效果",
|
"tt_scanline": "控制台中的 CRT 扫描线效果",
|
||||||
|
"tt_screenshot_open_dir": "在你的文件管理器中打开截图文件夹(位于配置目录下)",
|
||||||
|
"tt_screenshot_sweep": "在每个标签页遍历每种主题,将每种主题的截图保存到配置文件夹的 screenshots 目录(覆盖上一次遍历)",
|
||||||
|
"tt_screenshot_sweep_full": "与主题遍历类似,但同时捕获每个使用临时离线演示钱包数据的模态框 / 对话框 / 流程",
|
||||||
"tt_seed_backup": "显示并备份您钱包的 24 词恢复助记词",
|
"tt_seed_backup": "显示并备份您钱包的 24 词恢复助记词",
|
||||||
|
"tt_seed_demo_chat": "向聊天标签页注入示例对话,以便遍历能捕获其界面;仅在内存中,重启后消失",
|
||||||
"tt_seed_migrate": "创建一个新的助记词钱包并将您的资金转入其中",
|
"tt_seed_migrate": "创建一个新的助记词钱包并将您的资金转入其中",
|
||||||
"tt_set_pin": "设置 4-8 位 PIN 以快速解锁",
|
"tt_set_pin": "设置 4-8 位 PIN 以快速解锁",
|
||||||
"tt_shield_mining": "将透明挖矿奖励转移到屏蔽地址",
|
"tt_shield_mining": "将透明挖矿奖励转移到屏蔽地址",
|
||||||
|
|||||||
@@ -67,25 +67,9 @@ Options:
|
|||||||
--backend-dir PATH SilentDragonXLite/lib source directory.
|
--backend-dir PATH SilentDragonXLite/lib source directory.
|
||||||
--silentdragonxlitelib-dir PATH Override the wrapper's silentdragonxlitelib dependency path.
|
--silentdragonxlitelib-dir PATH Override the wrapper's silentdragonxlitelib dependency path.
|
||||||
--out-dir PATH Output directory for copied artifact and metadata.
|
--out-dir PATH Output directory for copied artifact and metadata.
|
||||||
--artifact PATH Inventory an existing artifact instead of building.
|
|
||||||
--no-build Do not run cargo; requires --artifact.
|
|
||||||
--reproducible Add deterministic Rust path remaps for clean builds.
|
--reproducible Add deterministic Rust path remaps for clean builds.
|
||||||
--remap-path-prefix FROM=TO Extra rustc path remap used with --reproducible.
|
--remap-path-prefix FROM=TO Extra rustc path remap used with --reproducible.
|
||||||
--builder NAME Redacted builder/provenance label. Default: local.
|
--builder NAME Redacted builder/provenance label. Default: local.
|
||||||
--signature-required Fail if verified signature metadata is not supplied.
|
|
||||||
--signature-file PATH Existing sidecar signature file to record.
|
|
||||||
--signature-format FORMAT Signature format: minisign, gpg, sigstore, external, or other.
|
|
||||||
--signature-verification-tool T Verification tool and version used by the release builder.
|
|
||||||
--signature-verification-command C
|
|
||||||
Verification command already run by the release builder.
|
|
||||||
--signature-key-fingerprint F Reviewed public-key fingerprint, when applicable.
|
|
||||||
--signature-certificate-identity ID
|
|
||||||
Reviewed certificate identity, when applicable.
|
|
||||||
--signature-certificate-issuer I
|
|
||||||
Reviewed certificate issuer, when applicable.
|
|
||||||
--signature-transparency-log-url URL
|
|
||||||
Transparency log entry, when applicable.
|
|
||||||
--signature-verified-sha256 SHA Artifact SHA-256 verified by the signature check.
|
|
||||||
-j, --jobs N Cargo parallel jobs.
|
-j, --jobs N Cargo parallel jobs.
|
||||||
--cargo-arg ARG Extra argument forwarded to cargo build.
|
--cargo-arg ARG Extra argument forwarded to cargo build.
|
||||||
-h, --help Show this help.
|
-h, --help Show this help.
|
||||||
@@ -95,9 +79,13 @@ Outputs:
|
|||||||
<out>/<platform>/lite-backend-symbols.txt
|
<out>/<platform>/lite-backend-symbols.txt
|
||||||
<out>/<platform>/lite-backend-artifact-manifest.json
|
<out>/<platform>/lite-backend-artifact-manifest.json
|
||||||
|
|
||||||
The script captures symbols, checksums, and optional read-only signature
|
The lite backend is always built from the vendored in-tree source
|
||||||
verification metadata only. It does not load the library, resolve function
|
(third_party/silentdragonxlite), which is the trust root. Prebuilt artifacts
|
||||||
pointers, call SDXL, sign, upload, or publish artifacts.
|
and self-attested signature metadata are NOT accepted (F15-1) — the previous
|
||||||
|
scheme only recorded an unverified "verified" claim. The script captures the
|
||||||
|
freshly-built artifact's symbols and checksum, and records build provenance.
|
||||||
|
It does not load the library, resolve function pointers, call SDXL, sign,
|
||||||
|
upload, or publish artifacts.
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,15 +154,8 @@ while [[ $# -gt 0 ]]; do
|
|||||||
OUT_DIR="$(absolute_path "$2")"
|
OUT_DIR="$(absolute_path "$2")"
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
--artifact)
|
--artifact|--no-build)
|
||||||
[[ $# -ge 2 ]] || die "--artifact requires a value"
|
die "$1 was removed (F15-1): the lite backend must be built from the vendored in-tree source (third_party/silentdragonxlite); prebuilt artifacts are no longer accepted."
|
||||||
ARTIFACT_PATH="$(absolute_path "$2")"
|
|
||||||
BUILD_ARTIFACT=false
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--no-build)
|
|
||||||
BUILD_ARTIFACT=false
|
|
||||||
shift
|
|
||||||
;;
|
;;
|
||||||
--reproducible)
|
--reproducible)
|
||||||
REPRODUCIBLE=true
|
REPRODUCIBLE=true
|
||||||
@@ -191,54 +172,11 @@ while [[ $# -gt 0 ]]; do
|
|||||||
BUILDER="$2"
|
BUILDER="$2"
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
--signature-required)
|
--signature-required|--signature-file|--signature-path|--signature-format|\
|
||||||
SIGNATURE_REQUIRED=true
|
--signature-verification-tool|--signature-tool|--signature-verification-command|\
|
||||||
shift
|
--signature-key-fingerprint|--signature-certificate-identity|--signature-certificate-issuer|\
|
||||||
;;
|
--signature-transparency-log-url|--signature-verified-sha256)
|
||||||
--signature-file|--signature-path)
|
die "signature-attestation flags were removed (F15-1): they recorded a self-attested \"verified\" claim without running any cryptographic verifier. The lite backend is built from the vendored in-tree source, which is the trust root."
|
||||||
[[ $# -ge 2 ]] || die "$1 requires a value"
|
|
||||||
SIGNATURE_FILE="$(absolute_path "$2")"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--signature-format)
|
|
||||||
[[ $# -ge 2 ]] || die "--signature-format requires a value"
|
|
||||||
SIGNATURE_FORMAT="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--signature-verification-tool|--signature-tool)
|
|
||||||
[[ $# -ge 2 ]] || die "$1 requires a value"
|
|
||||||
SIGNATURE_VERIFICATION_TOOL="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--signature-verification-command)
|
|
||||||
[[ $# -ge 2 ]] || die "--signature-verification-command requires a value"
|
|
||||||
SIGNATURE_VERIFICATION_COMMAND="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--signature-key-fingerprint)
|
|
||||||
[[ $# -ge 2 ]] || die "--signature-key-fingerprint requires a value"
|
|
||||||
SIGNATURE_KEY_FINGERPRINT="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--signature-certificate-identity)
|
|
||||||
[[ $# -ge 2 ]] || die "--signature-certificate-identity requires a value"
|
|
||||||
SIGNATURE_CERTIFICATE_IDENTITY="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--signature-certificate-issuer)
|
|
||||||
[[ $# -ge 2 ]] || die "--signature-certificate-issuer requires a value"
|
|
||||||
SIGNATURE_CERTIFICATE_ISSUER="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--signature-transparency-log-url)
|
|
||||||
[[ $# -ge 2 ]] || die "--signature-transparency-log-url requires a value"
|
|
||||||
SIGNATURE_TRANSPARENCY_LOG_URL="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--signature-verified-sha256)
|
|
||||||
[[ $# -ge 2 ]] || die "--signature-verified-sha256 requires a value"
|
|
||||||
SIGNATURE_VERIFIED_SHA256="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
;;
|
||||||
-j|--jobs)
|
-j|--jobs)
|
||||||
[[ $# -ge 2 ]] || die "--jobs requires a value"
|
[[ $# -ge 2 ]] || die "--jobs requires a value"
|
||||||
@@ -766,6 +704,7 @@ MANIFEST_FILE="$PLATFORM_OUT_DIR/lite-backend-artifact-manifest.json"
|
|||||||
printf ' },\n'
|
printf ' },\n'
|
||||||
printf ' "provenance": {\n'
|
printf ' "provenance": {\n'
|
||||||
printf ' "owner_ready": true,\n'
|
printf ' "owner_ready": true,\n'
|
||||||
|
printf ' "built_from_source": true,\n'
|
||||||
printf ' "metadata_provided": true,\n'
|
printf ' "metadata_provided": true,\n'
|
||||||
printf ' "source": '; json_escape "$BACKEND_SOURCE_DIR"; printf ',\n'
|
printf ' "source": '; json_escape "$BACKEND_SOURCE_DIR"; printf ',\n'
|
||||||
printf ' "cargo_build_source": '; json_escape "$BUILD_BACKEND_DIR"; printf ',\n'
|
printf ' "cargo_build_source": '; json_escape "$BUILD_BACKEND_DIR"; printf ',\n'
|
||||||
|
|||||||
@@ -24,18 +24,27 @@ if [ ! -f "${BUILD_DIR}/bin/ObsidianDragon" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check for appimagetool
|
# Check for appimagetool — pinned to a tagged release and SHA-256 verified before we exec it.
|
||||||
|
# The old "continuous" tag is a moving, unverified network download that runs on the release
|
||||||
|
# builder; verify it or refuse to package.
|
||||||
|
APPIMAGETOOL_URL="https://github.com/AppImage/appimagetool/releases/download/1.9.0/appimagetool-x86_64.AppImage"
|
||||||
|
APPIMAGETOOL_SHA256="46fdd785094c7f6e545b61afcfb0f3d98d8eab243f644b4b17698c01d06083d1"
|
||||||
APPIMAGETOOL=""
|
APPIMAGETOOL=""
|
||||||
if command -v appimagetool &> /dev/null; then
|
if command -v appimagetool &> /dev/null; then
|
||||||
APPIMAGETOOL="appimagetool"
|
APPIMAGETOOL="appimagetool" # maintainer's own trusted system install
|
||||||
elif [ -f "${BUILD_DIR}/appimagetool-x86_64.AppImage" ]; then
|
|
||||||
APPIMAGETOOL="${BUILD_DIR}/appimagetool-x86_64.AppImage"
|
|
||||||
else
|
else
|
||||||
print_status "Downloading appimagetool..."
|
AT="${BUILD_DIR}/appimagetool-x86_64.AppImage"
|
||||||
wget -q -O "${BUILD_DIR}/appimagetool-x86_64.AppImage" \
|
if [ ! -f "$AT" ] || ! echo "${APPIMAGETOOL_SHA256} ${AT}" | sha256sum -c --status; then
|
||||||
"https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
|
print_status "Downloading appimagetool 1.9.0 (pinned)..."
|
||||||
chmod +x "${BUILD_DIR}/appimagetool-x86_64.AppImage"
|
wget -q -O "$AT" "$APPIMAGETOOL_URL"
|
||||||
APPIMAGETOOL="${BUILD_DIR}/appimagetool-x86_64.AppImage"
|
if ! echo "${APPIMAGETOOL_SHA256} ${AT}" | sha256sum -c --status; then
|
||||||
|
print_error "appimagetool SHA-256 verification failed — refusing to use it"
|
||||||
|
rm -f "$AT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
chmod +x "$AT"
|
||||||
|
fi
|
||||||
|
APPIMAGETOOL="$AT"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
print_status "Creating AppDir structure..."
|
print_status "Creating AppDir structure..."
|
||||||
|
|||||||
23
setup.sh
23
setup.sh
@@ -391,11 +391,26 @@ elif $SETUP_SAPLING; then
|
|||||||
|
|
||||||
SPEND_URL="https://z.cash/downloads/sapling-spend.params"
|
SPEND_URL="https://z.cash/downloads/sapling-spend.params"
|
||||||
OUTPUT_URL="https://z.cash/downloads/sapling-output.params"
|
OUTPUT_URL="https://z.cash/downloads/sapling-output.params"
|
||||||
|
# Consensus-critical MPC parameters with fixed, well-known SHA-256 (identical across every
|
||||||
|
# Zcash-family node; also pinned in scripts/build-lite-backend-artifact.sh). z.cash is
|
||||||
|
# plain HTTPS with no signature, so verify the digest and refuse a tampered/corrupt file.
|
||||||
|
SPEND_SHA256="8e48ffd23abb3a5fd9c5589204f32d9c31285a04b78096ba40a79b75677efc13"
|
||||||
|
OUTPUT_SHA256="2f0ebbcbb9bb0bcffe95a397e7eba89c29eb4dde6191c339db88570e3f3fb0e4"
|
||||||
|
|
||||||
curl -fSL -o "$PARAMS_DIR/sapling-spend.params" "$SPEND_URL" && \
|
if curl -fSL -o "$PARAMS_DIR/sapling-spend.params" "$SPEND_URL" \
|
||||||
ok "Downloaded sapling-spend.params"
|
&& echo "${SPEND_SHA256} $PARAMS_DIR/sapling-spend.params" | sha256sum -c --status; then
|
||||||
curl -fSL -o "$PARAMS_DIR/sapling-output.params" "$OUTPUT_URL" && \
|
ok "Downloaded + verified sapling-spend.params"
|
||||||
ok "Downloaded sapling-output.params"
|
else
|
||||||
|
rm -f "$PARAMS_DIR/sapling-spend.params"
|
||||||
|
err "sapling-spend.params download or SHA-256 verification failed — not installed"
|
||||||
|
fi
|
||||||
|
if curl -fSL -o "$PARAMS_DIR/sapling-output.params" "$OUTPUT_URL" \
|
||||||
|
&& echo "${OUTPUT_SHA256} $PARAMS_DIR/sapling-output.params" | sha256sum -c --status; then
|
||||||
|
ok "Downloaded + verified sapling-output.params"
|
||||||
|
else
|
||||||
|
rm -f "$PARAMS_DIR/sapling-output.params"
|
||||||
|
err "sapling-output.params download or SHA-256 verification failed — not installed"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
skip "Sapling params not found (use --sapling to download, or they'll be extracted at runtime from embedded builds)"
|
skip "Sapling params not found (use --sapling to download, or they'll be extracted at runtime from embedded builds)"
|
||||||
|
|||||||
17
src/app.cpp
17
src/app.cpp
@@ -5320,6 +5320,11 @@ void App::renderLoadingOverlay(float contentH)
|
|||||||
|
|
||||||
void App::shutdown()
|
void App::shutdown()
|
||||||
{
|
{
|
||||||
|
// Wipe any copied secret from the OS clipboard before we exit — the 45s auto-clear timer
|
||||||
|
// never fires if the user quits sooner, which would otherwise leave a key/seed resident.
|
||||||
|
// (ImGui context is still alive here; App::shutdown() runs before ImGui::DestroyContext().)
|
||||||
|
clearSecretClipboardIfArmed();
|
||||||
|
|
||||||
// Clean up bootstrap if running
|
// Clean up bootstrap if running
|
||||||
if (bootstrap_) {
|
if (bootstrap_) {
|
||||||
bootstrap_->cancel();
|
bootstrap_->cancel();
|
||||||
@@ -5522,10 +5527,9 @@ void App::copySecretToClipboard(const std::string& secret)
|
|||||||
ui::Notifications::instance().info("Copied — clipboard auto-clears in 45s", 4.0f);
|
ui::Notifications::instance().info("Copied — clipboard auto-clears in 45s", 4.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::pumpSecretClipboardClear()
|
void App::clearSecretClipboardIfArmed()
|
||||||
{
|
{
|
||||||
if (clipboard_clear_deadline_ <= 0.0) return;
|
if (clipboard_secret_hash_ == 0) return;
|
||||||
if (ImGui::GetTime() < clipboard_clear_deadline_) return;
|
|
||||||
// Only clear if the clipboard STILL holds our secret (the user may have copied something else).
|
// Only clear if the clipboard STILL holds our secret (the user may have copied something else).
|
||||||
if (const char* cb = ImGui::GetClipboardText()) {
|
if (const char* cb = ImGui::GetClipboardText()) {
|
||||||
std::uint64_t h = 1469598103934665603ULL;
|
std::uint64_t h = 1469598103934665603ULL;
|
||||||
@@ -5536,6 +5540,13 @@ void App::pumpSecretClipboardClear()
|
|||||||
clipboard_secret_hash_ = 0;
|
clipboard_secret_hash_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void App::pumpSecretClipboardClear()
|
||||||
|
{
|
||||||
|
if (clipboard_clear_deadline_ <= 0.0) return;
|
||||||
|
if (ImGui::GetTime() < clipboard_clear_deadline_) return;
|
||||||
|
clearSecretClipboardIfArmed();
|
||||||
|
}
|
||||||
|
|
||||||
void App::maybeFinishTransactionSendProgress()
|
void App::maybeFinishTransactionSendProgress()
|
||||||
{
|
{
|
||||||
using Job = services::NetworkRefreshService::Job;
|
using Job = services::NetworkRefreshService::Job;
|
||||||
|
|||||||
@@ -573,6 +573,9 @@ public:
|
|||||||
// plaintext. Call pumpSecretClipboardClear() each frame to action the clear.
|
// plaintext. Call pumpSecretClipboardClear() each frame to action the clear.
|
||||||
void copySecretToClipboard(const std::string& secret);
|
void copySecretToClipboard(const std::string& secret);
|
||||||
void pumpSecretClipboardClear();
|
void pumpSecretClipboardClear();
|
||||||
|
// Immediately clear the clipboard if it still holds the armed secret (ignores the 45s timer).
|
||||||
|
// Called on app shutdown so a copied key/seed does not outlive the process in the OS clipboard.
|
||||||
|
void clearSecretClipboardIfArmed();
|
||||||
bool isTransactionRefreshInProgress() const {
|
bool isTransactionRefreshInProgress() const {
|
||||||
return network_refresh_.jobInProgress(services::NetworkRefreshService::Job::Transactions);
|
return network_refresh_.jobInProgress(services::NetworkRefreshService::Job::Transactions);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,6 +228,17 @@ bool ChatDatabase::ensureOpen()
|
|||||||
exec("PRAGMA journal_mode=WAL");
|
exec("PRAGMA journal_mode=WAL");
|
||||||
exec("PRAGMA synchronous=NORMAL");
|
exec("PRAGMA synchronous=NORMAL");
|
||||||
|
|
||||||
|
// C3-1: restrict the chat DB and its WAL/SHM sidecars to owner-only. sqlite creates them with
|
||||||
|
// umask-derived permissions (often world/group-readable); they hold per-row nonces + AEAD
|
||||||
|
// ciphertext of the user's messages. Best-effort (errors swallowed; a no-op-ish on Windows).
|
||||||
|
{
|
||||||
|
std::error_code perr;
|
||||||
|
const auto ownerOnly = std::filesystem::perms::owner_read | std::filesystem::perms::owner_write;
|
||||||
|
std::filesystem::permissions(database_path_, ownerOnly, std::filesystem::perm_options::replace, perr);
|
||||||
|
std::filesystem::permissions(database_path_ + "-wal", ownerOnly, std::filesystem::perm_options::replace, perr);
|
||||||
|
std::filesystem::permissions(database_path_ + "-shm", ownerOnly, std::filesystem::perm_options::replace, perr);
|
||||||
|
}
|
||||||
|
|
||||||
if (!createSchema()) {
|
if (!createSchema()) {
|
||||||
close();
|
close();
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -136,6 +136,14 @@ SeedWalletResult SeedWalletCreator::create(bool keepDatadir,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// W1-2: never hand back a live seed on a failure path. If the mnemonic was exported but a
|
||||||
|
// later step failed (empty address, or z_getnewaddress threw), the caller discards this
|
||||||
|
// result without wiping it, which would leave the seed resident. Success keeps it deliberately.
|
||||||
|
if (!r.ok && !r.seedPhrase.empty()) {
|
||||||
|
sodium_memzero(&r.seedPhrase[0], r.seedPhrase.size());
|
||||||
|
r.seedPhrase.clear();
|
||||||
|
}
|
||||||
|
|
||||||
// 7. Stop the isolated node (graceful; it flushes its tiny empty chain quickly).
|
// 7. Stop the isolated node (graceful; it flushes its tiny empty chain quickly).
|
||||||
cli.disconnect();
|
cli.disconnect();
|
||||||
temp.stop(20000);
|
temp.stop(20000);
|
||||||
|
|||||||
@@ -208,19 +208,20 @@ bool XmrigManager::generateConfig(const Config& cfg, const std::string& outPath)
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
fs::create_directories(fs::path(outPath).parent_path());
|
fs::create_directories(fs::path(outPath).parent_path());
|
||||||
std::ofstream ofs(outPath);
|
std::ofstream ofs(outPath, std::ios::trunc);
|
||||||
if (!ofs.is_open()) {
|
if (!ofs.is_open()) {
|
||||||
last_error_ = "Cannot write xmrig config: " + outPath;
|
last_error_ = "Cannot write xmrig config: " + outPath;
|
||||||
DEBUG_LOGF("[ERROR] XmrigManager: %s\n", last_error_.c_str());
|
DEBUG_LOGF("[ERROR] XmrigManager: %s\n", last_error_.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ofs << j.dump(4);
|
|
||||||
ofs.close();
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
// 0600 permissions — only owner can read/write
|
// Restrict to owner (0600) BEFORE writing any secret material (API token, wallet
|
||||||
|
// address, worker name). The file is still empty here, so the config is never
|
||||||
|
// world-readable — closing the window between creation and the previous post-write chmod.
|
||||||
chmod(outPath.c_str(), 0600);
|
chmod(outPath.c_str(), 0600);
|
||||||
#endif
|
#endif
|
||||||
|
ofs << j.dump(4);
|
||||||
|
ofs.close();
|
||||||
return true;
|
return true;
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
last_error_ = std::string("Config write error: ") + e.what();
|
last_error_ = std::string("Config write error: ") + e.what();
|
||||||
|
|||||||
@@ -25,9 +25,11 @@ namespace {
|
|||||||
|
|
||||||
// Recursively zero every string value in a JSON tree in place — used to wipe a discarded parse tree
|
// Recursively zero every string value in a JSON tree in place — used to wipe a discarded parse tree
|
||||||
// that held a secret (B7). Operates on the underlying std::string buffers via get_ref.
|
// that held a secret (B7). Operates on the underlying std::string buffers via get_ref.
|
||||||
void scrubJsonSecrets(nlohmann::json& j) {
|
// Templated so it works on both nlohmann::json and nlohmann::ordered_json (callRaw uses the latter).
|
||||||
|
template <typename J>
|
||||||
|
void scrubJsonSecrets(J& j) {
|
||||||
if (j.is_string()) {
|
if (j.is_string()) {
|
||||||
auto& s = j.get_ref<std::string&>();
|
auto& s = j.template get_ref<std::string&>();
|
||||||
if (!s.empty()) sodium_memzero(&s[0], s.size());
|
if (!s.empty()) sodium_memzero(&s[0], s.size());
|
||||||
} else if (j.is_object() || j.is_array()) {
|
} else if (j.is_object() || j.is_array()) {
|
||||||
for (auto& el : j) scrubJsonSecrets(el);
|
for (auto& el : j) scrubJsonSecrets(el);
|
||||||
@@ -96,6 +98,10 @@ void RPCClient::setTraceSource(std::string source)
|
|||||||
// Callback for libcurl to write response data
|
// Callback for libcurl to write response data
|
||||||
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
|
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
|
||||||
size_t totalSize = size * nmemb;
|
size_t totalSize = size * nmemb;
|
||||||
|
// Bound accumulation so a hostile/compromised daemon cannot OOM the client with an unbounded
|
||||||
|
// response body. 256 MiB is far above any legitimate JSON-RPC response yet prevents exhaustion.
|
||||||
|
static constexpr size_t kMaxRpcResponseBytes = 256u * 1024 * 1024;
|
||||||
|
if (userp->size() + totalSize > kMaxRpcResponseBytes) return 0; // short count aborts the transfer
|
||||||
userp->append((char*)contents, totalSize);
|
userp->append((char*)contents, totalSize);
|
||||||
return totalSize;
|
return totalSize;
|
||||||
}
|
}
|
||||||
@@ -202,6 +208,10 @@ bool RPCClient::connect(const std::string& host, const std::string& port,
|
|||||||
// budget for the TCP + TLS handshake over real network latency (1s would spuriously fail).
|
// budget for the TCP + TLS handshake over real network latency (1s would spuriously fail).
|
||||||
const long connectTimeout = Connection::isLocalHost(host) ? 2L : 10L;
|
const long connectTimeout = Connection::isLocalHost(host) ? 2L : 10L;
|
||||||
curl_easy_setopt(impl_->curl, CURLOPT_CONNECTTIMEOUT, connectTimeout);
|
curl_easy_setopt(impl_->curl, CURLOPT_CONNECTTIMEOUT, connectTimeout);
|
||||||
|
// Enforce TLS certificate + hostname verification explicitly rather than relying on libcurl's
|
||||||
|
// build defaults. Harmless on the localhost http:// case; essential for a remote https daemon.
|
||||||
|
curl_easy_setopt(impl_->curl, CURLOPT_SSL_VERIFYPEER, 1L);
|
||||||
|
curl_easy_setopt(impl_->curl, CURLOPT_SSL_VERIFYHOST, 2L);
|
||||||
|
|
||||||
// Test connection with getinfo. Use a SHORT timeout for the probe on localhost: a healthy
|
// Test connection with getinfo. Use a SHORT timeout for the probe on localhost: a healthy
|
||||||
// local daemon answers in milliseconds and a warming one returns -28 just as fast, so a long
|
// local daemon answers in milliseconds and a warming one returns -28 just as fast, so a long
|
||||||
@@ -508,14 +518,22 @@ std::string RPCClient::callRaw(const std::string& method, const json& params)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto& result = oj["result"];
|
auto& result = oj["result"];
|
||||||
|
std::string out;
|
||||||
if (result.is_null()) {
|
if (result.is_null()) {
|
||||||
return "null";
|
out = "null";
|
||||||
} else if (result.is_string()) {
|
} else if (result.is_string()) {
|
||||||
// Return the raw string (not JSON-encoded) — caller wraps as needed
|
// Return the raw string (not JSON-encoded) — caller wraps as needed
|
||||||
return result.get<std::string>();
|
out = result.get<std::string>();
|
||||||
} else {
|
} else {
|
||||||
return result.dump(4);
|
out = result.dump(4);
|
||||||
}
|
}
|
||||||
|
// B7: this raw path serves arbitrary console commands including dumpprivkey / z_exportkey,
|
||||||
|
// whose response carries plaintext key material. Zero the raw buffer and the parsed tree so
|
||||||
|
// the secret does not linger in freed heap (matching callSecret). The single returned copy is
|
||||||
|
// the caller's to manage.
|
||||||
|
scrubJsonSecrets(oj);
|
||||||
|
if (!response_data.empty()) sodium_memzero(&response_data[0], response_data.size());
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPCClient::doRPC(const std::string& method, const json& params, Callback cb, ErrorCallback err)
|
void RPCClient::doRPC(const std::string& method, const json& params, Callback cb, ErrorCallback err)
|
||||||
|
|||||||
@@ -57,6 +57,21 @@ inline ImU32 ReadableError() {
|
|||||||
return IM_COL32(r, g, b, (e >> IM_COL32_A_SHIFT) & 0xFF);
|
return IM_COL32(r, g, b, (e >> IM_COL32_A_SHIFT) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Middle-ellipsis truncation ("front...back", roughly equal halves) so `text` fits within
|
||||||
|
// maxWidth pixels when drawn with `font` at `fontSize`. Returns `text` unchanged if it already
|
||||||
|
// fits (or maxWidth is non-positive). Display-only — never mutate the underlying value with this.
|
||||||
|
inline std::string TruncateToWidth(const std::string& text, ImFont* font, float fontSize, float maxWidth) {
|
||||||
|
if (text.empty() || !font || maxWidth <= 0.0f) return text;
|
||||||
|
if (font->CalcTextSizeA(fontSize, FLT_MAX, 0.0f, text.c_str()).x <= maxWidth) return text;
|
||||||
|
const int n = static_cast<int>(text.size());
|
||||||
|
for (int f = n / 2; f >= 3; --f) {
|
||||||
|
const int b = (f - 2 > 3) ? (f - 2) : 3; // keep the two halves roughly equal
|
||||||
|
std::string t = text.substr(0, f) + "..." + text.substr(n - b);
|
||||||
|
if (font->CalcTextSizeA(fontSize, FLT_MAX, 0.0f, t.c_str()).x <= maxWidth) return t;
|
||||||
|
}
|
||||||
|
return n > 6 ? (text.substr(0, 3) + "..." + text.substr(n - 3)) : text;
|
||||||
|
}
|
||||||
|
|
||||||
// Animated "loading" ellipsis: "", ".", "..", "..." cycling on a ~3Hz phase.
|
// Animated "loading" ellipsis: "", ".", "..", "..." cycling on a ~3Hz phase.
|
||||||
inline const char* LoadingDots() {
|
inline const char* LoadingDots() {
|
||||||
int n = ((int)(ImGui::GetTime() * 3.0f)) % 4;
|
int n = ((int)(ImGui::GetTime() * 3.0f)) % 4;
|
||||||
|
|||||||
193
src/ui/material/settings_controls.h
Normal file
193
src/ui/material/settings_controls.h
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
// DragonX Wallet - ImGui Edition
|
||||||
|
// Copyright 2024-2026 The Hush Developers
|
||||||
|
// Released under the GPLv3
|
||||||
|
//
|
||||||
|
// Settings design-system controls — the polished chat-settings look (accent subsection headers,
|
||||||
|
// labeled rows with right-aligned controls, iOS-style segmented controls) promoted to reusable
|
||||||
|
// components, plus a tiered ActionButton (Primary/Secondary/Tertiary/Destructive) with optional
|
||||||
|
// leading Material icon and a ButtonFlow that wraps rows of buttons instead of shrinking them.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "draw_helpers.h" // colors, type, layout, icons, WithAlpha, ScaleAlpha, DrawButtonGlassOverlay
|
||||||
|
#include "imgui.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace dragonx {
|
||||||
|
namespace ui {
|
||||||
|
namespace material {
|
||||||
|
|
||||||
|
// Accent small-caps subsection header ("KEYS & BACKUP", "APPEARANCE"…) — the chat-settings section() look.
|
||||||
|
inline void SettingsSubheader(const char* text) {
|
||||||
|
const float dp = Layout::dpiScale();
|
||||||
|
ImGui::Dummy(ImVec2(0.0f, 8.0f * dp));
|
||||||
|
ImGui::PushFont(Type().caption());
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, WithAlpha(Primary(), 235));
|
||||||
|
ImGui::TextUnformatted(text);
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::PopFont();
|
||||||
|
ImGui::Dummy(ImVec2(0.0f, 2.0f * dp));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A labeled settings row: label left, control right-aligned in a fixed column. Construct once per card
|
||||||
|
// section with the content width (0 = auto), then call .label(text) before drawing each control (leaves
|
||||||
|
// the cursor at the control origin and sets the next item width to the control column).
|
||||||
|
struct SettingsRow {
|
||||||
|
float leftX, rowW, ctrlW, rowGap;
|
||||||
|
explicit SettingsRow(float contentWidth, float controlWidth = 250.0f) {
|
||||||
|
const float dp = Layout::dpiScale();
|
||||||
|
leftX = ImGui::GetCursorPosX();
|
||||||
|
rowW = (contentWidth > 0.0f) ? contentWidth : ImGui::GetContentRegionAvail().x;
|
||||||
|
ctrlW = controlWidth * dp;
|
||||||
|
rowGap = 5.0f * dp;
|
||||||
|
}
|
||||||
|
void label(const char* text) {
|
||||||
|
ImGui::Dummy(ImVec2(0.0f, rowGap));
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
ImGui::TextUnformatted(text);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), leftX + std::max(0.0f, rowW - ctrlW)));
|
||||||
|
ImGui::SetNextItemWidth(ctrlW);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// iOS-style segmented control (rounded track + inset pill on the selection). Draws its own labeled row
|
||||||
|
// and returns the (possibly changed) index.
|
||||||
|
inline int SegmentedControl(SettingsRow& row, const char* label, const char* const* items, int count, int value) {
|
||||||
|
const float dp = Layout::dpiScale();
|
||||||
|
row.label(label);
|
||||||
|
const ImVec2 origin = ImGui::GetCursorScreenPos();
|
||||||
|
const float h = ImGui::GetFrameHeight();
|
||||||
|
const float seg = row.ctrlW / static_cast<float>(count);
|
||||||
|
const float round = 7.0f * dp;
|
||||||
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||||
|
dl->AddRectFilled(origin, ImVec2(origin.x + row.ctrlW, origin.y + h), WithAlpha(OnSurface(), 20), round);
|
||||||
|
int result = value;
|
||||||
|
ImGui::PushID(label);
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
ImGui::PushID(i);
|
||||||
|
const ImVec2 mn(origin.x + i * seg, origin.y), mx(origin.x + (i + 1) * seg, origin.y + h);
|
||||||
|
ImGui::SetCursorScreenPos(mn);
|
||||||
|
if (ImGui::InvisibleButton("##s", ImVec2(seg, h))) result = i;
|
||||||
|
const bool hov = ImGui::IsItemHovered();
|
||||||
|
if (hov) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||||
|
const bool sel = (value == i);
|
||||||
|
if (sel) {
|
||||||
|
const float in = 2.0f * dp;
|
||||||
|
dl->AddRectFilled(ImVec2(mn.x + in, mn.y + in), ImVec2(mx.x - in, mx.y - in),
|
||||||
|
WithAlpha(Primary(), 210), std::max(1.0f, round - in));
|
||||||
|
} else if (hov) {
|
||||||
|
dl->AddRectFilled(mn, mx, WithAlpha(OnSurface(), 26), round);
|
||||||
|
}
|
||||||
|
const ImVec2 ts = ImGui::CalcTextSize(items[i]);
|
||||||
|
const float lpad = 4.0f * dp;
|
||||||
|
const float tx = (ts.x <= seg - 2.0f * lpad) ? (seg - ts.x) * 0.5f : lpad;
|
||||||
|
ImGui::PushClipRect(mn, mx, true);
|
||||||
|
dl->AddText(ImVec2(mn.x + tx, mn.y + (h - ts.y) * 0.5f),
|
||||||
|
sel ? IM_COL32(255, 255, 255, 236) : OnSurfaceMedium(), items[i]);
|
||||||
|
ImGui::PopClipRect();
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
ImGui::SetCursorScreenPos(origin);
|
||||||
|
ImGui::Dummy(ImVec2(row.ctrlW, h)); // reserve the control's rect for layout flow
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Tiered action buttons ───────────────────────────────────────────────────
|
||||||
|
// Primary = filled accent (the one main action of a group)
|
||||||
|
// Secondary = glass (default — common actions)
|
||||||
|
// Tertiary = ghost / low-emphasis (rarely used)
|
||||||
|
// Destructive = error-tinted outline (delete / reset)
|
||||||
|
enum class ActionTier { Primary, Secondary, Tertiary, Destructive };
|
||||||
|
|
||||||
|
// The width an ActionButton will occupy (for ButtonFlow / manual layout).
|
||||||
|
inline float ActionButtonWidth(const char* label, const char* icon, float minWidth = 0.0f) {
|
||||||
|
ImFont* lf = Type().button();
|
||||||
|
ImFont* icf = Type().iconSmall();
|
||||||
|
const float dp = Layout::dpiScale();
|
||||||
|
const float padX = 12.0f * dp, gap = 6.0f * dp;
|
||||||
|
const float labelW = lf->CalcTextSizeA(lf->LegacySize, FLT_MAX, 0, label).x;
|
||||||
|
const float iconW = (icon && icon[0] && icf) ? icf->CalcTextSizeA(icf->LegacySize, FLT_MAX, 0, icon).x : 0.0f;
|
||||||
|
const float w = padX * 2.0f + iconW + (iconW > 0.0f ? gap : 0.0f) + labelW;
|
||||||
|
return std::max(w, minWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A tiered action button with an optional leading Material icon (ICON_MD_* or nullptr). Auto-sizes to
|
||||||
|
// its content (>= minWidth). Respects BeginDisabled() (dims via the style alpha, not clickable).
|
||||||
|
inline bool ActionButton(const char* id, const char* label, const char* icon, ActionTier tier, float minWidth = 0.0f) {
|
||||||
|
ImFont* lf = Type().button();
|
||||||
|
ImFont* icf = Type().iconSmall();
|
||||||
|
const float dp = Layout::dpiScale();
|
||||||
|
const float gap = 6.0f * dp;
|
||||||
|
const float h = ImGui::GetFrameHeight();
|
||||||
|
const bool hasIcon = icon && icon[0] && icf;
|
||||||
|
const float labelW = lf->CalcTextSizeA(lf->LegacySize, FLT_MAX, 0, label).x;
|
||||||
|
const float iconW = hasIcon ? icf->CalcTextSizeA(icf->LegacySize, FLT_MAX, 0, icon).x : 0.0f;
|
||||||
|
const float w = ActionButtonWidth(label, icon, minWidth);
|
||||||
|
|
||||||
|
const ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||||
|
const bool pressed = ImGui::InvisibleButton(id, ImVec2(w, h));
|
||||||
|
const bool hov = ImGui::IsItemHovered(), act = ImGui::IsItemActive();
|
||||||
|
if (hov) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||||
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||||
|
const ImVec2 pMax(pos.x + w, pos.y + h);
|
||||||
|
const float round = ImGui::GetStyle().FrameRounding;
|
||||||
|
const float a = ImGui::GetStyle().Alpha; // BeginDisabled() lowers this
|
||||||
|
|
||||||
|
ImU32 bg = 0, border = 0, fg = OnSurface();
|
||||||
|
bool glass = false;
|
||||||
|
switch (tier) {
|
||||||
|
case ActionTier::Primary:
|
||||||
|
bg = WithAlpha(Primary(), act ? 255 : (hov ? 245 : 220));
|
||||||
|
fg = IM_COL32(255, 255, 255, 240);
|
||||||
|
break;
|
||||||
|
case ActionTier::Secondary:
|
||||||
|
bg = WithAlpha(OnSurface(), hov ? 26 : 16);
|
||||||
|
border = WithAlpha(OnSurface(), 40);
|
||||||
|
glass = true;
|
||||||
|
fg = OnSurface();
|
||||||
|
break;
|
||||||
|
case ActionTier::Tertiary:
|
||||||
|
bg = hov ? WithAlpha(OnSurface(), 18) : 0;
|
||||||
|
fg = OnSurfaceMedium();
|
||||||
|
break;
|
||||||
|
case ActionTier::Destructive:
|
||||||
|
bg = hov ? WithAlpha(Error(), 32) : WithAlpha(Error(), 12);
|
||||||
|
border = WithAlpha(Error(), 90);
|
||||||
|
fg = Error();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (bg) dl->AddRectFilled(pos, pMax, ScaleAlpha(bg, a), round);
|
||||||
|
if (border) dl->AddRect(pos, pMax, ScaleAlpha(border, a), round, 0, 1.0f);
|
||||||
|
if (glass) DrawButtonGlassOverlay(dl, pos, pMax, round, act, hov);
|
||||||
|
fg = ScaleAlpha(fg, a);
|
||||||
|
|
||||||
|
const float contentW = iconW + (iconW > 0.0f ? gap : 0.0f) + labelW;
|
||||||
|
float cx = pos.x + (w - contentW) * 0.5f;
|
||||||
|
if (hasIcon) {
|
||||||
|
dl->AddText(icf, icf->LegacySize, ImVec2(cx, pos.y + (h - icf->LegacySize) * 0.5f), fg, icon);
|
||||||
|
cx += iconW + gap;
|
||||||
|
}
|
||||||
|
dl->AddText(lf, lf->LegacySize, ImVec2(cx, pos.y + (h - lf->LegacySize) * 0.5f), fg, label);
|
||||||
|
return pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Places ActionButtons left→right, wrapping to a new row when the next one won't fit (instead of the
|
||||||
|
// old font-scale-to-fit). Call next(width) before each ActionButton.
|
||||||
|
struct ButtonFlow {
|
||||||
|
float availW, gap; float x = 0.0f; bool firstOnRow = true;
|
||||||
|
explicit ButtonFlow(float availWidth, float gapPx = 8.0f) : availW(availWidth) {
|
||||||
|
gap = gapPx * Layout::dpiScale();
|
||||||
|
}
|
||||||
|
void next(float w) {
|
||||||
|
if (firstOnRow) { firstOnRow = false; x = w; return; }
|
||||||
|
if (x + gap + w <= availW) { ImGui::SameLine(0, gap); x += gap + w; }
|
||||||
|
else { x = w; } // natural newline wraps to the next row
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace material
|
||||||
|
} // namespace ui
|
||||||
|
} // namespace dragonx
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -70,6 +70,7 @@ AddressRowLayout ComputeAddressRowLayout(float rowX,
|
|||||||
float spacingSm,
|
float spacingSm,
|
||||||
float spacingXs)
|
float spacingXs)
|
||||||
{
|
{
|
||||||
|
(void)spacingXs; // trailing button now insets by rowPadLeft (mirrors the left margin)
|
||||||
AddressRowLayout layout;
|
AddressRowLayout layout;
|
||||||
layout.contentStartX = rowX + rowPadLeft;
|
layout.contentStartX = rowX + rowPadLeft;
|
||||||
layout.contentStartY = rowY + spacingMd;
|
layout.contentStartY = rowY + spacingMd;
|
||||||
@@ -77,7 +78,9 @@ AddressRowLayout ComputeAddressRowLayout(float rowX,
|
|||||||
|
|
||||||
const float buttonY = rowY + (rowHeight - layout.buttonSize) * 0.5f;
|
const float buttonY = rowY + (rowHeight - layout.buttonSize) * 0.5f;
|
||||||
const float rightEdge = rowX + rowWidth;
|
const float rightEdge = rowX + rowWidth;
|
||||||
const float favoriteX = rightEdge - layout.buttonSize - spacingXs;
|
// Inset the trailing (favorite/star) button by the card's inner padding — the same margin the
|
||||||
|
// left content uses (rowPadLeft) — so it mirrors the left edge instead of hugging the card edge.
|
||||||
|
const float favoriteX = rightEdge - layout.buttonSize - rowPadLeft;
|
||||||
const float visibilityX = favoriteX - spacingSm - layout.buttonSize;
|
const float visibilityX = favoriteX - spacingSm - layout.buttonSize;
|
||||||
|
|
||||||
layout.favoriteButton = {favoriteX, buttonY, layout.buttonSize, layout.buttonSize};
|
layout.favoriteButton = {favoriteX, buttonY, layout.buttonSize, layout.buttonSize};
|
||||||
|
|||||||
@@ -1859,8 +1859,10 @@ void RenderChatSettingsControls(App* app, float contentWidth)
|
|||||||
// Right-align controls to the row's true right edge. The Settings tab renders us inside a GlassCard
|
// Right-align controls to the row's true right edge. The Settings tab renders us inside a GlassCard
|
||||||
// whose content region isn't narrowed to the card padding, so it passes an explicit contentWidth;
|
// whose content region isn't narrowed to the card padding, so it passes an explicit contentWidth;
|
||||||
// the chat modal's dialog content region is correct, so it passes 0 (auto).
|
// the chat modal's dialog content region is correct, so it passes 0 (auto).
|
||||||
const float leftX = ImGui::GetCursorPosX();
|
// leftX/rowW define the current column the rows lay out in; retargeted below
|
||||||
const float rowW = (contentWidth > 0.0f) ? contentWidth : ImGui::GetContentRegionAvail().x;
|
// to split Appearance | Messaging into two columns when the card is wide.
|
||||||
|
float leftX = ImGui::GetCursorPosX();
|
||||||
|
float rowW = (contentWidth > 0.0f) ? contentWidth : ImGui::GetContentRegionAvail().x;
|
||||||
|
|
||||||
// Label left, control right-aligned within [leftX, leftX+rowW]. Leaves the cursor at the control origin.
|
// Label left, control right-aligned within [leftX, leftX+rowW]. Leaves the cursor at the control origin.
|
||||||
auto beginRow = [&](const char* label) {
|
auto beginRow = [&](const char* label) {
|
||||||
@@ -1925,6 +1927,17 @@ void RenderChatSettingsControls(App* app, float contentWidth)
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Two internal columns when the card is wide enough: Appearance on the left,
|
||||||
|
// Messaging on the right — fills the width and roughly halves the height.
|
||||||
|
// (Mirrors the Node & Security card.) Narrow (the chat modal) stays single-column.
|
||||||
|
const float chatColGap = 24.0f * dp;
|
||||||
|
const bool chatTwoCol = rowW > 760.0f * dp;
|
||||||
|
const float chatColW = chatTwoCol ? (rowW - chatColGap) * 0.5f : rowW;
|
||||||
|
const float chatBaseLeftX = leftX;
|
||||||
|
const float chatTopY = ImGui::GetCursorPosY();
|
||||||
|
float chatLeftBottomY = 0.0f;
|
||||||
|
if (chatTwoCol) rowW = chatColW; // left column width
|
||||||
|
|
||||||
// ── Appearance ────────────────────────────────────────────────────────────────
|
// ── Appearance ────────────────────────────────────────────────────────────────
|
||||||
section("chat_sec_appearance");
|
section("chat_sec_appearance");
|
||||||
// Emoji style (monochrome / color). Color needs a FreeType build (native + the cross-built Windows
|
// Emoji style (monochrome / color). Color needs a FreeType build (native + the cross-built Windows
|
||||||
@@ -1933,6 +1946,7 @@ void RenderChatSettingsControls(App* app, float contentWidth)
|
|||||||
int v = st->getChatEmojiColor() ? 1 : 0;
|
int v = st->getChatEmojiColor() ? 1 : 0;
|
||||||
const char* items[] = { TR("chat_emoji_mono"), TR("chat_emoji_color") };
|
const char* items[] = { TR("chat_emoji_mono"), TR("chat_emoji_color") };
|
||||||
int nv = segmented(TR("chat_opt_emoji"), items, 2, v);
|
int nv = segmented(TR("chat_opt_emoji"), items, 2, v);
|
||||||
|
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_chat_emoji_style"));
|
||||||
if (nv != v) { st->setChatEmojiColor(nv == 1); st->save(); app->requestFontRebuild(); }
|
if (nv != v) { st->setChatEmojiColor(nv == 1); st->save(); app->requestFontRebuild(); }
|
||||||
}
|
}
|
||||||
// Bubble style (segmented) + accent color (a 6-way dropdown — too many for a segmented control).
|
// Bubble style (segmented) + accent color (a 6-way dropdown — too many for a segmented control).
|
||||||
@@ -1940,6 +1954,7 @@ void RenderChatSettingsControls(App* app, float contentWidth)
|
|||||||
const char* items[] = { TR("chat_bubble_rounded"), TR("chat_bubble_square"), TR("chat_bubble_minimal") };
|
const char* items[] = { TR("chat_bubble_rounded"), TR("chat_bubble_square"), TR("chat_bubble_minimal") };
|
||||||
int v = st->getChatBubbleStyle();
|
int v = st->getChatBubbleStyle();
|
||||||
int nv = segmented(TR("chat_opt_bubble_style"), items, 3, v);
|
int nv = segmented(TR("chat_opt_bubble_style"), items, 3, v);
|
||||||
|
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_chat_bubble_style"));
|
||||||
if (nv != v) { st->setChatBubbleStyle(nv); st->save(); }
|
if (nv != v) { st->setChatBubbleStyle(nv); st->save(); }
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -1948,12 +1963,14 @@ void RenderChatSettingsControls(App* app, float contentWidth)
|
|||||||
const char* items[] = { TR("chat_accent_theme"), TR("chat_accent_blue"), TR("chat_accent_green"),
|
const char* items[] = { TR("chat_accent_theme"), TR("chat_accent_blue"), TR("chat_accent_green"),
|
||||||
TR("chat_accent_purple"), TR("chat_accent_amber"), TR("chat_accent_pink") };
|
TR("chat_accent_purple"), TR("chat_accent_amber"), TR("chat_accent_pink") };
|
||||||
if (ImGui::Combo("##chat_baccent", &v, items, 6)) { st->setChatBubbleAccent(v); st->save(); }
|
if (ImGui::Combo("##chat_baccent", &v, items, 6)) { st->setChatBubbleAccent(v); st->save(); }
|
||||||
|
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_chat_bubble_accent"));
|
||||||
}
|
}
|
||||||
// Message density (segmented).
|
// Message density (segmented).
|
||||||
{
|
{
|
||||||
const char* items[] = { TR("chat_density_comfortable"), TR("chat_density_compact") };
|
const char* items[] = { TR("chat_density_comfortable"), TR("chat_density_compact") };
|
||||||
int v = st->getChatDensity();
|
int v = st->getChatDensity();
|
||||||
int nv = segmented(TR("chat_opt_density"), items, 2, v);
|
int nv = segmented(TR("chat_opt_density"), items, 2, v);
|
||||||
|
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_chat_density"));
|
||||||
if (nv != v) { st->setChatDensity(nv); st->save(); }
|
if (nv != v) { st->setChatDensity(nv); st->save(); }
|
||||||
}
|
}
|
||||||
// Message text size (slider).
|
// Message text size (slider).
|
||||||
@@ -1963,6 +1980,16 @@ void RenderChatSettingsControls(App* app, float contentWidth)
|
|||||||
if (ImGui::SliderFloat("##chat_font", &v, 0.8f, 1.5f, "%.2fx", ImGuiSliderFlags_AlwaysClamp)) {
|
if (ImGui::SliderFloat("##chat_font", &v, 0.8f, 1.5f, "%.2fx", ImGuiSliderFlags_AlwaysClamp)) {
|
||||||
st->setChatFontScale(v); st->save();
|
st->setChatFontScale(v); st->save();
|
||||||
}
|
}
|
||||||
|
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_chat_font_size"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move Messaging into the right column (float it with Indent so every row's
|
||||||
|
// line-start holds the column; retarget leftX so controls right-align in it).
|
||||||
|
if (chatTwoCol) {
|
||||||
|
chatLeftBottomY = ImGui::GetCursorPosY();
|
||||||
|
ImGui::SetCursorPosY(chatTopY);
|
||||||
|
ImGui::Indent(chatColW + chatColGap);
|
||||||
|
leftX = chatBaseLeftX + chatColW + chatColGap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Messaging ─────────────────────────────────────────────────────────────────
|
// ── Messaging ─────────────────────────────────────────────────────────────────
|
||||||
@@ -1974,12 +2001,14 @@ void RenderChatSettingsControls(App* app, float contentWidth)
|
|||||||
if (ImGui::SliderFloat("##chat_poll", &v, 0.5f, 15.0f, "%.1f s", ImGuiSliderFlags_AlwaysClamp)) {
|
if (ImGui::SliderFloat("##chat_poll", &v, 0.5f, 15.0f, "%.1f s", ImGuiSliderFlags_AlwaysClamp)) {
|
||||||
st->setChatPollRateSec(v); st->save();
|
st->setChatPollRateSec(v); st->save();
|
||||||
}
|
}
|
||||||
|
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_chat_poll_rate"));
|
||||||
}
|
}
|
||||||
// Chat timestamps (segmented) — overrides the app-wide clock (Settings → General) for this tab only.
|
// Chat timestamps (segmented) — overrides the app-wide clock (Settings → General) for this tab only.
|
||||||
{
|
{
|
||||||
const char* items[] = { TR("chat_ts_global_short"), TR("chat_ts_24h"), TR("chat_ts_12h") };
|
const char* items[] = { TR("chat_ts_global_short"), TR("chat_ts_24h"), TR("chat_ts_12h") };
|
||||||
int v = st->getChatTimeFormat();
|
int v = st->getChatTimeFormat();
|
||||||
int nv = segmented(TR("chat_opt_timestamp"), items, 3, v);
|
int nv = segmented(TR("chat_opt_timestamp"), items, 3, v);
|
||||||
|
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_chat_timestamp"));
|
||||||
if (nv != v) { st->setChatTimeFormat(nv); st->save(); }
|
if (nv != v) { st->setChatTimeFormat(nv); st->save(); }
|
||||||
}
|
}
|
||||||
// Enter-to-send (checkbox).
|
// Enter-to-send (checkbox).
|
||||||
@@ -1987,6 +2016,15 @@ void RenderChatSettingsControls(App* app, float contentWidth)
|
|||||||
ImGui::Dummy(ImVec2(0.0f, rowGap));
|
ImGui::Dummy(ImVec2(0.0f, rowGap));
|
||||||
bool v = st->getChatEnterSends();
|
bool v = st->getChatEnterSends();
|
||||||
if (ImGui::Checkbox(TR("chat_opt_enter_sends"), &v)) { st->setChatEnterSends(v); st->save(); }
|
if (ImGui::Checkbox(TR("chat_opt_enter_sends"), &v)) { st->setChatEnterSends(v); st->save(); }
|
||||||
|
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_chat_enter_sends"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the two-column band: un-indent and drop below the taller column.
|
||||||
|
if (chatTwoCol) {
|
||||||
|
ImGui::Unindent(chatColW + chatColGap);
|
||||||
|
const float chatRightBottomY = ImGui::GetCursorPosY();
|
||||||
|
ImGui::SetCursorPosX(chatBaseLeftX);
|
||||||
|
ImGui::SetCursorPosY(std::max(chatLeftBottomY, chatRightBottomY));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1097,6 +1097,10 @@ void RenderContactsTab(App* app)
|
|||||||
}
|
}
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
// Land the cursor at the glass-panel bottom (tpMin.y + listH) so the count footer lines up
|
||||||
|
// with the Cards/List views. The table is inset by tVpad and its outer_size is listH-2*tVpad,
|
||||||
|
// so it would otherwise end tVpad higher and pull the footer up.
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(tpMin.x, tpMin.y + listH));
|
||||||
} else {
|
} else {
|
||||||
// ── CARDS (0) / LIST (1) mode — tactile Material items, no grid lines. ──
|
// ── CARDS (0) / LIST (1) mode — tactile Material items, no grid lines. ──
|
||||||
const bool asCard = (viewMode == 0);
|
const bool asCard = (viewMode == 0);
|
||||||
|
|||||||
@@ -211,12 +211,11 @@ void ExportAllKeysDialog::render(App* app)
|
|||||||
std::string filepath = configDir + "/" + filename;
|
std::string filepath = configDir + "/" + filename;
|
||||||
bool writeOk = false;
|
bool writeOk = false;
|
||||||
if (exported > 0) {
|
if (exported > 0) {
|
||||||
std::ofstream file(filepath);
|
// Write the plaintext private keys 0600 + atomically (never
|
||||||
if (file.is_open()) {
|
// world/group-readable, never a half-written file) — the same restricted
|
||||||
file << keys;
|
// atomic-write idiom the PIN vault uses. A default ofstream would create
|
||||||
file.close();
|
// the key dump with umask-derived (often 0644) permissions.
|
||||||
writeOk = true;
|
writeOk = util::Platform::writeFileAtomically(filepath, keys, /*restrictPermissions=*/true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!keys.empty()) sodium_memzero(&keys[0], keys.size()); // don't leave every key in freed heap
|
if (!keys.empty()) sodium_memzero(&keys[0], keys.size()); // don't leave every key in freed heap
|
||||||
|
|
||||||
|
|||||||
@@ -36,21 +36,30 @@ static bool s_exporting = false;
|
|||||||
// Helper to escape CSV field
|
// Helper to escape CSV field
|
||||||
static std::string escapeCSV(const std::string& field)
|
static std::string escapeCSV(const std::string& field)
|
||||||
{
|
{
|
||||||
if (field.find(',') != std::string::npos ||
|
// Neutralize spreadsheet formula injection: a field beginning with '=', '+', '-', '@',
|
||||||
field.find('"') != std::string::npos ||
|
// tab, or CR is interpreted as a formula by Excel/LibreOffice, letting an attacker-supplied
|
||||||
field.find('\n') != std::string::npos) {
|
// memo/address execute on open. Prefix such fields with a single quote so they render as text.
|
||||||
|
std::string safe = field;
|
||||||
|
if (!safe.empty()) {
|
||||||
|
const char c0 = safe.front();
|
||||||
|
if (c0 == '=' || c0 == '+' || c0 == '-' || c0 == '@' || c0 == '\t' || c0 == '\r')
|
||||||
|
safe.insert(safe.begin(), '\'');
|
||||||
|
}
|
||||||
|
if (safe.find(',') != std::string::npos ||
|
||||||
|
safe.find('"') != std::string::npos ||
|
||||||
|
safe.find('\n') != std::string::npos) {
|
||||||
// Escape quotes and wrap in quotes
|
// Escape quotes and wrap in quotes
|
||||||
std::string escaped;
|
std::string escaped;
|
||||||
escaped.reserve(field.size() + 4);
|
escaped.reserve(safe.size() + 4);
|
||||||
escaped += '"';
|
escaped += '"';
|
||||||
for (char c : field) {
|
for (char c : safe) {
|
||||||
if (c == '"') escaped += "\"\"";
|
if (c == '"') escaped += "\"\"";
|
||||||
else escaped += c;
|
else escaped += c;
|
||||||
}
|
}
|
||||||
escaped += '"';
|
escaped += '"';
|
||||||
return escaped;
|
return escaped;
|
||||||
}
|
}
|
||||||
return field;
|
return safe;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportTransactionsDialog::show()
|
void ExportTransactionsDialog::show()
|
||||||
|
|||||||
@@ -57,7 +57,10 @@ void RenderMiningControls(App* app, const WalletState& state, const MiningInfo&
|
|||||||
|
|
||||||
// --- Compute thread grid layout based on controls card width ---
|
// --- Compute thread grid layout based on controls card width ---
|
||||||
// Estimate controlsW first to compute cols correctly
|
// Estimate controlsW first to compute cols correctly
|
||||||
float estControlsW = availWidth - std::min(schema::UI().drawElement("tabs.mining", "button-max-width-clamp").size, miningBtnMaxW) - miningBtnGap;
|
// The Mine button is square (= card height, which scales with DPI), so the width we
|
||||||
|
// reserve for it here must scale too — a RAW clamp under-reserves at >100% scaling, which
|
||||||
|
// over-estimates the grid width and lets the thread cells overflow the card (e.g. at 150%).
|
||||||
|
float estControlsW = availWidth - std::min(schema::UI().drawElement("tabs.mining", "button-max-width-clamp").size * dp, miningBtnMaxW) - miningBtnGap;
|
||||||
float innerW = estControlsW - pad * 2;
|
float innerW = estControlsW - pad * 2;
|
||||||
float cellSz = std::clamp(schema::UI().drawElement("tabs.mining", "cell-size").size * vs, schema::UI().drawElement("tabs.mining", "cell-min-size").size, schema::UI().drawElement("tabs.mining", "cell-max-size").sizeOr(42.0f));
|
float cellSz = std::clamp(schema::UI().drawElement("tabs.mining", "cell-size").size * vs, schema::UI().drawElement("tabs.mining", "cell-min-size").size, schema::UI().drawElement("tabs.mining", "cell-max-size").sizeOr(42.0f));
|
||||||
float cellGap = std::max(schema::UI().drawElement("tabs.mining", "cell-gap-min").size, cellSz * schema::UI().drawElement("tabs.mining", "cell-gap-ratio").size);
|
float cellGap = std::max(schema::UI().drawElement("tabs.mining", "cell-gap-min").size, cellSz * schema::UI().drawElement("tabs.mining", "cell-gap-ratio").size);
|
||||||
|
|||||||
@@ -308,11 +308,14 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
|
|||||||
pdl->AddRectFilled(rowMin, rowMax, IM_COL32(255, 255, 255, 10));
|
pdl->AddRectFilled(rowMin, rowMax, IM_COL32(255, 255, 255, 10));
|
||||||
if (rowHov && !inXZone)
|
if (rowHov && !inXZone)
|
||||||
pdl->AddRectFilled(rowMin, rowMax, StateHover());
|
pdl->AddRectFilled(rowMin, rowMax, StateHover());
|
||||||
// Item text with internal padding
|
// Item text with internal padding, middle-truncated so a long saved URL
|
||||||
|
// can't run under the trailing X (delete) button.
|
||||||
float textY = rowMin.y + (rowH - rowFontSz) * 0.5f;
|
float textY = rowMin.y + (rowH - rowFontSz) * 0.5f;
|
||||||
|
float maxTextW = popupInnerW - xZoneW - textPadX * 2.0f;
|
||||||
|
std::string urlDisp = material::TruncateToWidth(url, rowFont, rowFontSz, maxTextW);
|
||||||
pdl->AddText(rowFont, rowFontSz,
|
pdl->AddText(rowFont, rowFontSz,
|
||||||
ImVec2(rowMin.x + textPadX, textY),
|
ImVec2(rowMin.x + textPadX, textY),
|
||||||
isCurrent ? Primary() : OnSurface(), url.c_str());
|
isCurrent ? Primary() : OnSurface(), urlDisp.c_str());
|
||||||
// X button — flush with right edge, icon centered
|
// X button — flush with right edge, icon centered
|
||||||
{
|
{
|
||||||
ImVec2 xMin(rowMax.x - xZoneW, rowMin.y);
|
ImVec2 xMin(rowMax.x - xZoneW, rowMin.y);
|
||||||
@@ -467,11 +470,14 @@ void RenderMiningModeToggle(App* app, const WalletState& state, const MiningInfo
|
|||||||
pdl->AddRectFilled(rowMin, rowMax, IM_COL32(255, 255, 255, 10));
|
pdl->AddRectFilled(rowMin, rowMax, IM_COL32(255, 255, 255, 10));
|
||||||
if (rowHov && !inXZone)
|
if (rowHov && !inXZone)
|
||||||
pdl->AddRectFilled(rowMin, rowMax, StateHover());
|
pdl->AddRectFilled(rowMin, rowMax, StateHover());
|
||||||
// Full address text with internal padding
|
// Address text with internal padding, middle-truncated so a full z-address
|
||||||
|
// (~78 chars) can't run under the trailing X (delete) button.
|
||||||
float textY = rowMin.y + (wRowH - wRowFontSz) * 0.5f;
|
float textY = rowMin.y + (wRowH - wRowFontSz) * 0.5f;
|
||||||
|
float wMaxTextW = wPopupInnerW - wXZoneW - wTextPadX * 2.0f;
|
||||||
|
std::string addrDisp = material::TruncateToWidth(addr, wRowFont, wRowFontSz, wMaxTextW);
|
||||||
pdl->AddText(wRowFont, wRowFontSz,
|
pdl->AddText(wRowFont, wRowFontSz,
|
||||||
ImVec2(rowMin.x + wTextPadX, textY),
|
ImVec2(rowMin.x + wTextPadX, textY),
|
||||||
isCurrent ? Primary() : OnSurface(), addr.c_str());
|
isCurrent ? Primary() : OnSurface(), addrDisp.c_str());
|
||||||
// Tooltip for long addresses
|
// Tooltip for long addresses
|
||||||
if (rowHov && !inXZone)
|
if (rowHov && !inXZone)
|
||||||
material::Tooltip("%s", addr.c_str());
|
material::Tooltip("%s", addr.c_str());
|
||||||
|
|||||||
@@ -31,6 +31,31 @@ static bool endsWith(const std::string& s, const std::string& suffix) {
|
|||||||
return s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
|
return s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reject archive member names that would escape the extraction root (zip-slip).
|
||||||
|
// The snapshot legitimately carries sub-paths (blocks/, chainstate/), so — unlike the
|
||||||
|
// updaters, which flatten to baseName() — we keep the relative path but refuse any entry
|
||||||
|
// that is absolute, drive/UNC-rooted, or contains a ".." component.
|
||||||
|
static bool isSafeArchivePath(const std::string& name) {
|
||||||
|
if (name.empty()) return false;
|
||||||
|
std::string n = name;
|
||||||
|
std::replace(n.begin(), n.end(), '\\', '/'); // normalize Windows separators
|
||||||
|
if (n.front() == '/') return false; // absolute POSIX path
|
||||||
|
const char c0 = n[0];
|
||||||
|
if (n.size() >= 2 && n[1] == ':' &&
|
||||||
|
((c0 >= 'A' && c0 <= 'Z') || (c0 >= 'a' && c0 <= 'z')))
|
||||||
|
return false; // Windows drive letter (C:...)
|
||||||
|
size_t start = 0;
|
||||||
|
while (start <= n.size()) {
|
||||||
|
const size_t slash = n.find('/', start);
|
||||||
|
const std::string comp =
|
||||||
|
n.substr(start, slash == std::string::npos ? std::string::npos : slash - start);
|
||||||
|
if (comp == "..") return false; // path traversal component
|
||||||
|
if (slash == std::string::npos) break;
|
||||||
|
start = slash + 1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static size_t writeFileCallback(void* contents, size_t size, size_t nmemb, void* userp) {
|
static size_t writeFileCallback(void* contents, size_t size, size_t nmemb, void* userp) {
|
||||||
size_t total = size * nmemb;
|
size_t total = size * nmemb;
|
||||||
FILE* fp = static_cast<FILE*>(userp);
|
FILE* fp = static_cast<FILE*>(userp);
|
||||||
@@ -383,6 +408,16 @@ bool Bootstrap::extract(const std::string& zipPath, const std::string& dataDir)
|
|||||||
|
|
||||||
std::string filename = stat.m_filename;
|
std::string filename = stat.m_filename;
|
||||||
|
|
||||||
|
// *** SECURITY: reject zip-slip / path-traversal entries before building any path ***
|
||||||
|
// A legitimate snapshot from the project host never contains these; an entry that does
|
||||||
|
// indicates a malicious/corrupt archive, so abort rather than silently skip.
|
||||||
|
if (!isSafeArchivePath(filename)) {
|
||||||
|
DEBUG_LOGF("[Bootstrap] Unsafe archive path rejected: %s\n", filename.c_str());
|
||||||
|
setProgress(State::Failed, "Refusing to extract unsafe archive entry: " + filename);
|
||||||
|
mz_zip_reader_end(&zip);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// *** CRITICAL: Skip wallet.dat ***
|
// *** CRITICAL: Skip wallet.dat ***
|
||||||
if (filename == "wallet.dat" || endsWith(filename, "/wallet.dat")) {
|
if (filename == "wallet.dat" || endsWith(filename, "/wallet.dat")) {
|
||||||
DEBUG_LOGF("[Bootstrap] Skipping wallet.dat (protected)\n");
|
DEBUG_LOGF("[Bootstrap] Skipping wallet.dat (protected)\n");
|
||||||
|
|||||||
@@ -19,10 +19,19 @@ namespace util {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// Cap for in-memory text/JSON responses (release metadata, price/candle data). Far above any
|
||||||
|
// legitimate body, but bounds memory if a hostile/MITM'd server streams an unbounded response.
|
||||||
|
constexpr std::size_t kMaxMetadataBytes = 16u * 1024 * 1024;
|
||||||
|
|
||||||
size_t writeStringCb(void* contents, size_t size, size_t nmemb, void* userp)
|
size_t writeStringCb(void* contents, size_t size, size_t nmemb, void* userp)
|
||||||
{
|
{
|
||||||
static_cast<std::string*>(userp)->append(static_cast<char*>(contents), size * nmemb);
|
auto* s = static_cast<std::string*>(userp);
|
||||||
return size * nmemb;
|
const size_t n = size * nmemb;
|
||||||
|
// Hard cap: a chunked response omits Content-Length, so CURLOPT_MAXFILESIZE cannot catch it —
|
||||||
|
// aborting here (short count) makes curl fail the transfer instead of exhausting memory.
|
||||||
|
if (s->size() + n > kMaxMetadataBytes) return 0;
|
||||||
|
s->append(static_cast<char*>(contents), n);
|
||||||
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t writeFileCb(void* contents, size_t size, size_t nmemb, void* userp)
|
size_t writeFileCb(void* contents, size_t size, size_t nmemb, void* userp)
|
||||||
@@ -52,6 +61,7 @@ std::string httpGetString(const std::string& url, const char* logTag)
|
|||||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "ObsidianDragon/1.0");
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "ObsidianDragon/1.0");
|
||||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
|
||||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15L);
|
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_MAXFILESIZE_LARGE, static_cast<curl_off_t>(kMaxMetadataBytes)); // reject oversized metadata
|
||||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
|
||||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
|
||||||
const CURLcode res = curl_easy_perform(curl);
|
const CURLcode res = curl_easy_perform(curl);
|
||||||
|
|||||||
@@ -721,6 +721,47 @@ void I18n::loadBuiltinEnglish()
|
|||||||
strings_["tt_idle_delay"] = "How long to wait before starting mining";
|
strings_["tt_idle_delay"] = "How long to wait before starting mining";
|
||||||
strings_["tt_wizard"] = "Re-run the initial setup wizard\nDaemon will be restarted";
|
strings_["tt_wizard"] = "Re-run the initial setup wizard\nDaemon will be restarted";
|
||||||
strings_["tt_download_bootstrap"] = "Download blockchain bootstrap to speed up sync\nExisting block data will be replaced";
|
strings_["tt_download_bootstrap"] = "Download blockchain bootstrap to speed up sync\nExisting block data will be replaced";
|
||||||
|
// --- Full-node Node & Security tooltips ---
|
||||||
|
strings_["tt_rpc_toggle"] = "Show or hide the read-only RPC connection details (host, port, user, password) for the daemon";
|
||||||
|
strings_["tt_daemon_refresh"] = "Re-read the installed and bundled dragonxd version, size, and date shown above";
|
||||||
|
// --- Lite wallet Node & Security tooltips ---
|
||||||
|
strings_["tt_lite_lifecycle_toggle"] = "Show or hide the create / open / restore controls for managing your lite wallet file";
|
||||||
|
strings_["tt_lite_lifecycle_op"] = "Choose whether to create a new wallet, open an existing one, or restore one from a seed phrase";
|
||||||
|
strings_["tt_lite_wallet_path"] = "Path or name of the wallet file to open or restore into";
|
||||||
|
strings_["tt_lite_restore_seed"] = "The 24-word recovery seed phrase to restore this wallet from; hidden as you type";
|
||||||
|
strings_["tt_lite_restore_birthday"] = "Block height the wallet was created at; scanning starts here. Use 0 or the earliest height if unsure";
|
||||||
|
strings_["tt_lite_restore_account"] = "HD account index to restore; leave 0 unless you used multiple accounts under this seed";
|
||||||
|
strings_["tt_lite_restore_overwrite"] = "Replace an existing wallet file with this restore. Warning: overwrites the current wallet data";
|
||||||
|
strings_["tt_lite_lifecycle_pass"] = "Passphrase to unlock or set on the wallet during this create / open / restore operation";
|
||||||
|
strings_["tt_lite_lifecycle_run"] = "Run the selected create / open / restore operation with the values above";
|
||||||
|
strings_["tt_lite_show_seed"] = "Reveal this wallet's recovery seed phrase and birthday. Anyone with the seed can spend your funds";
|
||||||
|
strings_["tt_lite_show_keys"] = "Reveal this wallet's private spending keys. Anyone with a key can spend the funds it controls";
|
||||||
|
strings_["tt_lite_copy"] = "Copy the revealed secret to the clipboard";
|
||||||
|
strings_["tt_lite_save_seed_file"] = "Write the seed and birthday to an owner-only file (lite-seed-backup.txt) in the config folder";
|
||||||
|
strings_["tt_lite_hide_wipe"] = "Hide the revealed secret and securely wipe it from memory";
|
||||||
|
strings_["tt_lite_import_key"] = "Paste a private spending or viewing key to import; its history appears after the next sync";
|
||||||
|
strings_["tt_lite_import_key_btn"] = "Import the entered private key into this wallet; funds and history appear after the next sync";
|
||||||
|
strings_["tt_lite_encrypt_pass"] = "Passphrase to encrypt the wallet with. If lost, the wallet cannot be unlocked or recovered";
|
||||||
|
strings_["tt_lite_encrypt"] = "Encrypt the wallet with the passphrase above; it locks immediately and requires the passphrase to unlock";
|
||||||
|
strings_["tt_lite_unlock_pass"] = "Enter your passphrase to unlock the encrypted wallet";
|
||||||
|
strings_["tt_lite_unlock"] = "Unlock the encrypted wallet using the passphrase above";
|
||||||
|
strings_["tt_lite_lock"] = "Lock the wallet now; a passphrase is required to unlock and any chat session is torn down";
|
||||||
|
strings_["tt_lite_decrypt_pass"] = "Enter your passphrase to remove encryption from the wallet";
|
||||||
|
strings_["tt_lite_remove_encrypt"] = "Remove encryption and store the wallet unprotected; no passphrase will be required to open it";
|
||||||
|
// --- Chat & Contacts tooltips ---
|
||||||
|
strings_["tt_chat_emoji_style"] = "Render emoji in monochrome outline or full color";
|
||||||
|
strings_["tt_chat_bubble_style"] = "Message bubble shape: rounded, square, or minimal (flat, borderless)";
|
||||||
|
strings_["tt_chat_bubble_accent"] = "Accent color for your outgoing message bubbles (or follow the current theme)";
|
||||||
|
strings_["tt_chat_density"] = "Spacing between messages: Comfortable adds more padding; Compact fits more on screen";
|
||||||
|
strings_["tt_chat_font_size"] = "Scale chat message text from 0.8x to 1.5x. Affects only the Chat tab, not the rest of the app";
|
||||||
|
strings_["tt_chat_poll_rate"] = "How often to check for new and 0-conf messages (0.5-15 s). Faster is more responsive but uses more CPU";
|
||||||
|
strings_["tt_chat_timestamp"] = "Timestamp format for this tab only: follow the app-wide clock, or force 24-hour or 12-hour";
|
||||||
|
strings_["tt_chat_enter_sends"] = "When on, Enter sends the message and Shift+Enter adds a newline; when off, Enter adds a newline";
|
||||||
|
// --- Debug Options tooltips ---
|
||||||
|
strings_["tt_screenshot_sweep"] = "Cycle every theme across every tab, saving a screenshot of each into the config screenshots folder (overwrites the last sweep)";
|
||||||
|
strings_["tt_screenshot_sweep_full"] = "Like the theme sweep but also captures every modal / dialog / flow using temporary offline demo wallet data";
|
||||||
|
strings_["tt_screenshot_open_dir"] = "Open the screenshots folder (under the config directory) in your file manager";
|
||||||
|
strings_["tt_seed_demo_chat"] = "Inject sample conversations into the Chat tab so a sweep captures its UI; in-memory only, gone on restart";
|
||||||
strings_["download_bootstrap"] = "Download Bootstrap";
|
strings_["download_bootstrap"] = "Download Bootstrap";
|
||||||
strings_["download"] = "Download";
|
strings_["download"] = "Download";
|
||||||
strings_["retry"] = "Retry";
|
strings_["retry"] = "Retry";
|
||||||
|
|||||||
@@ -30,14 +30,6 @@ const std::vector<KnownPool>& knownPools()
|
|||||||
"https://pool.dragonx.is/api/stats", PoolStatsSchema::DragonXIs,
|
"https://pool.dragonx.is/api/stats", PoolStatsSchema::DragonXIs,
|
||||||
/*miningcorePoolId=*/"", /*feePercent=*/0.0, /*official=*/true,
|
/*miningcorePoolId=*/"", /*feePercent=*/0.0, /*official=*/true,
|
||||||
},
|
},
|
||||||
KnownPool{
|
|
||||||
// The mining (stratum) host is us.dragonx.cc — pool.dragonx.cc is the
|
|
||||||
// Cloudflare-proxied web/API host and does NOT accept stratum on :3333.
|
|
||||||
// Stats still come from pool.dragonx.cc/api/pools (proxied HTTP is fine).
|
|
||||||
"dragonx-cc-pplns", "pool.dragonx.cc", "us.dragonx.cc:3333", "rx/dragonx",
|
|
||||||
"https://pool.dragonx.cc/api/pools", PoolStatsSchema::Miningcore,
|
|
||||||
/*miningcorePoolId=*/"dragonx-pplns", /*feePercent=*/3.0, /*official=*/true,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
return pools;
|
return pools;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -314,6 +314,12 @@ void XmrigUpdater::installResolved(const std::string& targetDir, const XmrigRele
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (kXmrigRequireSignature) {
|
||||||
|
// Signatures are required but no key is pinned in this build: fail closed rather than
|
||||||
|
// silently downgrading to checksum-only (the checksum is same-origin as the archive).
|
||||||
|
fs::remove(zipPath, ec);
|
||||||
|
setProgress(State::Failed, "No signing key is pinned in this build — refusing to install.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,10 +81,56 @@ LiteConnectionSettings defaultLiteConnectionSettings()
|
|||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strict dotted-decimal check for the 127.0.0.0/8 loopback block: exactly four numeric octets
|
||||||
|
// (each 0-255) with the first equal to 127. This must NOT be a prefix match — "127.0.0.1.evil.com"
|
||||||
|
// and "127.evil.com" are attacker-controlled DNS names that a startsWith("127.") test would wrongly
|
||||||
|
// treat as loopback, reopening the plaintext-downgrade hole.
|
||||||
|
static bool isNumericIpv4Loopback(const std::string& h)
|
||||||
|
{
|
||||||
|
int parts = 0;
|
||||||
|
size_t start = 0;
|
||||||
|
while (true) {
|
||||||
|
const size_t dot = h.find('.', start);
|
||||||
|
const std::string seg = h.substr(start, dot == std::string::npos ? std::string::npos : dot - start);
|
||||||
|
if (seg.empty() || seg.size() > 3) return false;
|
||||||
|
int v = 0;
|
||||||
|
for (char c : seg) { if (c < '0' || c > '9') return false; v = v * 10 + (c - '0'); }
|
||||||
|
if (v > 255) return false;
|
||||||
|
if (parts == 0 && v != 127) return false; // 127.0.0.0/8 only
|
||||||
|
++parts;
|
||||||
|
if (dot == std::string::npos) break;
|
||||||
|
start = dot + 1;
|
||||||
|
}
|
||||||
|
return parts == 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isLoopbackLiteHostSpec(const std::string& hostPort)
|
||||||
|
{
|
||||||
|
std::string h = hostPort;
|
||||||
|
const size_t term = h.find_first_of("/?#"); // strip path/query/fragment
|
||||||
|
if (term != std::string::npos) h = h.substr(0, term);
|
||||||
|
const size_t at = h.rfind('@'); // strip userinfo (user:pass@host)
|
||||||
|
if (at != std::string::npos) h = h.substr(at + 1);
|
||||||
|
if (!h.empty() && h.front() == '[') { // [::1]:port (bracketed IPv6)
|
||||||
|
const size_t close = h.find(']');
|
||||||
|
h = (close == std::string::npos) ? h : h.substr(1, close - 1);
|
||||||
|
} else { // host or host:port
|
||||||
|
const size_t colon = h.find(':');
|
||||||
|
if (colon != std::string::npos) h = h.substr(0, colon);
|
||||||
|
}
|
||||||
|
return h == "localhost" || h == "::1" || isNumericIpv4Loopback(h);
|
||||||
|
}
|
||||||
|
|
||||||
bool isLiteServerUrlUsable(const std::string& serverUrl)
|
bool isLiteServerUrlUsable(const std::string& serverUrl)
|
||||||
{
|
{
|
||||||
const std::string normalized = liteTrimCopy(serverUrl);
|
const std::string normalized = liteTrimCopy(serverUrl);
|
||||||
return startsWith(normalized, "https://") || startsWith(normalized, "http://");
|
if (startsWith(normalized, "https://")) return true;
|
||||||
|
// SECURITY: plaintext http:// is a TLS downgrade for lightwalletd traffic (view keys,
|
||||||
|
// transactions, addresses). Permit it only for loopback (a local dev lightwalletd);
|
||||||
|
// reject remote plaintext servers instead of silently accepting the downgrade.
|
||||||
|
if (startsWith(normalized, "http://"))
|
||||||
|
return isLoopbackLiteHostSpec(normalized.substr(sizeof("http://") - 1));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isOfficialLiteServer(const std::string& serverUrl)
|
bool isOfficialLiteServer(const std::string& serverUrl)
|
||||||
|
|||||||
@@ -1066,7 +1066,16 @@ LiteEncryptionResult LiteWalletController::encryptWallet(std::string passphrase)
|
|||||||
}
|
}
|
||||||
out = parseEncryptionOpResponse(bridge_->execute("encrypt", passphrase));
|
out = parseEncryptionOpResponse(bridge_->execute("encrypt", passphrase));
|
||||||
secureWipeLiteSecret(passphrase);
|
secureWipeLiteSecret(passphrase);
|
||||||
if (out.ok) bridge_->execute("save", ""); // persist the now-encrypted wallet
|
if (out.ok) {
|
||||||
|
// Persist the now-encrypted wallet. If the save fails, do NOT report success — the
|
||||||
|
// on-disk wallet would still be unencrypted, contradicting what the user was told.
|
||||||
|
const auto saved = bridge_->execute("save", "");
|
||||||
|
if (!saved.ok) {
|
||||||
|
out.ok = false;
|
||||||
|
out.error = "wallet encrypted in memory but saving to disk failed" +
|
||||||
|
(saved.error.empty() ? std::string() : (": " + saved.error));
|
||||||
|
}
|
||||||
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1084,7 +1093,16 @@ LiteEncryptionResult LiteWalletController::decryptWallet(std::string passphrase)
|
|||||||
}
|
}
|
||||||
out = parseEncryptionOpResponse(bridge_->execute("decrypt", passphrase));
|
out = parseEncryptionOpResponse(bridge_->execute("decrypt", passphrase));
|
||||||
secureWipeLiteSecret(passphrase);
|
secureWipeLiteSecret(passphrase);
|
||||||
if (out.ok) bridge_->execute("save", ""); // persist the now-unencrypted wallet
|
if (out.ok) {
|
||||||
|
// Persist the now-unencrypted wallet. If the save fails, do NOT report success — the
|
||||||
|
// on-disk wallet would still be encrypted, contradicting what the user was told.
|
||||||
|
const auto saved = bridge_->execute("save", "");
|
||||||
|
if (!saved.ok) {
|
||||||
|
out.ok = false;
|
||||||
|
out.error = "wallet decrypted in memory but saving to disk failed" +
|
||||||
|
(saved.error.empty() ? std::string() : (": " + saved.error));
|
||||||
|
}
|
||||||
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3434,9 +3434,11 @@ void testBalanceAddressListModel()
|
|||||||
EXPECT_NEAR(layout.contentStartX, 22.0, 0.0001);
|
EXPECT_NEAR(layout.contentStartX, 22.0, 0.0001);
|
||||||
EXPECT_NEAR(layout.contentStartY, 26.0, 0.0001);
|
EXPECT_NEAR(layout.contentStartY, 26.0, 0.0001);
|
||||||
EXPECT_NEAR(layout.buttonSize, 38.0, 0.0001);
|
EXPECT_NEAR(layout.buttonSize, 38.0, 0.0001);
|
||||||
EXPECT_NEAR(layout.favoriteButton.x, 270.0, 0.0001);
|
// Trailing (favorite) button is inset by rowPadLeft (12) so it mirrors the left margin
|
||||||
EXPECT_NEAR(layout.visibilityButton.x, 228.0, 0.0001);
|
// instead of hugging the card edge: 310 - 38 - 12 = 260.
|
||||||
EXPECT_NEAR(layout.contentRight, 224.0, 0.0001);
|
EXPECT_NEAR(layout.favoriteButton.x, 260.0, 0.0001);
|
||||||
|
EXPECT_NEAR(layout.visibilityButton.x, 218.0, 0.0001);
|
||||||
|
EXPECT_NEAR(layout.contentRight, 214.0, 0.0001);
|
||||||
EXPECT_EQ(dragonx::ui::FormatAddressUsdValue(2.0, 3.5), std::string("$7.00"));
|
EXPECT_EQ(dragonx::ui::FormatAddressUsdValue(2.0, 3.5), std::string("$7.00"));
|
||||||
EXPECT_EQ(dragonx::ui::FormatAddressUsdValue(0.001, 2.0), std::string("$0.002000"));
|
EXPECT_EQ(dragonx::ui::FormatAddressUsdValue(0.001, 2.0), std::string("$0.002000"));
|
||||||
EXPECT_EQ(dragonx::ui::FormatAddressUsdValue(0.0, 2.0), std::string(""));
|
EXPECT_EQ(dragonx::ui::FormatAddressUsdValue(0.0, 2.0), std::string(""));
|
||||||
@@ -5774,23 +5776,20 @@ void testXmrigLiveInstall()
|
|||||||
void testPoolRegistryLookup()
|
void testPoolRegistryLookup()
|
||||||
{
|
{
|
||||||
using namespace dragonx::util;
|
using namespace dragonx::util;
|
||||||
EXPECT_EQ(knownPools().size(), static_cast<std::size_t>(2));
|
// pool.dragonx.cc was removed from the built-in defaults; pool.dragonx.is is the sole entry.
|
||||||
|
EXPECT_EQ(knownPools().size(), static_cast<std::size_t>(1));
|
||||||
|
|
||||||
// The algo follows the pool. Note pool.dragonx.cc's stratum host is us.dragonx.cc
|
// The algo follows the pool: pool.dragonx.is resolves to rx/hush regardless of the caller default.
|
||||||
// (the .cc domain is only the Cloudflare-proxied web/API host).
|
|
||||||
EXPECT_EQ(resolvePoolAlgo("us.dragonx.cc:3333", "rx/hush"), std::string("rx/dragonx"));
|
|
||||||
EXPECT_EQ(resolvePoolAlgo("pool.dragonx.is:3433", "rx/dragonx"), std::string("rx/hush"));
|
EXPECT_EQ(resolvePoolAlgo("pool.dragonx.is:3433", "rx/dragonx"), std::string("rx/hush"));
|
||||||
// Bare host (no port) still matches; scheme + path are tolerated.
|
// The former us.dragonx.cc (pool.dragonx.cc) host is now unknown -> caller's fallback algo.
|
||||||
EXPECT_EQ(resolvePoolAlgo("us.dragonx.cc", "rx/hush"), std::string("rx/dragonx"));
|
EXPECT_EQ(resolvePoolAlgo("us.dragonx.cc:3333", "rx/hush"), std::string("rx/hush"));
|
||||||
EXPECT_EQ(resolvePoolAlgo("stratum+tcp://us.dragonx.cc:3333/x", "rx/hush"),
|
|
||||||
std::string("rx/dragonx"));
|
|
||||||
// Unknown host -> fallback algo.
|
|
||||||
EXPECT_EQ(resolvePoolAlgo("my.pool.example:1234", "rx/hush"), std::string("rx/hush"));
|
EXPECT_EQ(resolvePoolAlgo("my.pool.example:1234", "rx/hush"), std::string("rx/hush"));
|
||||||
|
|
||||||
EXPECT_TRUE(findKnownPoolByUrl("us.dragonx.cc:3333") != nullptr);
|
EXPECT_TRUE(findKnownPoolByUrl("pool.dragonx.is:3433") != nullptr);
|
||||||
|
EXPECT_TRUE(findKnownPoolByUrl("us.dragonx.cc:3333") == nullptr); // removed built-in default
|
||||||
EXPECT_TRUE(findKnownPoolByUrl("unknown.host:1") == nullptr);
|
EXPECT_TRUE(findKnownPoolByUrl("unknown.host:1") == nullptr);
|
||||||
// A mismatched explicit port must NOT match a known pool.
|
// A mismatched explicit port must NOT match a known pool.
|
||||||
EXPECT_TRUE(findKnownPoolByUrl("us.dragonx.cc:9999") == nullptr);
|
EXPECT_TRUE(findKnownPoolByUrl("pool.dragonx.is:9999") == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schema-aware pool hashrate parsing (the two pools speak different APIs).
|
// Schema-aware pool hashrate parsing (the two pools speak different APIs).
|
||||||
|
|||||||
Reference in New Issue
Block a user