Compare commits
217 Commits
09e0b962c3
...
lite-v1.0.
| Author | SHA1 | Date | |
|---|---|---|---|
| fffee9f0b5 | |||
| 00ffc959e5 | |||
| b561406c23 | |||
| 5a0743a17b | |||
| a7b0770ad0 | |||
| 7d8323a622 | |||
| 320944fd18 | |||
| 6d5e0ac614 | |||
| 02554d523d | |||
| 21da9e75fc | |||
| c53b7f771e | |||
| bcee4bfe72 | |||
| e1870c3b23 | |||
| 7cf0bb8bc7 | |||
| 541a9e1fd5 | |||
| 07b8e0b2cb | |||
| de48414c65 | |||
| f0c681b0b5 | |||
| fdf2502ca8 | |||
| e393b0d847 | |||
| 3b041f20c1 | |||
| 77a0ad64b0 | |||
| d0ca2fdf52 | |||
| 9c07b6d33d | |||
| fedfe3d60c | |||
| 95ff9ce9ae | |||
| 203967411a | |||
| 3d0305a9ca | |||
| e7f38c2a45 | |||
| 3a2be661ea | |||
| 17525003c5 | |||
| 267d839d5b | |||
| 987d9b93c7 | |||
| 4c43f2e082 | |||
| 57fa6470b7 | |||
| c68889d276 | |||
| b0a4333cdf | |||
| 4471f54842 | |||
| e08121af2b | |||
| 31ebfc782e | |||
| f3776bcbe5 | |||
| 38f2aa2f8f | |||
| 40f8425d94 | |||
| 2157a30192 | |||
| bef3c0c47d | |||
| 5c570613c8 | |||
| 567732206e | |||
| 0dcc09fc5f | |||
| 82d3178119 | |||
| 7d47c6e14e | |||
| 973bc0d338 | |||
| e4c9b98ca2 | |||
| 1c38f68781 | |||
| 818c29fca7 | |||
| 0a43742897 | |||
| defe14d913 | |||
| 8b55a90102 | |||
| 0fd3d214a4 | |||
| 996e10ed02 | |||
| 2c909d35ea | |||
| 3b5db02e09 | |||
| 8d8cd337cf | |||
| acde8c0833 | |||
| a78b44246e | |||
| 63ed31d2ec | |||
| 64b27db2ff | |||
| 03fe6e077c | |||
| 125ffa8863 | |||
| 3465acc57b | |||
| 30bd0d99ff | |||
| 29fbe46cce | |||
| fc414eeed4 | |||
| fce873936e | |||
| 7351d8a06d | |||
| 76093fe82d | |||
| ef247c95ff | |||
| c291e8a587 | |||
| 06e55d6394 | |||
| 2072f70a60 | |||
| ee9ea15233 | |||
| 11de117331 | |||
| fac0245297 | |||
| 9cb91415d9 | |||
| 24c661f743 | |||
| fecc9015fb | |||
| bce69362eb | |||
| 7f46d9e2d5 | |||
| 100ed01469 | |||
| 055685a4c4 | |||
| 46f93e380b | |||
| 6f6ad89c9f | |||
| d9911a9bc9 | |||
| faf77de9ce | |||
| 7f414b3dcf | |||
| 994ddea6cd | |||
| 41e4e73e63 | |||
| 88091bd77c | |||
| 02839dc415 | |||
| 17b70a1388 | |||
| 42d59709f9 | |||
| 30bc80b2f2 | |||
| 3808b3ee82 | |||
| bb5124c1ae | |||
| e6c78da062 | |||
| 5874142186 | |||
| 928335dd6b | |||
| 63dcda0ad1 | |||
| 122db9d903 | |||
| fc509a9340 | |||
| f546d3e2b1 | |||
| 6f0f95f4fb | |||
| a7c50ff19b | |||
| e7f2d2e3c7 | |||
| 137147921c | |||
| fd0cab41f0 | |||
| 4b8d80b2fc | |||
| 923d086092 | |||
| 6ac8f66644 | |||
| 62fc202557 | |||
| f8034b843a | |||
| 731d4e04ff | |||
| 9649654c3c | |||
| e1df5ea798 | |||
| a53b13b6b6 | |||
| bc3c6037fc | |||
| a338fac208 | |||
| 9bc2a0b62a | |||
| 1b3446e43c | |||
| 19dea53ef8 | |||
| 3d2b734541 | |||
| 2d4ba89c00 | |||
| 2249bc31d5 | |||
| 1ce37aa0fa | |||
| 33317c4e78 | |||
| 03dde25339 | |||
| 3fbb64d14c | |||
| 1b0006a4af | |||
| ba13875eaf | |||
| a5d44c8aca | |||
| 1a41dec8d8 | |||
| 88e10f3e06 | |||
| d29283a9b7 | |||
| ca65aa8bf7 | |||
| dae677acd8 | |||
| 4ef09d5546 | |||
| 21be39c725 | |||
| abbdf3f4f7 | |||
| 28288d7197 | |||
| 304f65c3b4 | |||
| 04a5078133 | |||
| b6b4fdacb5 | |||
| f6024557d5 | |||
| d82821ba06 | |||
| 8290e96ec4 | |||
| 46f7da4fac | |||
| 5bd07b0505 | |||
| 90df19b8b6 | |||
| 5985f05fb2 | |||
| 60ee73bf1d | |||
| 625df8abe6 | |||
| 5dd68dc9bd | |||
| 8a30578872 | |||
| 63d5c817aa | |||
| 3662df550c | |||
| 067c96c425 | |||
| a2c4a8df73 | |||
| 822891d4ef | |||
| 22638b094c | |||
| bf81ec1702 | |||
| 56cd9b6273 | |||
| 8b9cd5bf80 | |||
| 2b191cea34 | |||
| 6ecbf835ec | |||
| a13190a6df | |||
| ffc24ee99b | |||
| 3274c72a58 | |||
| b7b32bfde8 | |||
| 0b47134112 | |||
| 828018de2b | |||
| 2a96c41e07 | |||
| c25da0d26d | |||
| 0c55ed0fe4 | |||
| 1fc3e03e6f | |||
| f428292ea4 | |||
| 46f3001360 | |||
| 8293f02e36 | |||
| b34069f57c | |||
| 4416e01f9c | |||
| 44a17f3ad0 | |||
| c9e9c9f979 | |||
| ab4dd370c0 | |||
| 675d434958 | |||
| e8055888a5 | |||
| 48eceb6593 | |||
| 4f71c18884 | |||
| 232b81f8ee | |||
| c40c252c9a | |||
| d156dddcd0 | |||
| 0e1957c5f9 | |||
| 3f95765dcc | |||
| 62c92cc862 | |||
| 3aeb847657 | |||
| 2a9d32e78b | |||
| 97a54f0e0c | |||
| 82da4af857 | |||
| bfe8b4d77d | |||
| 78c00daf92 | |||
| e82514f46a | |||
| 76708e9191 | |||
| a767702ff6 | |||
| 40e8128a30 | |||
| 326192e75e | |||
| a107fda9b8 | |||
| d1ff58c374 | |||
| 156735eb06 | |||
| 2f86bec98e | |||
| 6310f51f65 |
10
.gitignore
vendored
10
.gitignore
vendored
@@ -11,8 +11,8 @@ prebuilt-binaries/dragonxd-win/*
|
|||||||
!prebuilt-binaries/dragonxd-win/.gitkeep
|
!prebuilt-binaries/dragonxd-win/.gitkeep
|
||||||
prebuilt-binaries/dragonxd-mac/*
|
prebuilt-binaries/dragonxd-mac/*
|
||||||
!prebuilt-binaries/dragonxd-mac/.gitkeep
|
!prebuilt-binaries/dragonxd-mac/.gitkeep
|
||||||
prebuilt-binaries/xmrig-hac/*
|
prebuilt-binaries/drg-xmrig/*
|
||||||
!prebuilt-binaries/xmrig-hac/.gitkeep
|
!prebuilt-binaries/drg-xmrig/.gitkeep
|
||||||
|
|
||||||
|
|
||||||
# External sources / toolchains (created by scripts/setup.sh)
|
# External sources / toolchains (created by scripts/setup.sh)
|
||||||
@@ -33,7 +33,7 @@ imgui.ini
|
|||||||
*.bak*
|
*.bak*
|
||||||
*.params
|
*.params
|
||||||
asmap.dat
|
asmap.dat
|
||||||
/external/xmrig-hac
|
/external/drg-xmrig
|
||||||
/memory
|
/memory
|
||||||
/todo.md
|
/todo.md
|
||||||
/.github/
|
/.github/
|
||||||
@@ -54,3 +54,7 @@ third_party/silentdragonxlite/lib/vendor/
|
|||||||
|
|
||||||
# Generated by configure_file from res/ObsidianDragon.manifest.in (do not track)
|
# Generated by configure_file from res/ObsidianDragon.manifest.in (do not track)
|
||||||
res/ObsidianDragon.manifest
|
res/ObsidianDragon.manifest
|
||||||
|
|
||||||
|
# Cross-built mingw FreeType (color emoji) — regenerated by scripts/build-freetype-mingw.sh
|
||||||
|
third_party/freetype-mingw/
|
||||||
|
third_party/.freetype-mingw-build/
|
||||||
|
|||||||
@@ -79,13 +79,13 @@ The detailed milestone plan and design history (the v2 plan, backend artifact/AB
|
|||||||
|
|
||||||
## Miner updater (xmrig)
|
## Miner updater (xmrig)
|
||||||
|
|
||||||
The mining tab's pool section has an **"Update miner…"** button that downloads/verifies/installs the latest DRG-XMRig from the project Gitea (`util/XmrigUpdater` + `ui/windows/xmrig_download_dialog.h`). Flow: query `git.dragonx.is/api/v1/repos/DragonX/drg-xmrig/releases/latest` → pick the asset for this platform (`linux-x64` / `win-x64` / `macos-x86_64`; no match → "Unavailable") → libcurl download (TLS verified) → verify the archive **SHA-256** (from the release body) **and** a detached **ed25519 signature** → miniz-extract the binary (flattening the versioned subdir) into `resources::getDaemonDirectory()`. The whole archive is verified, so extracted members are trusted by transitivity (no per-member hash check). The pure, no-I/O core is split into `xmrig_updater_core.cpp` for unit tests; an env-gated (`DRAGONX_TEST_NETWORK=1`) test exercises the worker live. A **"Browse all releases…"** button (the `/releases` list, newest first, pre-releases included) lets users pin an older or pre-release build — same verify/install path via `startInstallRelease()`; the picker UI is shared with the daemon updater (`ui/windows/release_list_view.h`).
|
The mining tab's pool section has an **"Update miner…"** button that downloads/verifies/installs the latest DRG-XMRig from the project Gitea (`util/XmrigUpdater` + `ui/windows/xmrig_download_dialog.h`). Flow: query `git.dragonx.is/api/v1/repos/DragonX/drg-xmrig/releases/latest` → pick the asset for this platform (`linux-x64` / `win-x64` / `macos-x86_64`; no match → "Unavailable") → libcurl download (TLS verified) → verify the archive **SHA-256** (from the release body) **and** a detached **ed25519 signature** → miniz-extract the binary (flattening the versioned subdir) into `resources::getDaemonDirectory()`. The whole archive is verified, so extracted members are trusted by transitivity (no per-member hash check). The pure, no-I/O core is split into `xmrig_updater_core.cpp` for unit tests; an env-gated (`DRAGONX_TEST_NETWORK=1`) test exercises the worker live. The dialog is a two-pane version picker (every `/releases` entry on the left, newest first, pre-releases included) so users can pin an older or pre-release build — same verify/install path via `startInstallRelease()`. It shares only the `ReleaseRow` row model with the daemon updater (`ui/windows/release_list_view.h`); each dialog renders its own tactile Material list.
|
||||||
|
|
||||||
**Signature verification is enforced** (`kXmrigRequireSignature = true` in `src/util/xmrig_updater.h`), checked against the public key pinned in `kXmrigSignaturePublicKeyBase64`. **Consequence for releases:** every `drg-xmrig` release MUST ship a detached signature per archive or the in-app updater refuses it. To cut a release: build the archives, then `scripts/sign-xmrig-release.sh sign <secret.key> <archive.zip>...` (OpenSSL-based, no extra deps) and upload each `<archive>.sig` as a release asset alongside its `.zip`. The signing **secret key must stay offline** (it is gitignored: `*.ed25519.key`); only its base64 public key is pinned in the source. To rotate the key, regenerate (`scripts/sign-xmrig-release.sh keygen`) and update `kXmrigSignaturePublicKeyBase64`. An emergency env override is not provided — disabling verification means setting `kXmrigSignaturePublicKeyBase64` empty (and rebuilding).
|
**Signature verification is enforced** (`kXmrigRequireSignature = true` in `src/util/xmrig_updater.h`), checked against the public key pinned in `kXmrigSignaturePublicKeyBase64`. **Consequence for releases:** every `drg-xmrig` release MUST ship a detached signature per archive or the in-app updater refuses it. To cut a release: build the archives, then `scripts/sign-xmrig-release.sh sign <secret.key> <archive.zip>...` (OpenSSL-based, no extra deps) and upload each `<archive>.sig` as a release asset alongside its `.zip`. The signing **secret key must stay offline** (it is gitignored: `*.ed25519.key`); only its base64 public key is pinned in the source. To rotate the key, regenerate (`scripts/sign-xmrig-release.sh keygen`) and update `kXmrigSignaturePublicKeyBase64`. An emergency env override is not provided — disabling verification means setting `kXmrigSignaturePublicKeyBase64` empty (and rebuilding).
|
||||||
|
|
||||||
## Daemon updater (dragonxd)
|
## Daemon updater (dragonxd)
|
||||||
|
|
||||||
Settings → **NODE & SECURITY → DAEMON BINARY** has a **"Check for updates…"** button that downloads/verifies/installs the latest **dragonxd full node** from the project Gitea — the full-node sibling of the xmrig updater (`util/DaemonUpdater` + `ui/windows/daemon_download_dialog.h`, pure no-I/O core in `daemon_updater_core.cpp`; gated full-node-only via `supportsFullNodeLifecycleActions()`). Flow: query `git.dragonx.is/api/v1/repos/DragonX/dragonx/releases/latest` → pick the archive for this platform (`linux-amd64` / `macos` / `win64`; no match → "Unavailable") → libcurl download (TLS verified) → verify the archive **SHA-256** (parsed from the release body's markdown **checksum table**, not xmrig's `<hash> <name>` lines) **and** a detached **ed25519 signature** → miniz-extract the three executables (`dragonxd`/`dragonx-cli`/`dragonx-tx`, flattening the versioned subdir) into `resources::getDaemonDirectory()`. The archive also bundles Sapling params/asmap, which the updater deliberately leaves to the wallet's own resource extraction. Install is **atomic and safe while the node runs** (POSIX `rename()` replaces the in-use binary; Windows moves the locked `.exe` aside to `.old`); the new binary takes effect on the **next daemon start**, so the Done screen offers **"Restart daemon now"** (`App::restartDaemon()`). A **"Browse all releases…"** button (shared `release_list_view.h` picker) lets users pin a specific/older/pre-release node build via `startInstallRelease()` — with a downgrade caution, since an older binary may not match current chain data.
|
Settings → **NODE & SECURITY → DAEMON BINARY** has a **"Check for updates…"** button that downloads/verifies/installs the latest **dragonxd full node** from the project Gitea — the full-node sibling of the xmrig updater (`util/DaemonUpdater` + `ui/windows/daemon_download_dialog.h`, pure no-I/O core in `daemon_updater_core.cpp`; gated full-node-only via `supportsFullNodeLifecycleActions()`). Flow: query `git.dragonx.is/api/v1/repos/DragonX/dragonx/releases/latest` → pick the archive for this platform (`linux-amd64` / `macos` / `win64`; no match → "Unavailable") → libcurl download (TLS verified) → verify the archive **SHA-256** (parsed from the release body's markdown **checksum table**, not xmrig's `<hash> <name>` lines) **and** a detached **ed25519 signature** → miniz-extract the three executables (`dragonxd`/`dragonx-cli`/`dragonx-tx`, flattening the versioned subdir) into `resources::getDaemonDirectory()`. The archive also bundles Sapling params/asmap, which the updater deliberately leaves to the wallet's own resource extraction. Install is **atomic and safe while the node runs** (POSIX `rename()` replaces the in-use binary; Windows moves the locked `.exe` aside to `.old`); the new binary takes effect on the **next daemon start**, so the Done screen offers **"Restart daemon now"** (`App::restartDaemon()`). The dialog is a two-pane version picker (every `/releases` entry on the left) so users can pin a specific/older/pre-release node build via `startInstallRelease()` — with a downgrade caution, since an older binary may not match current chain data. It shares only the `ReleaseRow` row model (`ui/windows/release_list_view.h`) with the miner updater; each renders its own tactile Material list.
|
||||||
|
|
||||||
**Signature verification is enforced** (`kDaemonRequireSignature = true` in `src/util/daemon_updater.h`), checked against `kDaemonSignaturePublicKeyBase64`. **Consequence for releases:** every `dragonx` release MUST ship a detached `<archive>.sig` per platform archive or the in-app updater refuses it (as of v1.0.2 the releases publish SHA-256 but **no** signatures yet — sign + upload them to enable in-app updates). To cut a release: `scripts/sign-daemon-release.sh sign <secret.key> dragonx-<ver>-{linux-amd64,macos,win64}.zip` (OpenSSL-based) and upload each `.sig` next to its `.zip`. The signing **secret key stays offline** (gitignored `*.ed25519.key`; this repo's is `dragonx-daemon.ed25519.key`); only the base64 public key is pinned. To rotate: `scripts/sign-daemon-release.sh keygen` and update `kDaemonSignaturePublicKeyBase64`. The generic SHA-256 / ed25519 primitives are shared with the miner updater (`util::sha256Hex` / `util::verifyXmrigSignature`).
|
**Signature verification is enforced** (`kDaemonRequireSignature = true` in `src/util/daemon_updater.h`), checked against `kDaemonSignaturePublicKeyBase64`. **Consequence for releases:** every `dragonx` release MUST ship a detached `<archive>.sig` per platform archive or the in-app updater refuses it (as of v1.0.2 the releases publish SHA-256 but **no** signatures yet — sign + upload them to enable in-app updates). To cut a release: `scripts/sign-daemon-release.sh sign <secret.key> dragonx-<ver>-{linux-amd64,macos,win64}.zip` (OpenSSL-based) and upload each `.sig` next to its `.zip`. The signing **secret key stays offline** (gitignored `*.ed25519.key`; this repo's is `dragonx-daemon.ed25519.key`); only the base64 public key is pinned. To rotate: `scripts/sign-daemon-release.sh keygen` and update `kDaemonSignaturePublicKeyBase64`. The generic SHA-256 / ed25519 primitives are shared with the miner updater (`util::sha256Hex` / `util::verifyXmrigSignature`).
|
||||||
|
|
||||||
|
|||||||
119
CMakeLists.txt
119
CMakeLists.txt
@@ -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,36 +125,24 @@ 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
|
||||||
IMPORTED_LOCATION "${DRAGONX_LITE_BACKEND_LIBRARY}"
|
IMPORTED_LOCATION "${DRAGONX_LITE_BACKEND_LIBRARY}"
|
||||||
)
|
)
|
||||||
|
if(APPLE)
|
||||||
|
# The Rust backend's TLS stack (security-framework / core-foundation crates)
|
||||||
|
# references Secure Transport (SSL*) + CoreFoundation symbols. Link the frameworks
|
||||||
|
# that provide them, or the static lib leaves ~130 symbols undefined at link time.
|
||||||
|
set_property(TARGET dragonx_lite_backend APPEND PROPERTY
|
||||||
|
INTERFACE_LINK_LIBRARIES "-framework Security" "-framework CoreFoundation")
|
||||||
|
endif()
|
||||||
if(DRAGONX_LITE_BACKEND_INCLUDE_DIR)
|
if(DRAGONX_LITE_BACKEND_INCLUDE_DIR)
|
||||||
if(NOT IS_DIRECTORY "${DRAGONX_LITE_BACKEND_INCLUDE_DIR}")
|
if(NOT IS_DIRECTORY "${DRAGONX_LITE_BACKEND_INCLUDE_DIR}")
|
||||||
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_INCLUDE_DIR does not exist: ${DRAGONX_LITE_BACKEND_INCLUDE_DIR}")
|
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_INCLUDE_DIR does not exist: ${DRAGONX_LITE_BACKEND_INCLUDE_DIR}")
|
||||||
@@ -295,6 +282,38 @@ else()
|
|||||||
set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR})
|
set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# libwebp - WebP decode (still + animated via WebPAnimDecoder). Built from source, static, decode-only
|
||||||
|
# so Linux / mingw-Windows / macOS-osxcross all build it identically (the mingw/osx sysroots have no
|
||||||
|
# webp). Encode tools are disabled to avoid pulling in libpng/zlib that the cross sysroots lack.
|
||||||
|
message(STATUS "Fetching libwebp (decode-only, static)...")
|
||||||
|
FetchContent_Declare(
|
||||||
|
libwebp
|
||||||
|
GIT_REPOSITORY https://github.com/webmproject/libwebp.git
|
||||||
|
GIT_TAG v1.4.0
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
# libwebp's cpu.cmake applies -mno-sse2/-mno-sse4.1 to its scalar reference DSP
|
||||||
|
# files when it can't probe SSE support. Under a macOS universal build
|
||||||
|
# (-arch arm64;x86_64) that probe fails, so the flags land on the x86_64 slice,
|
||||||
|
# where -mno-sse2 disables _Float16 and breaks the SDK's <math.h>. Neutralize
|
||||||
|
# those disable flags (SSE2 is x86_64 baseline). Portable + idempotent; a no-op
|
||||||
|
# for single-arch Linux/Windows/x86_64 builds. See cmake/patch-libwebp-simd.cmake.
|
||||||
|
PATCH_COMMAND ${CMAKE_COMMAND}
|
||||||
|
-DCPU_CMAKE=<SOURCE_DIR>/cmake/cpu.cmake
|
||||||
|
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/patch-libwebp-simd.cmake
|
||||||
|
)
|
||||||
|
set(WEBP_LINK_STATIC ON CACHE BOOL "" FORCE)
|
||||||
|
set(WEBP_BUILD_ANIM_UTILS OFF CACHE BOOL "" FORCE)
|
||||||
|
set(WEBP_BUILD_CWEBP OFF CACHE BOOL "" FORCE)
|
||||||
|
set(WEBP_BUILD_DWEBP OFF CACHE BOOL "" FORCE)
|
||||||
|
set(WEBP_BUILD_GIF2WEBP OFF CACHE BOOL "" FORCE)
|
||||||
|
set(WEBP_BUILD_IMG2WEBP OFF CACHE BOOL "" FORCE)
|
||||||
|
set(WEBP_BUILD_VWEBP OFF CACHE BOOL "" FORCE)
|
||||||
|
set(WEBP_BUILD_WEBPINFO OFF CACHE BOOL "" FORCE)
|
||||||
|
set(WEBP_BUILD_LIBWEBPMUX OFF CACHE BOOL "" FORCE)
|
||||||
|
set(WEBP_BUILD_WEBPMUX OFF CACHE BOOL "" FORCE)
|
||||||
|
set(WEBP_BUILD_EXTRAS OFF CACHE BOOL "" FORCE)
|
||||||
|
FetchContent_MakeAvailable(libwebp)
|
||||||
|
|
||||||
# libsodium - platform-specific
|
# libsodium - platform-specific
|
||||||
# Search order per platform:
|
# Search order per platform:
|
||||||
# 1. Local pre-built in libs/libsodium{-mac,-win}/ (downloaded by scripts/fetch-libsodium.sh)
|
# 1. Local pre-built in libs/libsodium{-mac,-win}/ (downloaded by scripts/fetch-libsodium.sh)
|
||||||
@@ -383,6 +402,35 @@ else()
|
|||||||
list(APPEND IMGUI_HEADERS ${IMGUI_DIR}/backends/imgui_impl_opengl3.h)
|
list(APPEND IMGUI_HEADERS ${IMGUI_DIR}/backends/imgui_impl_opengl3.h)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Optional FreeType font loader — enables color-emoji rendering (COLR/CPAL Twemoji) when the chat
|
||||||
|
# "color emoji" setting is on; otherwise the wallet falls back to the monochrome emoji subset.
|
||||||
|
# - Native Linux/macOS: use the system FreeType via find_package.
|
||||||
|
# - Windows (mingw cross): the toolchain ships no FreeType, so build.sh --win-release cross-builds a
|
||||||
|
# static one (scripts/build-freetype-mingw.sh) and passes -DDRAGONX_MINGW_FREETYPE_PREFIX here.
|
||||||
|
# - Other cross builds (osxcross) without FreeType: silently fall back to monochrome.
|
||||||
|
set(DRAGONX_FREETYPE OFF)
|
||||||
|
set(DRAGONX_FREETYPE_LIB "")
|
||||||
|
set(DRAGONX_FREETYPE_INC "")
|
||||||
|
if(DEFINED DRAGONX_MINGW_FREETYPE_PREFIX AND EXISTS "${DRAGONX_MINGW_FREETYPE_PREFIX}/lib/libfreetype.a")
|
||||||
|
set(DRAGONX_FREETYPE ON)
|
||||||
|
set(DRAGONX_FREETYPE_LIB "${DRAGONX_MINGW_FREETYPE_PREFIX}/lib/libfreetype.a")
|
||||||
|
set(DRAGONX_FREETYPE_INC "${DRAGONX_MINGW_FREETYPE_PREFIX}/include/freetype2")
|
||||||
|
message(STATUS "FreeType (mingw cross-built) found — chat color emoji enabled")
|
||||||
|
elseif(NOT CMAKE_CROSSCOMPILING)
|
||||||
|
find_package(Freetype QUIET)
|
||||||
|
if(FREETYPE_FOUND)
|
||||||
|
set(DRAGONX_FREETYPE ON)
|
||||||
|
set(DRAGONX_FREETYPE_LIB Freetype::Freetype) # imported target carries include dirs
|
||||||
|
message(STATUS "FreeType ${FREETYPE_VERSION_STRING} found — chat color emoji enabled")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(DRAGONX_FREETYPE)
|
||||||
|
list(APPEND IMGUI_SOURCES ${IMGUI_DIR}/misc/freetype/imgui_freetype.cpp)
|
||||||
|
list(APPEND IMGUI_HEADERS ${IMGUI_DIR}/misc/freetype/imgui_freetype.h)
|
||||||
|
else()
|
||||||
|
message(STATUS "FreeType not found — chat color emoji falls back to monochrome")
|
||||||
|
endif()
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# QR Code library (bundled)
|
# QR Code library (bundled)
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -503,6 +551,7 @@ set(APP_SOURCES
|
|||||||
src/util/platform.cpp
|
src/util/platform.cpp
|
||||||
src/util/payment_uri.cpp
|
src/util/payment_uri.cpp
|
||||||
src/util/texture_loader.cpp
|
src/util/texture_loader.cpp
|
||||||
|
src/util/svg_texture.cpp
|
||||||
src/util/noise_texture.cpp
|
src/util/noise_texture.cpp
|
||||||
src/daemon/embedded_daemon.cpp
|
src/daemon/embedded_daemon.cpp
|
||||||
src/daemon/seed_wallet_creator.cpp
|
src/daemon/seed_wallet_creator.cpp
|
||||||
@@ -707,7 +756,9 @@ ${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-Medium.ttf;\
|
|||||||
${CMAKE_SOURCE_DIR}/res/fonts/UbuntuMono-R.ttf;\
|
${CMAKE_SOURCE_DIR}/res/fonts/UbuntuMono-R.ttf;\
|
||||||
${CMAKE_SOURCE_DIR}/res/fonts/MaterialIcons-Regular.ttf;\
|
${CMAKE_SOURCE_DIR}/res/fonts/MaterialIcons-Regular.ttf;\
|
||||||
${CMAKE_SOURCE_DIR}/res/fonts/MaterialDesignIcons-Pickaxe-Subset.ttf;\
|
${CMAKE_SOURCE_DIR}/res/fonts/MaterialDesignIcons-Pickaxe-Subset.ttf;\
|
||||||
${CMAKE_SOURCE_DIR}/res/fonts/NotoSansCJK-Subset.ttf"
|
${CMAKE_SOURCE_DIR}/res/fonts/NotoSansCJK-Subset.ttf;\
|
||||||
|
${CMAKE_SOURCE_DIR}/res/fonts/NotoEmoji-Subset.ttf;\
|
||||||
|
${CMAKE_SOURCE_DIR}/res/fonts/TwemojiMozilla-Color.ttf"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(ObsidianDragon
|
add_executable(ObsidianDragon
|
||||||
@@ -735,6 +786,7 @@ target_include_directories(ObsidianDragon PRIVATE
|
|||||||
${GLAD_INCLUDE}
|
${GLAD_INCLUDE}
|
||||||
${CURL_INCLUDE_DIRS}
|
${CURL_INCLUDE_DIRS}
|
||||||
${MINIZ_DIR}
|
${MINIZ_DIR}
|
||||||
|
${libwebp_SOURCE_DIR}/src # <webp/decode.h>, <webp/demux.h> (FetchContent build tree)
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(ObsidianDragon PRIVATE
|
target_link_libraries(ObsidianDragon PRIVATE
|
||||||
@@ -744,6 +796,8 @@ target_link_libraries(ObsidianDragon PRIVATE
|
|||||||
sqlite3_amalgamation
|
sqlite3_amalgamation
|
||||||
${CURL_LIBRARIES}
|
${CURL_LIBRARIES}
|
||||||
${SODIUM_LIBRARY}
|
${SODIUM_LIBRARY}
|
||||||
|
webp
|
||||||
|
webpdemux # WebPAnimDecoder (animated WebP); transitively pulls in webp + sharpyuv
|
||||||
)
|
)
|
||||||
|
|
||||||
if(DRAGONX_LITE_BACKEND_READY)
|
if(DRAGONX_LITE_BACKEND_READY)
|
||||||
@@ -837,6 +891,15 @@ else()
|
|||||||
target_compile_definitions(ObsidianDragon PRIVATE DRAGONX_HAS_GLAD)
|
target_compile_definitions(ObsidianDragon PRIVATE DRAGONX_HAS_GLAD)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Color-emoji font loader (FreeType) — linked + flagged only when found (see DRAGONX_FREETYPE above).
|
||||||
|
if(DRAGONX_FREETYPE)
|
||||||
|
target_link_libraries(ObsidianDragon PRIVATE ${DRAGONX_FREETYPE_LIB})
|
||||||
|
if(DRAGONX_FREETYPE_INC)
|
||||||
|
target_include_directories(ObsidianDragon PRIVATE ${DRAGONX_FREETYPE_INC})
|
||||||
|
endif()
|
||||||
|
target_compile_definitions(ObsidianDragon PRIVATE DRAGONX_HAVE_FREETYPE)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(HushChatFixtureCheck
|
add_executable(HushChatFixtureCheck
|
||||||
tools/hushchat_fixture_check.cpp
|
tools/hushchat_fixture_check.cpp
|
||||||
src/chat/chat_protocol.cpp
|
src/chat/chat_protocol.cpp
|
||||||
@@ -1137,5 +1200,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 "")
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ Download linux and windows binaries of latest releases and place in binary direc
|
|||||||
- prebuilt-binaries/dragonxd-win/
|
- prebuilt-binaries/dragonxd-win/
|
||||||
- prebuilt-binaries/dragonxd-mac/
|
- prebuilt-binaries/dragonxd-mac/
|
||||||
|
|
||||||
**xmrig HAC fork** (https://git.dragonx.is/dragonx/xmrig-hac):
|
**DRG-XMRig fork** (https://git.dragonx.is/DragonX/drg-xmrig):
|
||||||
- prebuilt-binaries/xmrig-hac/
|
- prebuilt-binaries/drg-xmrig/
|
||||||
|
|
||||||
|
|
||||||
## Build Steps
|
## Build Steps
|
||||||
|
|||||||
103
build.sh
103
build.sh
@@ -131,7 +131,7 @@ fi
|
|||||||
# truth): the full-node app uses project() VERSION + DRAGONX_VERSION_SUFFIX; ObsidianDragonLite uses
|
# truth): the full-node app uses project() VERSION + DRAGONX_VERSION_SUFFIX; ObsidianDragonLite uses
|
||||||
# DRAGONX_LITE_VERSION + DRAGONX_LITE_VERSION_SUFFIX.
|
# DRAGONX_LITE_VERSION + DRAGONX_LITE_VERSION_SUFFIX.
|
||||||
_cml="$SCRIPT_DIR/CMakeLists.txt"
|
_cml="$SCRIPT_DIR/CMakeLists.txt"
|
||||||
_full_ver=$(sed -n 's/^[[:space:]]*VERSION[[:space:]]\+\([0-9][0-9.]*\).*/\1/p' "$_cml" | head -1)
|
_full_ver=$(sed -n 's/^[[:space:]]*VERSION[[:space:]][[:space:]]*\([0-9][0-9.]*\).*/\1/p' "$_cml" | head -1)
|
||||||
_full_suffix=$(sed -n 's/^set(DRAGONX_VERSION_SUFFIX[[:space:]]*"\([^"]*\)").*/\1/p' "$_cml" | head -1)
|
_full_suffix=$(sed -n 's/^set(DRAGONX_VERSION_SUFFIX[[:space:]]*"\([^"]*\)").*/\1/p' "$_cml" | head -1)
|
||||||
_lite_ver=$(sed -n 's/^set(DRAGONX_LITE_VERSION[[:space:]]*"\([^"]*\)").*/\1/p' "$_cml" | head -1)
|
_lite_ver=$(sed -n 's/^set(DRAGONX_LITE_VERSION[[:space:]]*"\([^"]*\)").*/\1/p' "$_cml" | head -1)
|
||||||
_lite_suffix=$(sed -n 's/^set(DRAGONX_LITE_VERSION_SUFFIX[[:space:]]*"\([^"]*\)").*/\1/p' "$_cml" | head -1)
|
_lite_suffix=$(sed -n 's/^set(DRAGONX_LITE_VERSION_SUFFIX[[:space:]]*"\([^"]*\)").*/\1/p' "$_cml" | head -1)
|
||||||
@@ -386,7 +386,7 @@ build_release_linux() {
|
|||||||
[[ -f bin/sapling-output.params ]] && cp bin/sapling-output.params "$dist_dir/"
|
[[ -f bin/sapling-output.params ]] && cp bin/sapling-output.params "$dist_dir/"
|
||||||
fi
|
fi
|
||||||
# Bundle xmrig for mining support
|
# Bundle xmrig for mining support
|
||||||
local XMRIG_LINUX="$SCRIPT_DIR/prebuilt-binaries/xmrig-hac/xmrig"
|
local XMRIG_LINUX="$SCRIPT_DIR/prebuilt-binaries/drg-xmrig/xmrig"
|
||||||
[[ -f "$XMRIG_LINUX" ]] && { cp "$XMRIG_LINUX" "$dist_dir/"; chmod +x "$dist_dir/xmrig"; info "Bundled xmrig"; } || warn "xmrig not found — mining unavailable in zip"
|
[[ -f "$XMRIG_LINUX" ]] && { cp "$XMRIG_LINUX" "$dist_dir/"; chmod +x "$dist_dir/xmrig"; info "Bundled xmrig"; } || warn "xmrig not found — mining unavailable in zip"
|
||||||
cp -r bin/res "$dist_dir/" 2>/dev/null || true
|
cp -r bin/res "$dist_dir/" 2>/dev/null || true
|
||||||
|
|
||||||
@@ -419,7 +419,7 @@ build_release_linux() {
|
|||||||
[[ -f bin/sapling-output.params ]] && cp bin/sapling-output.params "$APPDIR/usr/bin/"
|
[[ -f bin/sapling-output.params ]] && cp bin/sapling-output.params "$APPDIR/usr/bin/"
|
||||||
fi
|
fi
|
||||||
# Bundle xmrig for mining support
|
# Bundle xmrig for mining support
|
||||||
local XMRIG_LINUX_AI="$SCRIPT_DIR/prebuilt-binaries/xmrig-hac/xmrig"
|
local XMRIG_LINUX_AI="$SCRIPT_DIR/prebuilt-binaries/drg-xmrig/xmrig"
|
||||||
[[ -f "$XMRIG_LINUX_AI" ]] && { cp "$XMRIG_LINUX_AI" "$APPDIR/usr/bin/"; chmod +x "$APPDIR/usr/bin/xmrig"; }
|
[[ -f "$XMRIG_LINUX_AI" ]] && { cp "$XMRIG_LINUX_AI" "$APPDIR/usr/bin/"; chmod +x "$APPDIR/usr/bin/xmrig"; }
|
||||||
|
|
||||||
# Desktop entry
|
# Desktop entry
|
||||||
@@ -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
|
||||||
@@ -628,8 +638,8 @@ HDR
|
|||||||
info "Lite mode: skipping embedded daemon binaries"
|
info "Lite mode: skipping embedded daemon binaries"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── xmrig binary (from prebuilt-binaries/xmrig-hac/) ────────────────
|
# ── xmrig binary (from prebuilt-binaries/drg-xmrig/) ────────────────
|
||||||
local XMRIG_DIR="$SCRIPT_DIR/prebuilt-binaries/xmrig-hac"
|
local XMRIG_DIR="$SCRIPT_DIR/prebuilt-binaries/drg-xmrig"
|
||||||
# The published DRG-XMRig archives ship the binary inside a versioned subdir, not as a flat
|
# The published DRG-XMRig archives ship the binary inside a versioned subdir, not as a flat
|
||||||
# xmrig.exe. Extract it from the matching win-x64 zip if it isn't already staged — otherwise
|
# xmrig.exe. Extract it from the matching win-x64 zip if it isn't already staged — otherwise
|
||||||
# the embed below never fires (HAS_EMBEDDED_XMRIG stays undefined) and the wallet ships with
|
# the embed below never fires (HAS_EMBEDDED_XMRIG stays undefined) and the wallet ships with
|
||||||
@@ -725,12 +735,27 @@ HDR
|
|||||||
"$SCRIPT_DIR/scripts/fetch-libsodium.sh" --win
|
"$SCRIPT_DIR/scripts/fetch-libsodium.sh" --win
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── FreeType for Windows (color-emoji rendering) ───────────────────────
|
||||||
|
# The mingw toolchain ships no FreeType; cross-build a minimal static one (COLR/CPAL, no external
|
||||||
|
# deps). Failure is non-fatal — the wallet just falls back to monochrome emoji.
|
||||||
|
local FT_MINGW_PREFIX="$SCRIPT_DIR/third_party/freetype-mingw"
|
||||||
|
if [[ ! -f "$FT_MINGW_PREFIX/lib/libfreetype.a" ]]; then
|
||||||
|
info "Cross-building FreeType for Windows (color emoji) ..."
|
||||||
|
"$SCRIPT_DIR/scripts/build-freetype-mingw.sh" "$FT_MINGW_PREFIX" \
|
||||||
|
|| warn "FreeType cross-build failed — Windows build will use monochrome emoji"
|
||||||
|
fi
|
||||||
|
local FT_CMAKE_ARG=()
|
||||||
|
if [[ -f "$FT_MINGW_PREFIX/lib/libfreetype.a" ]]; then
|
||||||
|
FT_CMAKE_ARG=(-DDRAGONX_MINGW_FREETYPE_PREFIX="$FT_MINGW_PREFIX")
|
||||||
|
fi
|
||||||
|
|
||||||
# ── CMake + build ────────────────────────────────────────────────────────
|
# ── CMake + build ────────────────────────────────────────────────────────
|
||||||
info "Configuring (cross-compile) ..."
|
info "Configuring (cross-compile) ..."
|
||||||
cmake "$SCRIPT_DIR" \
|
cmake "$SCRIPT_DIR" \
|
||||||
-DCMAKE_TOOLCHAIN_FILE="$bd/mingw-toolchain.cmake" \
|
-DCMAKE_TOOLCHAIN_FILE="$bd/mingw-toolchain.cmake" \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DDRAGONX_USE_SYSTEM_SDL3=OFF \
|
-DDRAGONX_USE_SYSTEM_SDL3=OFF \
|
||||||
|
"${FT_CMAKE_ARG[@]}" \
|
||||||
"${CMAKE_LITE_ARGS[@]}"
|
"${CMAKE_LITE_ARGS[@]}"
|
||||||
|
|
||||||
info "Building with $JOBS jobs ..."
|
info "Building with $JOBS jobs ..."
|
||||||
@@ -766,7 +791,7 @@ HDR
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Bundle xmrig for mining support
|
# Bundle xmrig for mining support
|
||||||
local XMRIG_WIN="$SCRIPT_DIR/prebuilt-binaries/xmrig-hac/xmrig.exe"
|
local XMRIG_WIN="$SCRIPT_DIR/prebuilt-binaries/drg-xmrig/xmrig.exe"
|
||||||
[[ -f "$XMRIG_WIN" ]] && { cp "$XMRIG_WIN" "$dist_dir/"; info "Bundled xmrig.exe"; } || warn "xmrig.exe not found — mining unavailable in zip"
|
[[ -f "$XMRIG_WIN" ]] && { cp "$XMRIG_WIN" "$dist_dir/"; info "Bundled xmrig.exe"; } || warn "xmrig.exe not found — mining unavailable in zip"
|
||||||
|
|
||||||
cp -r bin/res "$dist_dir/" 2>/dev/null || true
|
cp -r bin/res "$dist_dir/" 2>/dev/null || true
|
||||||
@@ -876,8 +901,26 @@ build_release_mac() {
|
|||||||
fi
|
fi
|
||||||
info "macOS cross-compiler: $OSXCROSS_CXX (arch: $MAC_ARCH)"
|
info "macOS cross-compiler: $OSXCROSS_CXX (arch: $MAC_ARCH)"
|
||||||
else
|
else
|
||||||
# Native macOS: build universal binary (arm64 + x86_64)
|
# Native macOS: build universal (arm64 + x86_64) by default. Override with
|
||||||
|
# DRAGONX_MAC_ARCHS (e.g. "x86_64").
|
||||||
|
MAC_ARCHS="${DRAGONX_MAC_ARCHS:-arm64;x86_64}"
|
||||||
|
# When linking the real lite backend, the app can only include architectures
|
||||||
|
# the backend static library actually provides. Its pinned ring 0.16.11 has no
|
||||||
|
# Apple-Silicon assembly, so that artifact is x86_64-only — constrain the app
|
||||||
|
# arch to the backend's (unless the user explicitly forced DRAGONX_MAC_ARCHS),
|
||||||
|
# otherwise the arm64 slice fails to link.
|
||||||
|
if $DO_LITE_BACKEND && [[ -z "${DRAGONX_MAC_ARCHS:-}" && -n "${lb_lib:-}" ]] && command -v lipo &>/dev/null; then
|
||||||
|
local _backend_archs; _backend_archs=$(lipo -archs "$lb_lib" 2>/dev/null | tr ' ' ';')
|
||||||
|
if [[ -n "$_backend_archs" && "$_backend_archs" != "$MAC_ARCHS" ]]; then
|
||||||
|
warn "Lite backend provides only [$_backend_archs] — building the app for that instead of universal."
|
||||||
|
MAC_ARCHS="$_backend_archs"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [[ "$MAC_ARCHS" == *";"* || "$MAC_ARCHS" == *","* ]]; then
|
||||||
MAC_ARCH="universal"
|
MAC_ARCH="universal"
|
||||||
|
else
|
||||||
|
MAC_ARCH="$MAC_ARCHS"
|
||||||
|
fi
|
||||||
export MACOSX_DEPLOYMENT_TARGET="11.0"
|
export MACOSX_DEPLOYMENT_TARGET="11.0"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -965,7 +1008,7 @@ TOOLCHAIN
|
|||||||
need_sodium=true
|
need_sodium=true
|
||||||
elif [[ -f "$SCRIPT_DIR/libs/libsodium/lib/libsodium.a" ]]; then
|
elif [[ -f "$SCRIPT_DIR/libs/libsodium/lib/libsodium.a" ]]; then
|
||||||
# Rebuild if existing lib is not universal (single-arch won't link)
|
# Rebuild if existing lib is not universal (single-arch won't link)
|
||||||
if ! lipo -info "$SCRIPT_DIR/libs/libsodium/lib/libsodium.a" 2>/dev/null | grep -q "arm64.*x86_64\|x86_64.*arm64"; then
|
if ! lipo -info "$SCRIPT_DIR/libs/libsodium/lib/libsodium.a" 2>/dev/null | grep -Eq "arm64.*x86_64|x86_64.*arm64"; then
|
||||||
info "Existing libsodium is not universal — rebuilding ..."
|
info "Existing libsodium is not universal — rebuilding ..."
|
||||||
rm -rf "$SCRIPT_DIR/libs/libsodium"
|
rm -rf "$SCRIPT_DIR/libs/libsodium"
|
||||||
need_sodium=true
|
need_sodium=true
|
||||||
@@ -976,13 +1019,13 @@ TOOLCHAIN
|
|||||||
"$SCRIPT_DIR/scripts/fetch-libsodium.sh"
|
"$SCRIPT_DIR/scripts/fetch-libsodium.sh"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
info "Configuring (native universal arm64+x86_64) ..."
|
info "Configuring (native macOS, arch: $MAC_ARCHS) ..."
|
||||||
cmake "$SCRIPT_DIR" \
|
cmake "$SCRIPT_DIR" \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG" \
|
-DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG" \
|
||||||
-DDRAGONX_USE_SYSTEM_SDL3=OFF \
|
-DDRAGONX_USE_SYSTEM_SDL3=OFF \
|
||||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
|
||||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \
|
-DCMAKE_OSX_ARCHITECTURES="$MAC_ARCHS" \
|
||||||
"${CMAKE_LITE_ARGS[@]}"
|
"${CMAKE_LITE_ARGS[@]}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1012,8 +1055,12 @@ TOOLCHAIN
|
|||||||
info "Binary: $(du -h "bin/${APP_BASENAME}" | cut -f1)"
|
info "Binary: $(du -h "bin/${APP_BASENAME}" | cut -f1)"
|
||||||
|
|
||||||
# ── Create .app bundle ───────────────────────────────────────────────────
|
# ── Create .app bundle ───────────────────────────────────────────────────
|
||||||
rm -rf "$out"
|
|
||||||
mkdir -p "$out"
|
mkdir -p "$out"
|
||||||
|
# Clean only THIS variant's prior artifacts so full-node and lite releases can
|
||||||
|
# coexist in release/mac/ (Linux/Windows scope their cleanup the same way). The
|
||||||
|
# "ObsidianDragon-" glob never matches "ObsidianDragonLite-" (and vice versa),
|
||||||
|
# and the ".app" names are exact.
|
||||||
|
rm -rf "$out/${APP_BASENAME}.app" "$out/${APP_BASENAME}-"*.app.zip "$out/${APP_BASENAME}-"*.dmg
|
||||||
|
|
||||||
local APP="$out/${APP_BASENAME}.app"
|
local APP="$out/${APP_BASENAME}.app"
|
||||||
local CONTENTS="$APP/Contents"
|
local CONTENTS="$APP/Contents"
|
||||||
@@ -1063,8 +1110,8 @@ TOOLCHAIN
|
|||||||
info "Lite mode: skipping macOS daemon and Sapling/asmap bundling"
|
info "Lite mode: skipping macOS daemon and Sapling/asmap bundling"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# xmrig binary (from prebuilt-binaries/xmrig-hac/)
|
# xmrig binary (from prebuilt-binaries/drg-xmrig/)
|
||||||
local XMRIG_MAC="$SCRIPT_DIR/prebuilt-binaries/xmrig-hac/xmrig"
|
local XMRIG_MAC="$SCRIPT_DIR/prebuilt-binaries/drg-xmrig/xmrig"
|
||||||
if [[ -f "$XMRIG_MAC" ]]; then
|
if [[ -f "$XMRIG_MAC" ]]; then
|
||||||
cp "$XMRIG_MAC" "$MACOS/xmrig"
|
cp "$XMRIG_MAC" "$MACOS/xmrig"
|
||||||
chmod +x "$MACOS/xmrig"
|
chmod +x "$MACOS/xmrig"
|
||||||
@@ -1213,8 +1260,10 @@ PLIST
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Create DMG ───────────────────────────────────────────────────────────
|
# ── Create DMG ───────────────────────────────────────────────────────────
|
||||||
local DMG_BASENAME="DragonX_Wallet"
|
# DMG filename matches the app bundle name (ObsidianDragon / ObsidianDragonLite).
|
||||||
$DO_LITE && DMG_BASENAME="DragonX_Wallet_Lite"
|
# The mounted volume + CFBundleName keep the "DragonX Wallet" display branding
|
||||||
|
# (APP_DISPLAY_NAME above).
|
||||||
|
local DMG_BASENAME="${APP_BASENAME}"
|
||||||
local DMG_NAME="${DMG_BASENAME}-${VERSION}-macOS-${MAC_ARCH}.dmg"
|
local DMG_NAME="${DMG_BASENAME}-${VERSION}-macOS-${MAC_ARCH}.dmg"
|
||||||
|
|
||||||
if command -v create-dmg &>/dev/null; then
|
if command -v create-dmg &>/dev/null; then
|
||||||
@@ -1296,3 +1345,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
|
||||||
|
|||||||
31
cmake/patch-libwebp-simd.cmake
Normal file
31
cmake/patch-libwebp-simd.cmake
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# patch-libwebp-simd.cmake — portable, idempotent FetchContent patch for libwebp.
|
||||||
|
#
|
||||||
|
# libwebp's cmake/cpu.cmake compiles its scalar *reference* DSP files with the
|
||||||
|
# SSE-disable flags "-mno-sse4.1;-mno-sse2" whenever it can't positively detect
|
||||||
|
# SSE support. Under a macOS *universal* build (-arch arm64;x86_64) the per-arch
|
||||||
|
# SSE flag probe fails (a flag valid for x86_64 is invalid for arm64), so those
|
||||||
|
# disable flags get applied to the x86_64 slice. clang gates the _Float16 type on
|
||||||
|
# SSE2 for x86_64, and the macOS 15+/26 SDK's <math.h> declares _Float16 math
|
||||||
|
# functions unconditionally — so any TU including <math.h> fails to compile with
|
||||||
|
# "_Float16 is not supported on this target".
|
||||||
|
#
|
||||||
|
# SSE2 is part of the x86_64 baseline ABI, so disabling it on the reference files
|
||||||
|
# is unnecessary on every platform we target. Blanking the SSE entries (indices
|
||||||
|
# must stay aligned with WEBP_SIMD_FLAGS) fixes the universal build and is a no-op
|
||||||
|
# for single-arch Linux/Windows/x86_64 builds. Idempotent: re-running is a no-op.
|
||||||
|
if(NOT DEFINED CPU_CMAKE OR NOT EXISTS "${CPU_CMAKE}")
|
||||||
|
message(FATAL_ERROR "patch-libwebp-simd: cpu.cmake not found at '${CPU_CMAKE}'")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(READ "${CPU_CMAKE}" _contents)
|
||||||
|
string(REPLACE
|
||||||
|
"set(SIMD_DISABLE_FLAGS \"-mno-sse4.1;-mno-sse2;;-mno-dspr2;;-mno-msa\")"
|
||||||
|
"set(SIMD_DISABLE_FLAGS \";;;-mno-dspr2;;-mno-msa\")"
|
||||||
|
_patched "${_contents}")
|
||||||
|
|
||||||
|
if(_patched STREQUAL _contents)
|
||||||
|
message(STATUS "patch-libwebp-simd: no change (already patched or pattern absent)")
|
||||||
|
else()
|
||||||
|
file(WRITE "${CPU_CMAKE}" "${_patched}")
|
||||||
|
message(STATUS "patch-libwebp-simd: neutralized x86 SSE-disable flags in cpu.cmake")
|
||||||
|
endif()
|
||||||
@@ -67,7 +67,8 @@
|
|||||||
//#define IMGUI_USE_LEGACY_CRC32_ADLER
|
//#define IMGUI_USE_LEGACY_CRC32_ADLER
|
||||||
|
|
||||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||||
//#define IMGUI_USE_WCHAR32
|
//---- Enabled so chat can render emoji (U+1F300+, above the BMP) — see Typography::loadFont emoji merge (Q12).
|
||||||
|
#define IMGUI_USE_WCHAR32
|
||||||
|
|
||||||
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||||
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
||||||
|
|||||||
744
libs/imgui/misc/freetype/imgui_freetype.cpp
Normal file
744
libs/imgui/misc/freetype/imgui_freetype.cpp
Normal file
@@ -0,0 +1,744 @@
|
|||||||
|
// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
|
||||||
|
// (code)
|
||||||
|
|
||||||
|
// Get the latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype
|
||||||
|
// Original code by @vuhdo (Aleksei Skriabin) in 2017, with improvements by @mikesart.
|
||||||
|
// Maintained since 2019 by @ocornut.
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2025/06/11: refactored for the new ImFontLoader architecture, and ImGuiBackendFlags_RendererHasTextures support.
|
||||||
|
// 2024/10/17: added plutosvg support for SVG Fonts (seems faster/better than lunasvg). Enable by using '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG'. (#7927)
|
||||||
|
// 2023/11/13: added support for ImFontConfig::RasterizationDensity field for scaling render density without scaling metrics.
|
||||||
|
// 2023/08/01: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG'. (#6591)
|
||||||
|
// 2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly.
|
||||||
|
// 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns nullptr.
|
||||||
|
// 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
|
||||||
|
// 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a preferred texture format.
|
||||||
|
// 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+).
|
||||||
|
// 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'. renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas().
|
||||||
|
// 2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails.
|
||||||
|
// 2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!)
|
||||||
|
// 2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions().
|
||||||
|
// 2019/01/10: re-factored to match big update in STB builder. fixed texture height waste. fixed redundant glyphs when merging. support for glyph padding.
|
||||||
|
// 2018/06/08: added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX.
|
||||||
|
// 2018/02/04: moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club)
|
||||||
|
// 2018/01/22: fix for addition of ImFontAtlas::TexUvscale member.
|
||||||
|
// 2017/10/22: minor inconsequential change to match change in master (removed an unnecessary statement).
|
||||||
|
// 2017/09/26: fixes for imgui internal changes.
|
||||||
|
// 2017/08/26: cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply.
|
||||||
|
// 2017/08/16: imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks.
|
||||||
|
|
||||||
|
// About Gamma Correct Blending:
|
||||||
|
// - FreeType assumes blending in linear space rather than gamma space.
|
||||||
|
// - See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph
|
||||||
|
// - For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
|
||||||
|
// - The default dear imgui styles will be impacted by this change (alpha values will need tweaking).
|
||||||
|
|
||||||
|
// FIXME: cfg.OversampleH, OversampleV are not supported, but generally not necessary with this rasterizer because Hinting makes everything look better.
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui_freetype.h"
|
||||||
|
#include "imgui_internal.h" // ImMin,ImMax,ImFontAtlasBuild*,
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <ft2build.h>
|
||||||
|
#include FT_FREETYPE_H // <freetype/freetype.h>
|
||||||
|
#include FT_MODULE_H // <freetype/ftmodapi.h>
|
||||||
|
#include FT_GLYPH_H // <freetype/ftglyph.h>
|
||||||
|
#include FT_SIZES_H // <freetype/ftsizes.h>
|
||||||
|
#include FT_SYNTHESIS_H // <freetype/ftsynth.h>
|
||||||
|
|
||||||
|
// Handle LunaSVG and PlutoSVG
|
||||||
|
#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) && defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG)
|
||||||
|
#error "Cannot enable both IMGUI_ENABLE_FREETYPE_LUNASVG and IMGUI_ENABLE_FREETYPE_PLUTOSVG"
|
||||||
|
#endif
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
#include FT_OTSVG_H // <freetype/otsvg.h>
|
||||||
|
#include FT_BBOX_H // <freetype/ftbbox.h>
|
||||||
|
#include <lunasvg.h>
|
||||||
|
#endif
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||||
|
#include <plutosvg.h>
|
||||||
|
#endif
|
||||||
|
#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) || defined (IMGUI_ENABLE_FREETYPE_PLUTOSVG)
|
||||||
|
#if !((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
|
||||||
|
#error IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (push)
|
||||||
|
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
|
||||||
|
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
|
||||||
|
#ifndef __clang__
|
||||||
|
#pragma GCC diagnostic ignored "-Wsubobject-linkage" // warning: 'xxxx' has a field 'xxxx' whose type uses the anonymous namespace
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// Data
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Default memory allocators
|
||||||
|
static void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); }
|
||||||
|
static void ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); }
|
||||||
|
|
||||||
|
// Current memory allocators
|
||||||
|
static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc;
|
||||||
|
static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc;
|
||||||
|
static void* GImGuiFreeTypeAllocatorUserData = nullptr;
|
||||||
|
|
||||||
|
// Lunasvg support
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
static FT_Error ImGuiLunasvgPortInit(FT_Pointer* state);
|
||||||
|
static void ImGuiLunasvgPortFree(FT_Pointer* state);
|
||||||
|
static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state);
|
||||||
|
static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// Code
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define FT_CEIL(X) (((X + 63) & -64) / 64) // From SDL_ttf: Handy routines for converting from fixed point
|
||||||
|
#define FT_SCALEFACTOR 64.0f
|
||||||
|
|
||||||
|
// Glyph metrics:
|
||||||
|
// --------------
|
||||||
|
//
|
||||||
|
// xmin xmax
|
||||||
|
// | |
|
||||||
|
// |<-------- width -------->|
|
||||||
|
// | |
|
||||||
|
// | +-------------------------+----------------- ymax
|
||||||
|
// | | ggggggggg ggggg | ^ ^
|
||||||
|
// | | g:::::::::ggg::::g | | |
|
||||||
|
// | | g:::::::::::::::::g | | |
|
||||||
|
// | | g::::::ggggg::::::gg | | |
|
||||||
|
// | | g:::::g g:::::g | | |
|
||||||
|
// offsetX -|-------->| g:::::g g:::::g | offsetY |
|
||||||
|
// | | g:::::g g:::::g | | |
|
||||||
|
// | | g::::::g g:::::g | | |
|
||||||
|
// | | g:::::::ggggg:::::g | | |
|
||||||
|
// | | g::::::::::::::::g | | height
|
||||||
|
// | | gg::::::::::::::g | | |
|
||||||
|
// baseline ---*---------|---- gggggggg::::::g-----*-------- |
|
||||||
|
// / | | g:::::g | |
|
||||||
|
// origin | | gggggg g:::::g | |
|
||||||
|
// | | g:::::gg gg:::::g | |
|
||||||
|
// | | g::::::ggg:::::::g | |
|
||||||
|
// | | gg:::::::::::::g | |
|
||||||
|
// | | ggg::::::ggg | |
|
||||||
|
// | | gggggg | v
|
||||||
|
// | +-------------------------+----------------- ymin
|
||||||
|
// | |
|
||||||
|
// |------------- advanceX ----------->|
|
||||||
|
|
||||||
|
// Stored in ImFontAtlas::FontLoaderData. ALLOCATED BY US.
|
||||||
|
struct ImGui_ImplFreeType_Data
|
||||||
|
{
|
||||||
|
FT_Library Library;
|
||||||
|
FT_MemoryRec_ MemoryManager;
|
||||||
|
ImGui_ImplFreeType_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Stored in ImFontConfig::FontLoaderData. ALLOCATED BY US.
|
||||||
|
struct ImGui_ImplFreeType_FontSrcData
|
||||||
|
{
|
||||||
|
// Initialize from an external data buffer. Doesn't copy data, and you must ensure it stays valid up to this object lifetime.
|
||||||
|
bool InitFont(FT_Library ft_library, const ImFontConfig* src, ImGuiFreeTypeLoaderFlags extra_user_flags);
|
||||||
|
void CloseFont();
|
||||||
|
ImGui_ImplFreeType_FontSrcData() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
~ImGui_ImplFreeType_FontSrcData() { CloseFont(); }
|
||||||
|
|
||||||
|
// Members
|
||||||
|
FT_Face FtFace;
|
||||||
|
ImGuiFreeTypeLoaderFlags UserFlags; // = ImFontConfig::FontLoaderFlags
|
||||||
|
FT_Int32 LoadFlags;
|
||||||
|
ImFontBaked* BakedLastActivated;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Stored in ImFontBaked::FontLoaderDatas: pointer to SourcesCount instances of this. ALLOCATED BY CORE.
|
||||||
|
struct ImGui_ImplFreeType_FontSrcBakedData
|
||||||
|
{
|
||||||
|
FT_Size FtSize; // This represent a FT_Face with a given size.
|
||||||
|
ImGui_ImplFreeType_FontSrcBakedData() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ImGui_ImplFreeType_FontSrcData::InitFont(FT_Library ft_library, const ImFontConfig* src, ImGuiFreeTypeLoaderFlags extra_font_loader_flags)
|
||||||
|
{
|
||||||
|
FT_Error error = FT_New_Memory_Face(ft_library, (const FT_Byte*)src->FontData, (FT_Long)src->FontDataSize, (FT_Long)src->FontNo, &FtFace);
|
||||||
|
if (error != 0)
|
||||||
|
return false;
|
||||||
|
error = FT_Select_Charmap(FtFace, FT_ENCODING_UNICODE);
|
||||||
|
if (error != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Convert to FreeType flags (NB: Bold and Oblique are processed separately)
|
||||||
|
UserFlags = (ImGuiFreeTypeLoaderFlags)(src->FontLoaderFlags | extra_font_loader_flags);
|
||||||
|
|
||||||
|
LoadFlags = 0;
|
||||||
|
if ((UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) == 0)
|
||||||
|
LoadFlags |= FT_LOAD_NO_BITMAP;
|
||||||
|
if (UserFlags & ImGuiFreeTypeLoaderFlags_NoHinting)
|
||||||
|
LoadFlags |= FT_LOAD_NO_HINTING;
|
||||||
|
if (UserFlags & ImGuiFreeTypeLoaderFlags_NoAutoHint)
|
||||||
|
LoadFlags |= FT_LOAD_NO_AUTOHINT;
|
||||||
|
if (UserFlags & ImGuiFreeTypeLoaderFlags_ForceAutoHint)
|
||||||
|
LoadFlags |= FT_LOAD_FORCE_AUTOHINT;
|
||||||
|
if (UserFlags & ImGuiFreeTypeLoaderFlags_LightHinting)
|
||||||
|
LoadFlags |= FT_LOAD_TARGET_LIGHT;
|
||||||
|
else if (UserFlags & ImGuiFreeTypeLoaderFlags_MonoHinting)
|
||||||
|
LoadFlags |= FT_LOAD_TARGET_MONO;
|
||||||
|
else
|
||||||
|
LoadFlags |= FT_LOAD_TARGET_NORMAL;
|
||||||
|
|
||||||
|
if (UserFlags & ImGuiFreeTypeLoaderFlags_LoadColor)
|
||||||
|
LoadFlags |= FT_LOAD_COLOR;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplFreeType_FontSrcData::CloseFont()
|
||||||
|
{
|
||||||
|
if (FtFace)
|
||||||
|
{
|
||||||
|
FT_Done_Face(FtFace);
|
||||||
|
FtFace = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const FT_Glyph_Metrics* ImGui_ImplFreeType_LoadGlyph(ImGui_ImplFreeType_FontSrcData* src_data, uint32_t codepoint)
|
||||||
|
{
|
||||||
|
uint32_t glyph_index = FT_Get_Char_Index(src_data->FtFace, codepoint);
|
||||||
|
if (glyph_index == 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// If this crash for you: FreeType 2.11.0 has a crash bug on some bitmap/colored fonts.
|
||||||
|
// - https://gitlab.freedesktop.org/freetype/freetype/-/issues/1076
|
||||||
|
// - https://github.com/ocornut/imgui/issues/4567
|
||||||
|
// - https://github.com/ocornut/imgui/issues/4566
|
||||||
|
// You can use FreeType 2.10, or the patched version of 2.11.0 in VcPkg, or probably any upcoming FreeType version.
|
||||||
|
FT_Error error = FT_Load_Glyph(src_data->FtFace, glyph_index, src_data->LoadFlags);
|
||||||
|
if (error)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Need an outline for this to work
|
||||||
|
FT_GlyphSlot slot = src_data->FtFace->glyph;
|
||||||
|
#if defined(IMGUI_ENABLE_FREETYPE_LUNASVG) || defined(IMGUI_ENABLE_FREETYPE_PLUTOSVG)
|
||||||
|
IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP || slot->format == FT_GLYPH_FORMAT_SVG);
|
||||||
|
#else
|
||||||
|
#if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
|
||||||
|
IM_ASSERT(slot->format != FT_GLYPH_FORMAT_SVG && "The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_PLUTOSVG or IMGUI_ENABLE_FREETYPE_LUNASVG in imconfig.h and install required libraries in order to use this font");
|
||||||
|
#endif
|
||||||
|
IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP);
|
||||||
|
#endif // IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
|
||||||
|
// Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting)
|
||||||
|
if (src_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bold)
|
||||||
|
FT_GlyphSlot_Embolden(slot);
|
||||||
|
if (src_data->UserFlags & ImGuiFreeTypeLoaderFlags_Oblique)
|
||||||
|
{
|
||||||
|
FT_GlyphSlot_Oblique(slot);
|
||||||
|
//FT_BBox bbox;
|
||||||
|
//FT_Outline_Get_BBox(&slot->outline, &bbox);
|
||||||
|
//slot->metrics.width = bbox.xMax - bbox.xMin;
|
||||||
|
//slot->metrics.height = bbox.yMax - bbox.yMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &slot->metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplFreeType_BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch)
|
||||||
|
{
|
||||||
|
IM_ASSERT(ft_bitmap != nullptr);
|
||||||
|
const uint32_t w = ft_bitmap->width;
|
||||||
|
const uint32_t h = ft_bitmap->rows;
|
||||||
|
const uint8_t* src = ft_bitmap->buffer;
|
||||||
|
const uint32_t src_pitch = ft_bitmap->pitch;
|
||||||
|
|
||||||
|
switch (ft_bitmap->pixel_mode)
|
||||||
|
{
|
||||||
|
case FT_PIXEL_MODE_GRAY: // Grayscale image, 1 byte per pixel.
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
for (uint32_t x = 0; x < w; x++)
|
||||||
|
dst[x] = IM_COL32(255, 255, 255, src[x]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FT_PIXEL_MODE_MONO: // Monochrome image, 1 bit per pixel. The bits in each byte are ordered from MSB to LSB.
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
{
|
||||||
|
uint8_t bits = 0;
|
||||||
|
const uint8_t* bits_ptr = src;
|
||||||
|
for (uint32_t x = 0; x < w; x++, bits <<= 1)
|
||||||
|
{
|
||||||
|
if ((x & 7) == 0)
|
||||||
|
bits = *bits_ptr++;
|
||||||
|
dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? 255 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FT_PIXEL_MODE_BGRA:
|
||||||
|
{
|
||||||
|
// FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good.
|
||||||
|
#define DE_MULTIPLY(color, alpha) ImMin((ImU32)(255.0f * (float)color / (float)(alpha + FLT_MIN) + 0.5f), 255u)
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
for (uint32_t x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
|
||||||
|
dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a);
|
||||||
|
}
|
||||||
|
#undef DE_MULTIPLY
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FreeType memory allocation callbacks
|
||||||
|
static void* FreeType_Alloc(FT_Memory /*memory*/, long size)
|
||||||
|
{
|
||||||
|
return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FreeType_Free(FT_Memory /*memory*/, void* block)
|
||||||
|
{
|
||||||
|
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size, void* block)
|
||||||
|
{
|
||||||
|
// Implement realloc() as we don't ask user to provide it.
|
||||||
|
if (block == nullptr)
|
||||||
|
return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
|
||||||
|
if (new_size == 0)
|
||||||
|
{
|
||||||
|
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_size > cur_size)
|
||||||
|
{
|
||||||
|
void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
memcpy(new_block, block, (size_t)cur_size);
|
||||||
|
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
return new_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplFreeType_LoaderInit(ImFontAtlas* atlas)
|
||||||
|
{
|
||||||
|
IM_ASSERT(atlas->FontLoaderData == nullptr);
|
||||||
|
ImGui_ImplFreeType_Data* bd = IM_NEW(ImGui_ImplFreeType_Data)();
|
||||||
|
|
||||||
|
// FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html
|
||||||
|
bd->MemoryManager.user = nullptr;
|
||||||
|
bd->MemoryManager.alloc = &FreeType_Alloc;
|
||||||
|
bd->MemoryManager.free = &FreeType_Free;
|
||||||
|
bd->MemoryManager.realloc = &FreeType_Realloc;
|
||||||
|
|
||||||
|
// https://www.freetype.org/freetype2/docs/reference/ft2-module_management.html#FT_New_Library
|
||||||
|
FT_Error error = FT_New_Library(&bd->MemoryManager, &bd->Library);
|
||||||
|
if (error != 0)
|
||||||
|
{
|
||||||
|
IM_DELETE(bd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator.
|
||||||
|
FT_Add_Default_Modules(bd->Library);
|
||||||
|
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
// Install svg hooks for FreeType
|
||||||
|
// https://freetype.org/freetype2/docs/reference/ft2-properties.html#svg-hooks
|
||||||
|
// https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html#svg_fonts
|
||||||
|
SVG_RendererHooks hooks = { ImGuiLunasvgPortInit, ImGuiLunasvgPortFree, ImGuiLunasvgPortRender, ImGuiLunasvgPortPresetSlot };
|
||||||
|
FT_Property_Set(bd->Library, "ot-svg", "svg-hooks", &hooks);
|
||||||
|
#endif // IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||||
|
// With plutosvg, use provided hooks
|
||||||
|
FT_Property_Set(bd->Library, "ot-svg", "svg-hooks", plutosvg_ft_svg_hooks());
|
||||||
|
#endif // IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||||
|
|
||||||
|
// Store our data
|
||||||
|
atlas->FontLoaderData = (void*)bd;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplFreeType_LoaderShutdown(ImFontAtlas* atlas)
|
||||||
|
{
|
||||||
|
ImGui_ImplFreeType_Data* bd = (ImGui_ImplFreeType_Data*)atlas->FontLoaderData;
|
||||||
|
IM_ASSERT(bd != nullptr);
|
||||||
|
FT_Done_Library(bd->Library);
|
||||||
|
IM_DELETE(bd);
|
||||||
|
atlas->FontLoaderData = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplFreeType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src)
|
||||||
|
{
|
||||||
|
ImGui_ImplFreeType_Data* bd = (ImGui_ImplFreeType_Data*)atlas->FontLoaderData;
|
||||||
|
ImGui_ImplFreeType_FontSrcData* bd_font_data = IM_NEW(ImGui_ImplFreeType_FontSrcData);
|
||||||
|
IM_ASSERT(src->FontLoaderData == nullptr);
|
||||||
|
src->FontLoaderData = bd_font_data;
|
||||||
|
|
||||||
|
if (!bd_font_data->InitFont(bd->Library, src, (ImGuiFreeTypeLoaderFlags)atlas->FontLoaderFlags))
|
||||||
|
{
|
||||||
|
IM_DELETE(bd_font_data);
|
||||||
|
src->FontLoaderData = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplFreeType_FontSrcDestroy(ImFontAtlas* atlas, ImFontConfig* src)
|
||||||
|
{
|
||||||
|
IM_UNUSED(atlas);
|
||||||
|
ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData;
|
||||||
|
IM_DELETE(bd_font_data);
|
||||||
|
src->FontLoaderData = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src)
|
||||||
|
{
|
||||||
|
IM_UNUSED(atlas);
|
||||||
|
float size = baked->Size;
|
||||||
|
if (src->MergeMode && src->SizePixels != 0.0f)
|
||||||
|
size *= (src->SizePixels / baked->OwnerFont->Sources[0]->SizePixels);
|
||||||
|
size *= src->ExtraSizeScale;
|
||||||
|
|
||||||
|
ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData;
|
||||||
|
bd_font_data->BakedLastActivated = baked;
|
||||||
|
|
||||||
|
// We use one FT_Size per (source + baked) combination.
|
||||||
|
ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src;
|
||||||
|
IM_ASSERT(bd_baked_data != nullptr);
|
||||||
|
IM_PLACEMENT_NEW(bd_baked_data) ImGui_ImplFreeType_FontSrcBakedData();
|
||||||
|
|
||||||
|
FT_New_Size(bd_font_data->FtFace, &bd_baked_data->FtSize);
|
||||||
|
FT_Activate_Size(bd_baked_data->FtSize);
|
||||||
|
|
||||||
|
// Vuhdo 2017: "I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height'
|
||||||
|
// is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me.
|
||||||
|
// FT_Set_Pixel_Sizes() doesn't seem to get us the same result."
|
||||||
|
// (FT_Set_Pixel_Sizes() essentially calls FT_Request_Size() with FT_SIZE_REQUEST_TYPE_NOMINAL)
|
||||||
|
const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity;
|
||||||
|
FT_Size_RequestRec req;
|
||||||
|
req.type = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
|
||||||
|
req.width = 0;
|
||||||
|
req.height = (uint32_t)(size * 64 * rasterizer_density);
|
||||||
|
req.horiResolution = 0;
|
||||||
|
req.vertResolution = 0;
|
||||||
|
FT_Request_Size(bd_font_data->FtFace, &req);
|
||||||
|
|
||||||
|
// Output
|
||||||
|
if (src->MergeMode == false)
|
||||||
|
{
|
||||||
|
// Read metrics
|
||||||
|
FT_Size_Metrics metrics = bd_baked_data->FtSize->metrics;
|
||||||
|
const float scale = 1.0f / (rasterizer_density * src->ExtraSizeScale);
|
||||||
|
baked->Ascent = (float)FT_CEIL(metrics.ascender) * scale; // The pixel extents above the baseline in pixels (typically positive).
|
||||||
|
baked->Descent = (float)FT_CEIL(metrics.descender) * scale; // The extents below the baseline in pixels (typically negative).
|
||||||
|
//LineSpacing = (float)FT_CEIL(metrics.height) * scale; // The baseline-to-baseline distance. Note that it usually is larger than the sum of the ascender and descender taken as absolute values. There is also no guarantee that no glyphs extend above or below subsequent baselines when using this distance. Think of it as a value the designer of the font finds appropriate.
|
||||||
|
//LineGap = (float)FT_CEIL(metrics.height - metrics.ascender + metrics.descender) * scale; // The spacing in pixels between one row's descent and the next row's ascent.
|
||||||
|
//MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance) * scale; // This field gives the maximum horizontal cursor advance for all glyphs in the font.
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src)
|
||||||
|
{
|
||||||
|
IM_UNUSED(atlas);
|
||||||
|
IM_UNUSED(baked);
|
||||||
|
IM_UNUSED(src);
|
||||||
|
ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src;
|
||||||
|
IM_ASSERT(bd_baked_data != nullptr);
|
||||||
|
FT_Done_Size(bd_baked_data->FtSize);
|
||||||
|
bd_baked_data->~ImGui_ImplFreeType_FontSrcBakedData(); // ~IM_PLACEMENT_DELETE()
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x)
|
||||||
|
{
|
||||||
|
ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData;
|
||||||
|
uint32_t glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint);
|
||||||
|
if (glyph_index == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bd_font_data->BakedLastActivated != baked) // <-- could use id
|
||||||
|
{
|
||||||
|
// Activate current size
|
||||||
|
ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src;
|
||||||
|
FT_Activate_Size(bd_baked_data->FtSize);
|
||||||
|
bd_font_data->BakedLastActivated = baked;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FT_Glyph_Metrics* metrics = ImGui_ImplFreeType_LoadGlyph(bd_font_data, codepoint);
|
||||||
|
if (metrics == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
FT_Face face = bd_font_data->FtFace;
|
||||||
|
FT_GlyphSlot slot = face->glyph;
|
||||||
|
const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity;
|
||||||
|
|
||||||
|
// Load metrics only mode
|
||||||
|
const float advance_x = (slot->advance.x / FT_SCALEFACTOR) / rasterizer_density;
|
||||||
|
if (out_advance_x != NULL)
|
||||||
|
{
|
||||||
|
IM_ASSERT(out_glyph == NULL);
|
||||||
|
*out_advance_x = advance_x;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render glyph into a bitmap (currently held by FreeType)
|
||||||
|
FT_Render_Mode render_mode = (bd_font_data->UserFlags & ImGuiFreeTypeLoaderFlags_Monochrome) ? FT_RENDER_MODE_MONO : FT_RENDER_MODE_NORMAL;
|
||||||
|
FT_Error error = FT_Render_Glyph(slot, render_mode);
|
||||||
|
const FT_Bitmap* ft_bitmap = &slot->bitmap;
|
||||||
|
if (error != 0 || ft_bitmap == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const int w = (int)ft_bitmap->width;
|
||||||
|
const int h = (int)ft_bitmap->rows;
|
||||||
|
const bool is_visible = (w != 0 && h != 0);
|
||||||
|
|
||||||
|
// Prepare glyph
|
||||||
|
out_glyph->Codepoint = codepoint;
|
||||||
|
out_glyph->AdvanceX = advance_x;
|
||||||
|
|
||||||
|
// Pack and retrieve position inside texture atlas
|
||||||
|
if (is_visible)
|
||||||
|
{
|
||||||
|
ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect(atlas, w, h);
|
||||||
|
if (pack_id == ImFontAtlasRectId_Invalid)
|
||||||
|
{
|
||||||
|
// Pathological out of memory case (TexMaxWidth/TexMaxHeight set too small?)
|
||||||
|
IM_ASSERT(pack_id != ImFontAtlasRectId_Invalid && "Out of texture memory.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ImTextureRect* r = ImFontAtlasPackGetRect(atlas, pack_id);
|
||||||
|
|
||||||
|
// Render pixels to our temporary buffer
|
||||||
|
atlas->Builder->TempBuffer.resize(w * h * 4);
|
||||||
|
uint32_t* temp_buffer = (uint32_t*)atlas->Builder->TempBuffer.Data;
|
||||||
|
ImGui_ImplFreeType_BlitGlyph(ft_bitmap, temp_buffer, w);
|
||||||
|
|
||||||
|
const float ref_size = baked->OwnerFont->Sources[0]->SizePixels;
|
||||||
|
const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;
|
||||||
|
float font_off_x = ImFloor(src->GlyphOffset.x * offsets_scale + 0.5f); // Snap scaled offset.
|
||||||
|
float font_off_y = ImFloor(src->GlyphOffset.y * offsets_scale + 0.5f) + baked->Ascent;
|
||||||
|
float recip_h = 1.0f / rasterizer_density;
|
||||||
|
float recip_v = 1.0f / rasterizer_density;
|
||||||
|
|
||||||
|
// Register glyph
|
||||||
|
float glyph_off_x = (float)face->glyph->bitmap_left;
|
||||||
|
float glyph_off_y = (float)-face->glyph->bitmap_top;
|
||||||
|
out_glyph->X0 = glyph_off_x * recip_h + font_off_x;
|
||||||
|
out_glyph->Y0 = glyph_off_y * recip_v + font_off_y;
|
||||||
|
out_glyph->X1 = (glyph_off_x + w) * recip_h + font_off_x;
|
||||||
|
out_glyph->Y1 = (glyph_off_y + h) * recip_v + font_off_y;
|
||||||
|
out_glyph->Visible = true;
|
||||||
|
out_glyph->Colored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA);
|
||||||
|
out_glyph->PackId = pack_id;
|
||||||
|
ImFontAtlasBakedSetFontGlyphBitmap(atlas, baked, src, out_glyph, r, (const unsigned char*)temp_buffer, ImTextureFormat_RGBA32, w * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplFreetype_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint)
|
||||||
|
{
|
||||||
|
IM_UNUSED(atlas);
|
||||||
|
ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData;
|
||||||
|
int glyph_index = FT_Get_Char_Index(bd_font_data->FtFace, codepoint);
|
||||||
|
return glyph_index != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImFontLoader* ImGuiFreeType::GetFontLoader()
|
||||||
|
{
|
||||||
|
static ImFontLoader loader;
|
||||||
|
loader.Name = "FreeType";
|
||||||
|
loader.LoaderInit = ImGui_ImplFreeType_LoaderInit;
|
||||||
|
loader.LoaderShutdown = ImGui_ImplFreeType_LoaderShutdown;
|
||||||
|
loader.FontSrcInit = ImGui_ImplFreeType_FontSrcInit;
|
||||||
|
loader.FontSrcDestroy = ImGui_ImplFreeType_FontSrcDestroy;
|
||||||
|
loader.FontSrcContainsGlyph = ImGui_ImplFreetype_FontSrcContainsGlyph;
|
||||||
|
loader.FontBakedInit = ImGui_ImplFreeType_FontBakedInit;
|
||||||
|
loader.FontBakedDestroy = ImGui_ImplFreeType_FontBakedDestroy;
|
||||||
|
loader.FontBakedLoadGlyph = ImGui_ImplFreeType_FontBakedLoadGlyph;
|
||||||
|
loader.FontBakedSrcLoaderDataSize = sizeof(ImGui_ImplFreeType_FontSrcBakedData);
|
||||||
|
return &loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
|
||||||
|
{
|
||||||
|
GImGuiFreeTypeAllocFunc = alloc_func;
|
||||||
|
GImGuiFreeTypeFreeFunc = free_func;
|
||||||
|
GImGuiFreeTypeAllocatorUserData = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGuiFreeType::DebugEditFontLoaderFlags(unsigned int* p_font_loader_flags)
|
||||||
|
{
|
||||||
|
bool edited = false;
|
||||||
|
edited |= ImGui::CheckboxFlags("NoHinting", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_NoHinting);
|
||||||
|
edited |= ImGui::CheckboxFlags("NoAutoHint", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_NoAutoHint);
|
||||||
|
edited |= ImGui::CheckboxFlags("ForceAutoHint",p_font_loader_flags, ImGuiFreeTypeLoaderFlags_ForceAutoHint);
|
||||||
|
edited |= ImGui::CheckboxFlags("LightHinting", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_LightHinting);
|
||||||
|
edited |= ImGui::CheckboxFlags("MonoHinting", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_MonoHinting);
|
||||||
|
edited |= ImGui::CheckboxFlags("Bold", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Bold);
|
||||||
|
edited |= ImGui::CheckboxFlags("Oblique", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Oblique);
|
||||||
|
edited |= ImGui::CheckboxFlags("Monochrome", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Monochrome);
|
||||||
|
edited |= ImGui::CheckboxFlags("LoadColor", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_LoadColor);
|
||||||
|
edited |= ImGui::CheckboxFlags("Bitmap", p_font_loader_flags, ImGuiFreeTypeLoaderFlags_Bitmap);
|
||||||
|
return edited;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
// For more details, see https://gitlab.freedesktop.org/freetype/freetype-demos/-/blob/master/src/rsvg-port.c
|
||||||
|
// The original code from the demo is licensed under CeCILL-C Free Software License Agreement (https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/LICENSE.TXT)
|
||||||
|
struct LunasvgPortState
|
||||||
|
{
|
||||||
|
FT_Error err = FT_Err_Ok;
|
||||||
|
lunasvg::Matrix matrix;
|
||||||
|
std::unique_ptr<lunasvg::Document> svg = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static FT_Error ImGuiLunasvgPortInit(FT_Pointer* _state)
|
||||||
|
{
|
||||||
|
*_state = IM_NEW(LunasvgPortState)();
|
||||||
|
return FT_Err_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGuiLunasvgPortFree(FT_Pointer* _state)
|
||||||
|
{
|
||||||
|
IM_DELETE(*(LunasvgPortState**)_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state)
|
||||||
|
{
|
||||||
|
LunasvgPortState* state = *(LunasvgPortState**)_state;
|
||||||
|
|
||||||
|
// If there was an error while loading the svg in ImGuiLunasvgPortPresetSlot(), the renderer hook still get called, so just returns the error.
|
||||||
|
if (state->err != FT_Err_Ok)
|
||||||
|
return state->err;
|
||||||
|
|
||||||
|
// rows is height, pitch (or stride) equals to width * sizeof(int32)
|
||||||
|
lunasvg::Bitmap bitmap((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch);
|
||||||
|
#if LUNASVG_VERSION_MAJOR >= 3
|
||||||
|
state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated
|
||||||
|
#else
|
||||||
|
state->svg->setMatrix(state->svg->matrix().identity()); // Reset the svg matrix to the default value
|
||||||
|
state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated
|
||||||
|
#endif
|
||||||
|
state->err = FT_Err_Ok;
|
||||||
|
return state->err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state)
|
||||||
|
{
|
||||||
|
FT_SVG_Document document = (FT_SVG_Document)slot->other;
|
||||||
|
LunasvgPortState* state = *(LunasvgPortState**)_state;
|
||||||
|
FT_Size_Metrics& metrics = document->metrics;
|
||||||
|
|
||||||
|
// This function is called twice, once in the FT_Load_Glyph() and another right before ImGuiLunasvgPortRender().
|
||||||
|
// If it's the latter, don't do anything because it's // already done in the former.
|
||||||
|
if (cache)
|
||||||
|
return state->err;
|
||||||
|
|
||||||
|
state->svg = lunasvg::Document::loadFromData((const char*)document->svg_document, document->svg_document_length);
|
||||||
|
if (state->svg == nullptr)
|
||||||
|
{
|
||||||
|
state->err = FT_Err_Invalid_SVG_Document;
|
||||||
|
return state->err;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LUNASVG_VERSION_MAJOR >= 3
|
||||||
|
lunasvg::Box box = state->svg->boundingBox();
|
||||||
|
#else
|
||||||
|
lunasvg::Box box = state->svg->box();
|
||||||
|
#endif
|
||||||
|
double scale = std::min(metrics.x_ppem / box.w, metrics.y_ppem / box.h);
|
||||||
|
double xx = (double)document->transform.xx / (1 << 16);
|
||||||
|
double xy = -(double)document->transform.xy / (1 << 16);
|
||||||
|
double yx = -(double)document->transform.yx / (1 << 16);
|
||||||
|
double yy = (double)document->transform.yy / (1 << 16);
|
||||||
|
double x0 = (double)document->delta.x / 64 * box.w / metrics.x_ppem;
|
||||||
|
double y0 = -(double)document->delta.y / 64 * box.h / metrics.y_ppem;
|
||||||
|
|
||||||
|
#if LUNASVG_VERSION_MAJOR >= 3
|
||||||
|
// Scale, transform and pre-translate the matrix for the rendering step
|
||||||
|
state->matrix = lunasvg::Matrix::translated(-box.x, -box.y);
|
||||||
|
state->matrix.multiply(lunasvg::Matrix(xx, xy, yx, yy, x0, y0));
|
||||||
|
state->matrix.scale(scale, scale);
|
||||||
|
|
||||||
|
// Apply updated transformation to the bounding box
|
||||||
|
box.transform(state->matrix);
|
||||||
|
#else
|
||||||
|
// Scale and transform, we don't translate the svg yet
|
||||||
|
state->matrix.identity();
|
||||||
|
state->matrix.scale(scale, scale);
|
||||||
|
state->matrix.transform(xx, xy, yx, yy, x0, y0);
|
||||||
|
state->svg->setMatrix(state->matrix);
|
||||||
|
|
||||||
|
// Pre-translate the matrix for the rendering step
|
||||||
|
state->matrix.translate(-box.x, -box.y);
|
||||||
|
|
||||||
|
// Get the box again after the transformation
|
||||||
|
box = state->svg->box();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Calculate the bitmap size
|
||||||
|
slot->bitmap_left = FT_Int(box.x);
|
||||||
|
slot->bitmap_top = FT_Int(-box.y);
|
||||||
|
slot->bitmap.rows = (unsigned int)(ImCeil((float)box.h));
|
||||||
|
slot->bitmap.width = (unsigned int)(ImCeil((float)box.w));
|
||||||
|
slot->bitmap.pitch = slot->bitmap.width * 4;
|
||||||
|
slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
|
||||||
|
|
||||||
|
// Compute all the bearings and set them correctly. The outline is scaled already, we just need to use the bounding box.
|
||||||
|
double metrics_width = box.w;
|
||||||
|
double metrics_height = box.h;
|
||||||
|
double horiBearingX = box.x;
|
||||||
|
double horiBearingY = -box.y;
|
||||||
|
double vertBearingX = slot->metrics.horiBearingX / 64.0 - slot->metrics.horiAdvance / 64.0 / 2.0;
|
||||||
|
double vertBearingY = (slot->metrics.vertAdvance / 64.0 - slot->metrics.height / 64.0) / 2.0;
|
||||||
|
slot->metrics.width = FT_Pos(IM_ROUND(metrics_width * 64.0)); // Using IM_ROUND() assume width and height are positive
|
||||||
|
slot->metrics.height = FT_Pos(IM_ROUND(metrics_height * 64.0));
|
||||||
|
slot->metrics.horiBearingX = FT_Pos(horiBearingX * 64);
|
||||||
|
slot->metrics.horiBearingY = FT_Pos(horiBearingY * 64);
|
||||||
|
slot->metrics.vertBearingX = FT_Pos(vertBearingX * 64);
|
||||||
|
slot->metrics.vertBearingY = FT_Pos(vertBearingY * 64);
|
||||||
|
|
||||||
|
if (slot->metrics.vertAdvance == 0)
|
||||||
|
slot->metrics.vertAdvance = FT_Pos(metrics_height * 1.2 * 64.0);
|
||||||
|
|
||||||
|
state->err = FT_Err_Ok;
|
||||||
|
return state->err;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
||||||
83
libs/imgui/misc/freetype/imgui_freetype.h
Normal file
83
libs/imgui/misc/freetype/imgui_freetype.h
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
|
||||||
|
// (headers)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "imgui.h" // IMGUI_API
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
// Usage:
|
||||||
|
// - Add '#define IMGUI_ENABLE_FREETYPE' in your imconfig to automatically enable support
|
||||||
|
// for imgui_freetype in imgui. It is equivalent to selecting the default loader with:
|
||||||
|
// io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader())
|
||||||
|
|
||||||
|
// Optional support for OpenType SVG fonts:
|
||||||
|
// - Add '#define IMGUI_ENABLE_FREETYPE_PLUTOSVG' to use plutosvg (not provided). See #7927.
|
||||||
|
// - Add '#define IMGUI_ENABLE_FREETYPE_LUNASVG' to use lunasvg (not provided). See #6591.
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
struct ImFontAtlas;
|
||||||
|
struct ImFontLoader;
|
||||||
|
|
||||||
|
// Hinting greatly impacts visuals (and glyph sizes).
|
||||||
|
// - By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter.
|
||||||
|
// - When disabled, FreeType generates blurrier glyphs, more or less matches the stb_truetype.h
|
||||||
|
// - The Default hinting mode usually looks good, but may distort glyphs in an unusual way.
|
||||||
|
// - The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer.
|
||||||
|
// You can set those flags globally in ImFontAtlas::FontLoaderFlags
|
||||||
|
// You can set those flags on a per font basis in ImFontConfig::FontLoaderFlags
|
||||||
|
typedef unsigned int ImGuiFreeTypeLoaderFlags;
|
||||||
|
enum ImGuiFreeTypeLoaderFlags_
|
||||||
|
{
|
||||||
|
ImGuiFreeTypeLoaderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.
|
||||||
|
ImGuiFreeTypeLoaderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter.
|
||||||
|
ImGuiFreeTypeLoaderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter.
|
||||||
|
ImGuiFreeTypeLoaderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text.
|
||||||
|
ImGuiFreeTypeLoaderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output.
|
||||||
|
ImGuiFreeTypeLoaderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font?
|
||||||
|
ImGuiFreeTypeLoaderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style?
|
||||||
|
ImGuiFreeTypeLoaderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results!
|
||||||
|
ImGuiFreeTypeLoaderFlags_LoadColor = 1 << 8, // Enable FreeType color-layered glyphs
|
||||||
|
ImGuiFreeTypeLoaderFlags_Bitmap = 1 << 9, // Enable FreeType bitmap glyphs
|
||||||
|
|
||||||
|
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
|
ImGuiFreeTypeBuilderFlags_NoHinting = ImGuiFreeTypeLoaderFlags_NoHinting,
|
||||||
|
ImGuiFreeTypeBuilderFlags_NoAutoHint = ImGuiFreeTypeLoaderFlags_NoAutoHint,
|
||||||
|
ImGuiFreeTypeBuilderFlags_ForceAutoHint = ImGuiFreeTypeLoaderFlags_ForceAutoHint,
|
||||||
|
ImGuiFreeTypeBuilderFlags_LightHinting = ImGuiFreeTypeLoaderFlags_LightHinting,
|
||||||
|
ImGuiFreeTypeBuilderFlags_MonoHinting = ImGuiFreeTypeLoaderFlags_MonoHinting,
|
||||||
|
ImGuiFreeTypeBuilderFlags_Bold = ImGuiFreeTypeLoaderFlags_Bold,
|
||||||
|
ImGuiFreeTypeBuilderFlags_Oblique = ImGuiFreeTypeLoaderFlags_Oblique,
|
||||||
|
ImGuiFreeTypeBuilderFlags_Monochrome = ImGuiFreeTypeLoaderFlags_Monochrome,
|
||||||
|
ImGuiFreeTypeBuilderFlags_LoadColor = ImGuiFreeTypeLoaderFlags_LoadColor,
|
||||||
|
ImGuiFreeTypeBuilderFlags_Bitmap = ImGuiFreeTypeLoaderFlags_Bitmap,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// Obsolete names (will be removed)
|
||||||
|
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
|
typedef ImGuiFreeTypeLoaderFlags_ ImGuiFreeTypeBuilderFlags_;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ImGuiFreeType
|
||||||
|
{
|
||||||
|
// This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'.
|
||||||
|
// If you need to dynamically select between multiple builders:
|
||||||
|
// - you can manually assign this builder with 'atlas->SetFontLoader(ImGuiFreeType::GetFontLoader())'
|
||||||
|
// - prefer deep-copying this into your own ImFontLoader instance if you use hot-reloading that messes up static data.
|
||||||
|
IMGUI_API const ImFontLoader* GetFontLoader();
|
||||||
|
|
||||||
|
// Override allocators. By default ImGuiFreeType will use IM_ALLOC()/IM_FREE()
|
||||||
|
// However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired.
|
||||||
|
IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = nullptr);
|
||||||
|
|
||||||
|
// Display UI to edit ImFontAtlas::FontLoaderFlags (shared) or ImFontConfig::FontLoaderFlags (single source)
|
||||||
|
IMGUI_API bool DebugEditFontLoaderFlags(ImGuiFreeTypeLoaderFlags* p_font_loader_flags);
|
||||||
|
|
||||||
|
// Obsolete names (will be removed)
|
||||||
|
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
|
//IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType(); // Renamed/changed in 1.92. Change 'io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()' to 'io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader())' if you need runtime selection.
|
||||||
|
//static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontLoaderFlags = flags; return atlas->Build(); } // Prefer using '#define IMGUI_ENABLE_FREETYPE'
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
||||||
3278
libs/nanosvg/nanosvg.h
Normal file
3278
libs/nanosvg/nanosvg.h
Normal file
File diff suppressed because it is too large
Load Diff
1472
libs/nanosvg/nanosvgrast.h
Normal file
1472
libs/nanosvg/nanosvgrast.h
Normal file
File diff suppressed because it is too large
Load Diff
0
prebuilt-binaries/drg-xmrig/.gitkeep
Normal file
0
prebuilt-binaries/drg-xmrig/.gitkeep
Normal file
BIN
res/fonts/NotoEmoji-Subset.ttf
Normal file
BIN
res/fonts/NotoEmoji-Subset.ttf
Normal file
Binary file not shown.
Binary file not shown.
BIN
res/fonts/TwemojiMozilla-Color.ttf
Normal file
BIN
res/fonts/TwemojiMozilla-Color.ttf
Normal file
Binary file not shown.
BIN
res/img/backgrounds/texture/jade_bg.png
Normal file
BIN
res/img/backgrounds/texture/jade_bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
23
res/img/logos/logo_dragonx.svg
Normal file
23
res/img/logos/logo_dragonx.svg
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-2 {
|
||||||
|
fill: #d82652;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="cls-2" d="M103.98,128s-6.29-24.7-18.73-34.43c-8.53-8.03-15.63-16.49-21.25-24.16-5.62,7.68-12.72,16.17-21.25,24.16-12.4,9.74-18.73,34.43-18.73,34.43-2.38-24.34,7.85-35.82,12.87-41.29,7.82-8.5,15.31-16.56,21.75-25.02-7.64-11.44-11.41-19.72-11.41-19.72-.89-3.27-3.84-6.64-3.84-6.64,6.08-8.1-1.6-16.98-1.6-16.98,5.79-5.62,6.71-10.02,8.32-18.34-1.96,22.35,4.02,39.09,13.93,54.12,9.84-15.03,15.81-31.77,13.86-54.12,1.6,8.35,2.52,12.72,8.32,18.34,0,0-7.68,8.88-1.6,16.98,0,0-2.95,3.38-3.84,6.64,0,0-3.77,8.28-11.37,19.72,6.43,8.45,13.97,16.56,21.75,25.02,4.97,5.47,15.21,16.95,12.83,41.29h0Z"/>
|
||||||
|
<g>
|
||||||
|
<path class="cls-1" d="M55.33,61.62c-3.55,4.55-7.39,8.99-11.44,13.47-5.29-4.48-11.23-5.33-11.23-5.33,22.92-7.82,2.81-15.17.28-16.31C9.28,42.78,9.1,14.78,9.1,14.78c11.51,34.15,36.42,32.3,36.42,32.3.35-.21.67-.46.92-.71,1.64,3.27,4.58,8.67,8.88,15.24h0Z"/>
|
||||||
|
<g>
|
||||||
|
<path class="cls-1" d="M68.62,40.41c-1.35,2.98-2.91,5.83-4.62,8.63-1.71-2.81-3.23-5.69-4.62-8.63,1.74-3.45,4.62-20.58,4.62-20.58,0,0,2.88,17.13,4.62,20.58Z"/>
|
||||||
|
<path class="cls-1" d="M76.01,97.93l-3.48,2.34s-.1-4.44-3.52-1.84c-.42.32-2.38,2.21-.03,4.27,0,0-4.05,4.08-4.97,8.21-.92-4.12-4.97-8.21-4.97-8.21,2.34-2.06.39-3.95-.03-4.27-3.41-2.59-3.52,1.84-3.52,1.84l-3.48-2.34c.28-3.55.1-6.68-.46-9.42,4.69-4.94,8.85-9.88,12.47-14.61,3.66,4.72,7.78,9.67,12.47,14.61-.57,2.74-.75,5.86-.46,9.42Z"/>
|
||||||
|
<path class="cls-1" d="M95.34,69.76s-5.94.85-11.23,5.33c-4.02-4.48-7.89-8.92-11.44-13.47,4.3-6.57,7.25-11.98,8.88-15.24.25.25.57.5.92.71,0,0,24.91,1.84,36.42-32.3,0,0-.18,28-23.84,38.66-2.52,1.14-22.64,8.5.28,16.31h0Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
325
res/lang/de.json
325
res/lang/de.json
@@ -30,6 +30,7 @@
|
|||||||
"address_book_added": "Adresse zum Buch hinzugefügt",
|
"address_book_added": "Adresse zum Buch hinzugefügt",
|
||||||
"address_book_confirm_delete": "Löschen bestätigen?",
|
"address_book_confirm_delete": "Löschen bestätigen?",
|
||||||
"address_book_count": "%zu Adressen gespeichert",
|
"address_book_count": "%zu Adressen gespeichert",
|
||||||
|
"address_book_count_one": "%zu Adresse gespeichert",
|
||||||
"address_book_deleted": "Eintrag gelöscht",
|
"address_book_deleted": "Eintrag gelöscht",
|
||||||
"address_book_edit": "Adresse bearbeiten",
|
"address_book_edit": "Adresse bearbeiten",
|
||||||
"address_book_empty": "Keine gespeicherten Adressen. Klicken Sie auf 'Neue hinzufügen', um eine hinzuzufügen.",
|
"address_book_empty": "Keine gespeicherten Adressen. Klicken Sie auf 'Neue hinzufügen', um eine hinzuzufügen.",
|
||||||
@@ -53,8 +54,20 @@
|
|||||||
"amount_details": "BETRAGSDETAILS",
|
"amount_details": "BETRAGSDETAILS",
|
||||||
"amount_exceeds_balance": "Betrag übersteigt Guthaben",
|
"amount_exceeds_balance": "Betrag übersteigt Guthaben",
|
||||||
"amount_label": "Betrag:",
|
"amount_label": "Betrag:",
|
||||||
|
"animate_avatars": "Avatare animieren",
|
||||||
"appearance": "ERSCHEINUNGSBILD",
|
"appearance": "ERSCHEINUNGSBILD",
|
||||||
"auto_shield": "Mining automatisch abschirmen",
|
"auto_shield": "Mining automatisch abschirmen",
|
||||||
|
"av_intro": "Mining-Software wird oft als potenziell unerwünscht eingestuft. Führen Sie diese Schritte aus, um das Pool-Mining zu aktivieren:",
|
||||||
|
"av_open_security": "Windows-Sicherheit öffnen",
|
||||||
|
"av_step1": "Schritt 1: Ausnahme hinzufügen",
|
||||||
|
"av_step1_b1": "Öffnen Sie Windows-Sicherheit > Viren- & Bedrohungsschutz",
|
||||||
|
"av_step1_b2": "Klicken Sie auf Einstellungen verwalten > Ausschlüsse > Hinzufügen oder entfernen",
|
||||||
|
"av_step1_b3": "Ordner hinzufügen: %APPDATA%\\ObsidianDragon\\",
|
||||||
|
"av_step2": "Schritt 2: Aus Quarantäne wiederherstellen (falls nötig)",
|
||||||
|
"av_step2_b1": "Windows-Sicherheit > Schutzverlauf",
|
||||||
|
"av_step2_b2": "Suchen Sie xmrig.exe und klicken Sie auf Wiederherstellen",
|
||||||
|
"av_step3": "Schritt 3: Wallet neu starten und erneut versuchen",
|
||||||
|
"av_title": "Windows Defender hat den Miner blockiert",
|
||||||
"available": "Verfügbar",
|
"available": "Verfügbar",
|
||||||
"backup_backing_up": "Sicherung läuft...",
|
"backup_backing_up": "Sicherung läuft...",
|
||||||
"backup_create": "Sicherung erstellen",
|
"backup_create": "Sicherung erstellen",
|
||||||
@@ -62,6 +75,7 @@
|
|||||||
"backup_data": "SICHERUNG & DATEN",
|
"backup_data": "SICHERUNG & DATEN",
|
||||||
"backup_description": "Erstellen Sie eine Sicherung Ihrer wallet.dat-Datei. Diese Datei enthält alle Ihre privaten Schlüssel und den Transaktionsverlauf. Bewahren Sie die Sicherung an einem sicheren Ort auf.",
|
"backup_description": "Erstellen Sie eine Sicherung Ihrer wallet.dat-Datei. Diese Datei enthält alle Ihre privaten Schlüssel und den Transaktionsverlauf. Bewahren Sie die Sicherung an einem sicheren Ort auf.",
|
||||||
"backup_destination": "Sicherungsziel:",
|
"backup_destination": "Sicherungsziel:",
|
||||||
|
"backup_overwrite_confirm": "Dort existiert bereits eine Datei — erneut speichern, um sie zu überschreiben.",
|
||||||
"backup_source": "Quelle: %s",
|
"backup_source": "Quelle: %s",
|
||||||
"backup_tip_external": "Speichern Sie Sicherungen auf externen Laufwerken oder Cloud-Speicher",
|
"backup_tip_external": "Speichern Sie Sicherungen auf externen Laufwerken oder Cloud-Speicher",
|
||||||
"backup_tip_multiple": "Erstellen Sie mehrere Sicherungen an verschiedenen Orten",
|
"backup_tip_multiple": "Erstellen Sie mehrere Sicherungen an verschiedenen Orten",
|
||||||
@@ -70,6 +84,7 @@
|
|||||||
"backup_title": "Wallet sichern",
|
"backup_title": "Wallet sichern",
|
||||||
"backup_wallet": "Wallet sichern...",
|
"backup_wallet": "Wallet sichern...",
|
||||||
"backup_wallet_not_found": "Warnung: wallet.dat nicht am erwarteten Speicherort gefunden",
|
"backup_wallet_not_found": "Warnung: wallet.dat nicht am erwarteten Speicherort gefunden",
|
||||||
|
"backup_warn": "Diese Datei enthält alle Ihre privaten Schlüssel — bewahren Sie sie sicher auf.",
|
||||||
"balance": "Guthaben",
|
"balance": "Guthaben",
|
||||||
"balance_history_collecting": "Guthabenverlauf — Daten werden gesammelt...",
|
"balance_history_collecting": "Guthabenverlauf — Daten werden gesammelt...",
|
||||||
"balance_layout": "Guthaben-Layout",
|
"balance_layout": "Guthaben-Layout",
|
||||||
@@ -114,28 +129,105 @@
|
|||||||
"bootstrap_wallet_protected": "(wallet.dat ist geschützt)",
|
"bootstrap_wallet_protected": "(wallet.dat ist geschützt)",
|
||||||
"bootstrap_warning": "Vorhandene Blockdaten (blocks, chainstate, notarizations) werden gelöscht und ersetzt. Ihre wallet.dat wird NICHT verändert oder gelöscht.",
|
"bootstrap_warning": "Vorhandene Blockdaten (blocks, chainstate, notarizations) werden gelöscht und ersetzt. Ihre wallet.dat wird NICHT verändert oder gelöscht.",
|
||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
|
"change_pass_confirm": "Neue bestätigen:",
|
||||||
|
"change_pass_current": "Aktuelle Passphrase:",
|
||||||
|
"change_pass_new": "Neue Passphrase:",
|
||||||
|
"change_pass_title": "Passphrase ändern",
|
||||||
"characters": "Zeichen",
|
"characters": "Zeichen",
|
||||||
"chat": "Chat",
|
"chat": "Chat",
|
||||||
|
"chat_accent_amber": "Bernstein",
|
||||||
|
"chat_accent_blue": "Blau",
|
||||||
|
"chat_accent_green": "Grün",
|
||||||
|
"chat_accent_pink": "Rosa",
|
||||||
|
"chat_accent_purple": "Lila",
|
||||||
|
"chat_accent_theme": "Design",
|
||||||
|
"chat_add_contact": "Kontakt hinzufügen",
|
||||||
|
"chat_awaiting_key": "Warten auf Antwort",
|
||||||
|
"chat_bubble_minimal": "Minimal",
|
||||||
|
"chat_bubble_rounded": "Abgerundet",
|
||||||
|
"chat_bubble_square": "Eckig",
|
||||||
|
"chat_buffer_loading": "Chat-Puffer: …",
|
||||||
|
"chat_buffer_preparing": "Chat-Puffer: bereite %d/%d vor…",
|
||||||
|
"chat_buffer_ready": "Chat-Puffer: %d/%d bereit",
|
||||||
|
"chat_buffer_sending": "Chat: sende %d Nachrichten…",
|
||||||
|
"chat_buffer_sending_one": "Chat: sende %d Nachricht…",
|
||||||
"chat_cancel": "Abbrechen",
|
"chat_cancel": "Abbrechen",
|
||||||
|
"chat_contact_added": "Kontakt hinzugefügt – benenne ihn in Kontakte um",
|
||||||
"chat_contact_request": "kontaktanfrage",
|
"chat_contact_request": "kontaktanfrage",
|
||||||
|
"chat_copy_address_tip": "Zum Kopieren der Adresse klicken",
|
||||||
|
"chat_density_comfortable": "Komfortabel",
|
||||||
|
"chat_density_compact": "Kompakt",
|
||||||
|
"chat_emoji_color": "Farbig",
|
||||||
|
"chat_emoji_mono": "Monochrom",
|
||||||
|
"chat_emoji_search": "Emoji suchen",
|
||||||
"chat_empty_hint": "Noch keine Unterhaltungen. Nachrichten, die du erhältst, erscheinen hier.",
|
"chat_empty_hint": "Noch keine Unterhaltungen. Nachrichten, die du erhältst, erscheinen hier.",
|
||||||
|
"chat_empty_start": "Starte eine mit \"Neue Unterhaltung\".",
|
||||||
|
"chat_empty_title": "Noch keine Unterhaltungen",
|
||||||
|
"chat_export": "Chat exportieren…",
|
||||||
|
"chat_export_done": "Unterhaltung exportiert",
|
||||||
|
"chat_export_failed": "Exportdatei konnte nicht geschrieben werden.",
|
||||||
|
"chat_export_warn": "Speichert die entschlüsselten Nachrichten als Klartext. Bewahre die Datei sicher auf.",
|
||||||
|
"chat_filter": "Chat",
|
||||||
|
"chat_hidden_toast": "Unterhaltung ausgeblendet – eine neue Nachricht holt sie zurück",
|
||||||
|
"chat_hide": "Ausblenden",
|
||||||
|
"chat_hide_hidden": "Ausgeblendete verbergen",
|
||||||
|
"chat_jump_latest": "Neueste",
|
||||||
|
"chat_len_over": "Nachricht zu lang",
|
||||||
"chat_locked_hint": "Entsperre deine Wallet, um deine Chats zu laden.",
|
"chat_locked_hint": "Entsperre deine Wallet, um deine Chats zu laden.",
|
||||||
"chat_new_button": "Neue Unterhaltung",
|
"chat_mute": "Stummschalten",
|
||||||
|
"chat_new_button": "Neuer Chat",
|
||||||
"chat_new_message": "Nachricht",
|
"chat_new_message": "Nachricht",
|
||||||
|
"chat_new_message_toast": "Neue verschlüsselte Chat-Nachricht",
|
||||||
"chat_new_send": "Anfrage senden",
|
"chat_new_send": "Anfrage senden",
|
||||||
"chat_new_title": "Neue Unterhaltung",
|
"chat_new_title": "Neuer Chat",
|
||||||
"chat_new_zaddr": "z-Adresse des Empfängers",
|
"chat_new_zaddr": "z-Adresse des Empfängers",
|
||||||
|
"chat_no_matches": "Keine Unterhaltungen entsprechen deiner Suche.",
|
||||||
|
"chat_no_z_contacts": "Noch keine Kontakte mit geschützter Adresse",
|
||||||
|
"chat_opt_bubble_accent": "Blasenfarbe",
|
||||||
|
"chat_opt_bubble_style": "Blasenstil",
|
||||||
|
"chat_opt_density": "Nachrichtendichte",
|
||||||
|
"chat_opt_emoji": "Emoji-Stil",
|
||||||
|
"chat_opt_enter_sends": "Eingabetaste sendet",
|
||||||
|
"chat_opt_font_size": "Textgröße",
|
||||||
|
"chat_opt_global_clock": "Globales Uhrzeitformat",
|
||||||
|
"chat_opt_poll": "Abrufrate",
|
||||||
|
"chat_opt_timestamp": "Zeitstempel",
|
||||||
|
"chat_pick_contact": "Aus Kontakten wählen…",
|
||||||
|
"chat_rename": "Kontakt umbenennen",
|
||||||
|
"chat_rename_hint": "Kontaktname",
|
||||||
|
"chat_renamed": "Kontakt umbenannt",
|
||||||
|
"chat_retry": "Wiederholen",
|
||||||
|
"chat_search": "Unterhaltungen durchsuchen",
|
||||||
|
"chat_sec_appearance": "DARSTELLUNG",
|
||||||
|
"chat_sec_messaging": "NACHRICHTEN",
|
||||||
"chat_select_hint": "Wähle eine Unterhaltung aus, um sie anzuzeigen.",
|
"chat_select_hint": "Wähle eine Unterhaltung aus, um sie anzuzeigen.",
|
||||||
"chat_send": "Senden",
|
"chat_send": "Senden",
|
||||||
"chat_send_failed": "nicht gesendet",
|
"chat_send_failed": "nicht gesendet",
|
||||||
|
"chat_sending": "senden…",
|
||||||
|
"chat_settings_done": "Fertig",
|
||||||
|
"chat_settings_section": "CHAT & KONTAKTE",
|
||||||
|
"chat_settings_tip": "Chat anpassen",
|
||||||
|
"chat_settings_title": "Chat-Einstellungen",
|
||||||
|
"chat_show_hidden": "Ausgeblendete anzeigen",
|
||||||
|
"chat_time_now": "jetzt",
|
||||||
"chat_toast_compose_failed": "Nachricht konnte nicht erstellt werden (zu lang?).",
|
"chat_toast_compose_failed": "Nachricht konnte nicht erstellt werden (zu lang?).",
|
||||||
"chat_toast_lite_busy": "Es wird bereits gesendet, oder es ist keine Wallet geöffnet.",
|
"chat_toast_lite_busy": "Es wird bereits gesendet, oder es ist keine Wallet geöffnet.",
|
||||||
|
"chat_toast_need_funds": "Ein kleines geschütztes Guthaben ist nötig, um Chats zu senden (zur Deckung der Gebühr).",
|
||||||
"chat_toast_no_zaddr": "Keine z-Adresse verfügbar, um den Chat zu senden.",
|
"chat_toast_no_zaddr": "Keine z-Adresse verfügbar, um den Chat zu senden.",
|
||||||
"chat_toast_not_connected": "Nicht verbunden – Nachricht nicht gesendet.",
|
"chat_toast_not_connected": "Nicht verbunden – Nachricht nicht gesendet.",
|
||||||
"chat_toast_request_compose_failed": "Kontaktanfrage konnte nicht erstellt werden (ungültige Adresse / ungültiger Text?).",
|
"chat_toast_request_compose_failed": "Kontaktanfrage konnte nicht erstellt werden (ungültige Adresse / ungültiger Text?).",
|
||||||
"chat_toast_request_queued": "Kontaktanfrage in Warteschlange.",
|
"chat_toast_request_queued": "Kontaktanfrage in Warteschlange.",
|
||||||
"chat_toast_waiting_reply": "Warte auf die Antwort des Kontakts, bevor du ihm schreiben kannst.",
|
"chat_toast_waiting_reply": "Warte auf die Antwort des Kontakts, bevor du ihm schreiben kannst.",
|
||||||
|
"chat_today": "Heute",
|
||||||
|
"chat_ts_12h": "12-Stunden",
|
||||||
|
"chat_ts_24h": "24-Stunden",
|
||||||
|
"chat_ts_global": "Global folgen",
|
||||||
|
"chat_ts_global_short": "Global",
|
||||||
|
"chat_unhide": "Einblenden",
|
||||||
|
"chat_unmute": "Stummschaltung aufheben",
|
||||||
|
"chat_verify_key": "Identitätsschlüssel – zum Verifizieren vergleichen",
|
||||||
"chat_waiting_reply": "Warte auf die Antwort dieses Kontakts – sobald er antwortet, kannst du ihm schreiben.",
|
"chat_waiting_reply": "Warte auf die Antwort dieses Kontakts – sobald er antwortet, kannst du ihm schreiben.",
|
||||||
|
"chat_yesterday": "Gestern",
|
||||||
"chat_you": "Du",
|
"chat_you": "Du",
|
||||||
"choose_icon": "Symbol wählen",
|
"choose_icon": "Symbol wählen",
|
||||||
"clear": "Leeren",
|
"clear": "Leeren",
|
||||||
@@ -147,6 +239,7 @@
|
|||||||
"click_copy_address": "Klicken zum Kopieren der Adresse",
|
"click_copy_address": "Klicken zum Kopieren der Adresse",
|
||||||
"click_copy_uri": "Klicken zum Kopieren der URI",
|
"click_copy_uri": "Klicken zum Kopieren der URI",
|
||||||
"click_to_copy": "Klicken zum Kopieren",
|
"click_to_copy": "Klicken zum Kopieren",
|
||||||
|
"clock_format": "Uhrzeitformat",
|
||||||
"close": "Schließen",
|
"close": "Schließen",
|
||||||
"conf_count": "%d Best.",
|
"conf_count": "%d Best.",
|
||||||
"confirm_and_send": "Bestätigen & Senden",
|
"confirm_and_send": "Bestätigen & Senden",
|
||||||
@@ -180,10 +273,24 @@
|
|||||||
"connected_peers": "Verbundene Peers",
|
"connected_peers": "Verbundene Peers",
|
||||||
"connecting": "Verbinde...",
|
"connecting": "Verbinde...",
|
||||||
"console": "Konsole",
|
"console": "Konsole",
|
||||||
|
"console_accents": "Farbakzente",
|
||||||
"console_app": "App",
|
"console_app": "App",
|
||||||
"console_auto_scroll": "Automatisch scrollen",
|
"console_auto_scroll": "Automatisch scrollen",
|
||||||
"console_available_commands": "Verfügbare Befehle:",
|
"console_available_commands": "Verfügbare Befehle:",
|
||||||
|
"console_backend_reference": "Backend-Befehlsreferenz",
|
||||||
|
"console_backend_unavailable": "Kein Backend",
|
||||||
"console_capturing_output": "Erfasse Daemon-Ausgabe...",
|
"console_capturing_output": "Erfasse Daemon-Ausgabe...",
|
||||||
|
"console_cat_advanced": "Erweitert",
|
||||||
|
"console_cat_blockchain": "Blockchain",
|
||||||
|
"console_cat_control": "Steuerung",
|
||||||
|
"console_cat_keys": "Schlüssel & Sicherheit",
|
||||||
|
"console_cat_mining": "Mining",
|
||||||
|
"console_cat_network": "Netzwerk",
|
||||||
|
"console_cat_raw_transactions": "Rohtransaktionen",
|
||||||
|
"console_cat_send": "Senden",
|
||||||
|
"console_cat_sync": "Synchronisierung",
|
||||||
|
"console_cat_utility": "Dienstprogramme",
|
||||||
|
"console_cat_wallet": "Wallet",
|
||||||
"console_clear": "Leeren",
|
"console_clear": "Leeren",
|
||||||
"console_clear_console": "Konsole leeren",
|
"console_clear_console": "Konsole leeren",
|
||||||
"console_cleared": "Konsole geleert",
|
"console_cleared": "Konsole geleert",
|
||||||
@@ -215,18 +322,37 @@
|
|||||||
"console_help_help": " help - Diese Hilfe anzeigen",
|
"console_help_help": " help - Diese Hilfe anzeigen",
|
||||||
"console_help_setgenerate": " setgenerate - Mining steuern",
|
"console_help_setgenerate": " setgenerate - Mining steuern",
|
||||||
"console_help_stop": " stop - Daemon stoppen",
|
"console_help_stop": " stop - Daemon stoppen",
|
||||||
|
"console_last_error": "Letzter Fehler:",
|
||||||
"console_line_count": "%zu Zeilen",
|
"console_line_count": "%zu Zeilen",
|
||||||
"console_matches": "Treffer",
|
"console_matches": "Treffer",
|
||||||
"console_new_lines": "%d neue Zeilen",
|
"console_new_lines": "%d neue Zeilen",
|
||||||
"console_no_daemon": "Kein Daemon",
|
"console_no_daemon": "Kein Daemon",
|
||||||
|
"console_no_output": "(keine Ausgabe)",
|
||||||
"console_not_connected": "Fehler: Nicht mit Daemon verbunden",
|
"console_not_connected": "Fehler: Nicht mit Daemon verbunden",
|
||||||
|
"console_not_connected_lite": "Fehler: Keine Wallet geöffnet",
|
||||||
"console_quit_note": "'quit'/'exit' werden hier nicht benötigt — schließen Sie einfach das Fenster.",
|
"console_quit_note": "'quit'/'exit' werden hier nicht benötigt — schließen Sie einfach das Fenster.",
|
||||||
|
"console_ref_builds": "Ergibt",
|
||||||
|
"console_ref_cancel": "Abbrechen",
|
||||||
|
"console_ref_destructive": "Folgenreich",
|
||||||
|
"console_ref_example": "Beispiel",
|
||||||
|
"console_ref_insert": "In Konsole einfügen",
|
||||||
|
"console_ref_insert_run": "Einfügen & ausführen",
|
||||||
|
"console_ref_no_match": "Keine Befehle gefunden.",
|
||||||
|
"console_ref_no_params": "Erwartet keine Parameter.",
|
||||||
|
"console_ref_optional": "optional",
|
||||||
|
"console_ref_parameters": "Parameter",
|
||||||
|
"console_ref_run": "Ausführen",
|
||||||
|
"console_ref_run_confirm": "%s jetzt ausführen? Dies ist ein folgenreicher Befehl.",
|
||||||
|
"console_ref_search_hint": "Nach Name oder Aufgabe suchen…",
|
||||||
|
"console_ref_select_hint": "Wählen Sie einen Befehl, um zu sehen, was er tut.",
|
||||||
|
"console_ref_value": "Wert",
|
||||||
"console_rpc_reference": "RPC-Befehlsreferenz",
|
"console_rpc_reference": "RPC-Befehlsreferenz",
|
||||||
"console_rpc_trace": "RPC",
|
"console_rpc_trace": "RPC",
|
||||||
"console_scanline": "Konsolen-Scanline",
|
"console_scanline": "Konsolen-Scanline",
|
||||||
"console_search_commands": "Befehle suchen...",
|
"console_search_commands": "Befehle suchen...",
|
||||||
"console_select_all": "Alles auswählen",
|
"console_select_all": "Alles auswählen",
|
||||||
"console_show_app_output": "[App]-Wallet-Protokollzeilen anzeigen",
|
"console_show_app_output": "[App]-Wallet-Protokollzeilen anzeigen",
|
||||||
|
"console_show_backend_ref": "Backend-Befehlsreferenz anzeigen",
|
||||||
"console_show_daemon_output": "Daemon-Ausgabe anzeigen",
|
"console_show_daemon_output": "Daemon-Ausgabe anzeigen",
|
||||||
"console_show_errors_only": "Nur Fehler anzeigen",
|
"console_show_errors_only": "Nur Fehler anzeigen",
|
||||||
"console_show_rpc_ref": "RPC-Befehlsreferenz anzeigen",
|
"console_show_rpc_ref": "RPC-Befehlsreferenz anzeigen",
|
||||||
@@ -239,17 +365,43 @@
|
|||||||
"console_status_stopped": "Gestoppt",
|
"console_status_stopped": "Gestoppt",
|
||||||
"console_status_stopping": "Stoppt",
|
"console_status_stopping": "Stoppt",
|
||||||
"console_status_unknown": "Unbekannt",
|
"console_status_unknown": "Unbekannt",
|
||||||
|
"console_stop_confirm_node": "'stop' fährt den Node herunter und trennt die Wallet. Geben Sie zur Bestätigung erneut 'stop' ein.",
|
||||||
"console_tab_completion": "Tab zur Vervollständigung",
|
"console_tab_completion": "Tab zur Vervollständigung",
|
||||||
|
"console_text_colors": "Textfarben",
|
||||||
|
"console_toggle_accents": "Farbakzente der Zeilen umschalten",
|
||||||
|
"console_toggle_text_color": "Textfarben der Zeilen umschalten",
|
||||||
"console_type_help": "Geben Sie 'help' ein für verfügbare Befehle",
|
"console_type_help": "Geben Sie 'help' ein für verfügbare Befehle",
|
||||||
"console_welcome": "Willkommen bei ObsidianDragon Konsole",
|
"console_welcome": "Willkommen bei ObsidianDragon Konsole",
|
||||||
"console_zoom_in": "Vergrößern",
|
"console_zoom_in": "Vergrößern",
|
||||||
"console_zoom_out": "Verkleinern",
|
"console_zoom_out": "Verkleinern",
|
||||||
|
"contact_avatar": "AVATAR",
|
||||||
|
"contact_avatar_bad_image": "Dieses Bild konnte nicht geladen werden.",
|
||||||
|
"contact_avatar_badge": "Abzeichen",
|
||||||
|
"contact_avatar_badge_hint": "Das Abzeichen wird automatisch anhand des Adresstyps gewählt.",
|
||||||
|
"contact_avatar_choose": "Bild auswählen…",
|
||||||
|
"contact_avatar_copy_failed": "Dieses Bild konnte nicht kopiert werden.",
|
||||||
|
"contact_avatar_icon": "Symbol",
|
||||||
|
"contact_avatar_image": "Bild",
|
||||||
|
"contact_avatar_image_hint": "Das Bild wird in die App kopiert, damit es verfügbar bleibt, wenn das Original verschoben wird.",
|
||||||
|
"contact_avatar_remove": "Entfernen",
|
||||||
|
"contact_avatar_shielded": "Abgeschirmt",
|
||||||
|
"contact_avatar_transparent": "Transparent",
|
||||||
"contact_global": "In jeder Wallet anzeigen (globaler Kontakt)",
|
"contact_global": "In jeder Wallet anzeigen (globaler Kontakt)",
|
||||||
"contact_global_badge_tt": "Globaler Kontakt — in jeder Wallet sichtbar",
|
"contact_global_badge_tt": "Globaler Kontakt — in jeder Wallet sichtbar",
|
||||||
"contact_global_tt": "Ein: Dieser Kontakt bleibt sichtbar, egal welche Wallet Sie laden. Aus: Er gehört nur zur aktuellen Wallet.",
|
"contact_global_tt": "Ein: Dieser Kontakt bleibt sichtbar, egal welche Wallet Sie laden. Aus: Er gehört nur zur aktuellen Wallet.",
|
||||||
|
"contact_preview_addr": "Adresse erscheint hier",
|
||||||
|
"contact_preview_name": "Kontaktname",
|
||||||
|
"contact_wallet_loading": "Die Wallet lädt noch — aktiviere „In jeder Wallet anzeigen“ oder versuche es gleich erneut.",
|
||||||
"contacts": "Kontakte",
|
"contacts": "Kontakte",
|
||||||
|
"contacts_avatar_shape": "Avatarform",
|
||||||
|
"contacts_list_scale": "Listengröße",
|
||||||
"contacts_search_no_match": "Keine passenden Kontakte",
|
"contacts_search_no_match": "Keine passenden Kontakte",
|
||||||
"contacts_search_placeholder": "Kontakte durchsuchen...",
|
"contacts_search_placeholder": "Kontakte durchsuchen...",
|
||||||
|
"contacts_settings_tip": "Kontakte anpassen",
|
||||||
|
"contacts_settings_title": "Kontakteinstellungen",
|
||||||
|
"contacts_shape_circle": "Kreis",
|
||||||
|
"contacts_shape_square": "Quadrat",
|
||||||
|
"contacts_shape_tab": "Reiter",
|
||||||
"copied": "Kopiert!",
|
"copied": "Kopiert!",
|
||||||
"copy": "Kopieren",
|
"copy": "Kopieren",
|
||||||
"copy_address": "Vollständige Adresse kopieren",
|
"copy_address": "Vollständige Adresse kopieren",
|
||||||
@@ -302,6 +454,19 @@
|
|||||||
"date": "Datum",
|
"date": "Datum",
|
||||||
"date_label": "Datum:",
|
"date_label": "Datum:",
|
||||||
"debug_logging": "FEHLERPROTOKOLLIERUNG",
|
"debug_logging": "FEHLERPROTOKOLLIERUNG",
|
||||||
|
"decrypt_desc": "Die Wallet wird exportiert, der Daemon mit einer frischen, unverschlüsselten Wallet neu gestartet und alle Schlüssel werden erneut importiert. Je nach Wallet-Größe kann dies einige Minuten dauern.",
|
||||||
|
"decrypt_error_title": "Entschlüsselung fehlgeschlagen",
|
||||||
|
"decrypt_step_backup": "Verschlüsselte Wallet wird gesichert",
|
||||||
|
"decrypt_step_export": "Wallet-Schlüssel werden exportiert",
|
||||||
|
"decrypt_step_restart": "Daemon wird neu gestartet",
|
||||||
|
"decrypt_step_stop": "Daemon wird gestoppt",
|
||||||
|
"decrypt_step_unlock": "Wallet wird entsperrt",
|
||||||
|
"decrypt_success_desc": "Ihre Wallet ist jetzt unverschlüsselt. Eine Sicherung der verschlüsselten Wallet wurde als wallet.dat.encrypted.bak in Ihrem Datenverzeichnis gespeichert.",
|
||||||
|
"decrypt_success_title": "Wallet erfolgreich entschlüsselt!",
|
||||||
|
"decrypt_title": "Wallet-Verschlüsselung entfernen",
|
||||||
|
"decrypt_wait_general": "Bitte warten. Der Daemon exportiert Schlüssel, startet neu und importiert erneut. Dies kann einige Minuten dauern.",
|
||||||
|
"decrypt_wait_restart": "Warten, bis der Daemon vollständig gestartet ist...",
|
||||||
|
"decrypt_warning": "Dadurch wird die Verschlüsselung Ihrer Wallet entfernt. Ihre privaten Schlüssel werden ungeschützt auf der Festplatte gespeichert.",
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
"delete_blockchain": "Blockchain löschen",
|
"delete_blockchain": "Blockchain löschen",
|
||||||
"delete_blockchain_confirm": "Löschen & Neu synchronisieren",
|
"delete_blockchain_confirm": "Löschen & Neu synchronisieren",
|
||||||
@@ -314,6 +479,15 @@
|
|||||||
"download_bootstrap": "Bootstrap herunterladen",
|
"download_bootstrap": "Bootstrap herunterladen",
|
||||||
"dragonx_green": "DragonX (Grün)",
|
"dragonx_green": "DragonX (Grün)",
|
||||||
"edit": "Bearbeiten",
|
"edit": "Bearbeiten",
|
||||||
|
"enc_confirm": "Bestätigen:",
|
||||||
|
"enc_desc": "Die Verschlüsselung Ihrer Wallet schützt Ihre privaten Schlüssel mit einer Passphrase. Nach der Verschlüsselung wird der Daemon neu gestartet.",
|
||||||
|
"enc_encrypting": "Wallet wird verschlüsselt...",
|
||||||
|
"enc_pin_desc": "Mit einer 4-8-stelligen PIN entsperren Sie Ihre Wallet, ohne jedes Mal die vollständige Passphrase einzugeben.",
|
||||||
|
"enc_pin_set_ok": "PIN erfolgreich festgelegt",
|
||||||
|
"enc_pin_skipped": "PIN übersprungen. Sie können später in den Einstellungen eine festlegen.",
|
||||||
|
"enc_pin_vault_fail": "PIN-Tresor konnte nicht erstellt werden",
|
||||||
|
"enc_success": "Wallet erfolgreich verschlüsselt!",
|
||||||
|
"enc_wait": "Bitte warten, schließen Sie die Anwendung nicht.",
|
||||||
"error": "Fehler",
|
"error": "Fehler",
|
||||||
"error_format": "Fehler: %s",
|
"error_format": "Fehler: %s",
|
||||||
"est_time_to_block": "Gesch. Zeit bis Block",
|
"est_time_to_block": "Gesch. Zeit bis Block",
|
||||||
@@ -333,23 +507,34 @@
|
|||||||
"explorer_mempool": "Mempool",
|
"explorer_mempool": "Mempool",
|
||||||
"explorer_mempool_size": "Größe",
|
"explorer_mempool_size": "Größe",
|
||||||
"explorer_mempool_txs": "Transaktionen",
|
"explorer_mempool_txs": "Transaktionen",
|
||||||
|
"explorer_next_block": "Nächster Block",
|
||||||
"explorer_no_results": "Keine passenden zwischengespeicherten Blöcke",
|
"explorer_no_results": "Keine passenden zwischengespeicherten Blöcke",
|
||||||
"explorer_not_connected": "Nicht mit dem Daemon verbunden — Block- oder Transaktions-Hash kann nicht nachgeschlagen werden",
|
"explorer_not_connected": "Nicht mit dem Daemon verbunden — Block- oder Transaktions-Hash kann nicht nachgeschlagen werden",
|
||||||
|
"explorer_prev_block": "Vorheriger Block",
|
||||||
"explorer_recent_blocks": "Letzte Blöcke",
|
"explorer_recent_blocks": "Letzte Blöcke",
|
||||||
"explorer_search": "Suchen",
|
"explorer_search": "Suchen",
|
||||||
"explorer_section": "EXPLORER",
|
"explorer_section": "EXPLORER",
|
||||||
"explorer_tx_outputs": "Ausgaben",
|
"explorer_tx_outputs": "Ausgaben",
|
||||||
"explorer_tx_size": "Größe",
|
"explorer_tx_size": "Größe",
|
||||||
|
"explorer_txid": "TxID:",
|
||||||
"export": "Exportieren",
|
"export": "Exportieren",
|
||||||
"export_csv": "CSV exportieren",
|
"export_csv": "CSV exportieren",
|
||||||
"export_keys_btn": "Schlüssel exportieren",
|
"export_keys_btn": "Schlüssel exportieren",
|
||||||
"export_keys_danger": "ACHTUNG: Dies exportiert ALLE privaten Schlüssel aus Ihrer Wallet! Jeder mit Zugriff auf diese Datei kann Ihre Gelder stehlen. Sicher aufbewahren und nach Gebrauch löschen.",
|
"export_keys_danger": "ACHTUNG: Dies exportiert ALLE privaten Schlüssel aus Ihrer Wallet! Jeder mit Zugriff auf diese Datei kann Ihre Gelder stehlen. Sicher aufbewahren und nach Gebrauch löschen.",
|
||||||
"export_keys_include_t": "T-Adressen einschließen (transparent)",
|
"export_keys_include_t": "T-Adressen einschließen (transparent)",
|
||||||
"export_keys_include_z": "Z-Adressen einschließen (abgeschirmt)",
|
"export_keys_include_z": "Z-Adressen einschließen (abgeschirmt)",
|
||||||
|
"export_keys_none_addrs": "Keine Adressen zum Exportieren",
|
||||||
|
"export_keys_none_result": "Keine Schlüssel exportiert (0 von %d) — entsperren Sie die Wallet und versuchen Sie es erneut.",
|
||||||
|
"export_keys_none_toast": "Es konnten keine Schlüssel exportiert werden — ist die Wallet entsperrt?",
|
||||||
|
"export_keys_not_connected": "Nicht mit dem Daemon verbunden",
|
||||||
"export_keys_options": "Export-Optionen:",
|
"export_keys_options": "Export-Optionen:",
|
||||||
|
"export_keys_partial": "%d von %d Schlüsseln exportiert — unvollständig (einige hatten keinen Ausgabeschlüssel oder die Wallet ist gesperrt).",
|
||||||
|
"export_keys_partial_toast": "Teilexport: %d von %d Schlüsseln",
|
||||||
"export_keys_progress": "Exportiere %d/%d...",
|
"export_keys_progress": "Exportiere %d/%d...",
|
||||||
|
"export_keys_select_type": "Wählen Sie mindestens einen Adresstyp",
|
||||||
"export_keys_success": "Schlüssel erfolgreich exportiert",
|
"export_keys_success": "Schlüssel erfolgreich exportiert",
|
||||||
"export_keys_title": "Alle privaten Schlüssel exportieren",
|
"export_keys_title": "Alle privaten Schlüssel exportieren",
|
||||||
|
"export_keys_write_fail": "Schlüsseldatei konnte nicht geschrieben werden.",
|
||||||
"export_private_key": "Privaten Schlüssel exportieren",
|
"export_private_key": "Privaten Schlüssel exportieren",
|
||||||
"export_tx_count": "%zu Transaktionen als CSV exportieren.",
|
"export_tx_count": "%zu Transaktionen als CSV exportieren.",
|
||||||
"export_tx_file_fail": "CSV-Datei konnte nicht erstellt werden",
|
"export_tx_file_fail": "CSV-Datei konnte nicht erstellt werden",
|
||||||
@@ -389,23 +574,50 @@
|
|||||||
"hide_qr": "QR ausblenden",
|
"hide_qr": "QR ausblenden",
|
||||||
"hide_zero_balances": "Nullsalden ausblenden",
|
"hide_zero_balances": "Nullsalden ausblenden",
|
||||||
"history": "Verlauf",
|
"history": "Verlauf",
|
||||||
|
"img_picker_count": "%d Bild(er) in diesem Ordner",
|
||||||
|
"img_picker_empty": "Dieser Ordner enthält keine Unterordner oder Bilder.",
|
||||||
|
"img_picker_none": "Keine Bilder in diesem Ordner",
|
||||||
|
"img_picker_pictures": "Bilderordner",
|
||||||
|
"img_picker_title": "Bild auswählen",
|
||||||
|
"img_picker_use": "Bild verwenden",
|
||||||
"immature_type": "Unreif",
|
"immature_type": "Unreif",
|
||||||
"import": "Importieren",
|
"import": "Importieren",
|
||||||
|
"import_key_address": "Adresse:",
|
||||||
"import_key_btn": "Schlüssel importieren",
|
"import_key_btn": "Schlüssel importieren",
|
||||||
|
"import_key_done": "Importiert. Wallet wird neu gescannt.",
|
||||||
|
"import_key_field": "Schlüssel",
|
||||||
"import_key_formats": "Unterstützte Schlüsselformate:",
|
"import_key_formats": "Unterstützte Schlüsselformate:",
|
||||||
"import_key_full_rescan": "(0 = vollständiger Rescan)",
|
"import_key_full_rescan": "(0 = vollständiger Rescan)",
|
||||||
|
"import_key_import": "Importieren",
|
||||||
"import_key_label": "Privater Schlüssel:",
|
"import_key_label": "Privater Schlüssel:",
|
||||||
|
"import_key_need_node": "Verbinden Sie einen laufenden Node, um einen Schlüssel zu importieren.",
|
||||||
"import_key_no_valid": "Keine gültigen Schlüssel in der Eingabe gefunden",
|
"import_key_no_valid": "Keine gültigen Schlüssel in der Eingabe gefunden",
|
||||||
"import_key_progress": "Importiere %d/%d...",
|
"import_key_progress": "Importiere %d/%d...",
|
||||||
"import_key_rescan": "Blockchain nach Import neu scannen",
|
"import_key_rescan": "Blockchain nach Import neu scannen",
|
||||||
|
"import_key_rescanning": "Import & erneuter Scan — das kann einige Minuten dauern",
|
||||||
|
"import_key_reveal_tip": "Schlüssel ein-/ausblenden",
|
||||||
"import_key_start_height": "Starthöhe:",
|
"import_key_start_height": "Starthöhe:",
|
||||||
"import_key_success": "Schlüssel erfolgreich importiert",
|
"import_key_success": "Schlüssel erfolgreich importiert",
|
||||||
"import_key_t_format": "T-Adresse WIF private Schlüssel",
|
"import_key_t_format": "T-Adresse WIF private Schlüssel",
|
||||||
"import_key_title": "Privaten Schlüssel importieren",
|
"import_key_title": "Privaten Schlüssel importieren",
|
||||||
"import_key_tooltip": "Geben Sie einen oder mehrere private Schlüssel ein, einen pro Zeile.\nUnterstützt sowohl z-Adresse als auch t-Adresse Schlüssel.\nZeilen die mit # beginnen werden als Kommentare behandelt.",
|
"import_key_tooltip": "Geben Sie einen oder mehrere private Schlüssel ein, einen pro Zeile.\nUnterstützt sowohl z-Adresse als auch t-Adresse Schlüssel.\nZeilen die mit # beginnen werden als Kommentare behandelt.",
|
||||||
|
"import_key_type_tkey": "Transparenter privater Schlüssel",
|
||||||
|
"import_key_type_unknown": "Unbekanntes Schlüsselformat",
|
||||||
|
"import_key_type_zspend": "Abgeschirmter Ausgabeschlüssel",
|
||||||
|
"import_key_type_zview": "Abgeschirmter Anzeigeschlüssel (nur Lesen)",
|
||||||
|
"import_key_warn": "Importieren Sie nur einen Schlüssel, der Ihnen gehört — er gewährt Zugriff auf dessen Guthaben.",
|
||||||
"import_key_warning": "Warnung: Teilen Sie niemals Ihre privaten Schlüssel! Das Importieren von Schlüsseln aus nicht vertrauenswürdigen Quellen kann Ihr Wallet gefährden.",
|
"import_key_warning": "Warnung: Teilen Sie niemals Ihre privaten Schlüssel! Das Importieren von Schlüsseln aus nicht vertrauenswürdigen Quellen kann Ihr Wallet gefährden.",
|
||||||
|
"import_key_wrong_type": "Das sieht nach einem Anzeigeschlüssel aus. Verwenden Sie stattdessen \"Anzeigeschlüssel importieren\".",
|
||||||
"import_key_z_format": "Z-Adresse Ausgabeschlüssel (secret-extended-key-...)",
|
"import_key_z_format": "Z-Adresse Ausgabeschlüssel (secret-extended-key-...)",
|
||||||
"import_private_key": "Privaten Schlüssel importieren...",
|
"import_private_key": "Privaten Schlüssel importieren...",
|
||||||
|
"import_scan_hint": "0 = von Anfang an neu scannen",
|
||||||
|
"import_scan_label": "Ab Blockhöhe scannen (optional)",
|
||||||
|
"import_scan_tip": "aktuelle Höhe",
|
||||||
|
"import_scan_transparent": "Transparente Schlüssel scannen immer vollständig neu",
|
||||||
|
"import_viewkey_field": "Anzeigeschlüssel",
|
||||||
|
"import_viewkey_note": "Nur Lesen: Ein Anzeigeschlüssel zeigt Guthaben und Transaktionen einer Adresse, kann deren Mittel aber nicht ausgeben.",
|
||||||
|
"import_viewkey_title": "Anzeigeschlüssel importieren",
|
||||||
|
"import_viewkey_wrong_type": "Das sieht nach einem Ausgabeschlüssel aus. Verwenden Sie stattdessen \"Privaten Schlüssel importieren\".",
|
||||||
"incorrect_passphrase": "Falsches Passwort",
|
"incorrect_passphrase": "Falsches Passwort",
|
||||||
"incorrect_pin": "Falsche PIN",
|
"incorrect_pin": "Falsche PIN",
|
||||||
"insufficient_funds": "Unzureichendes Guthaben für diesen Betrag plus Gebühr.",
|
"insufficient_funds": "Unzureichendes Guthaben für diesen Betrag plus Gebühr.",
|
||||||
@@ -414,6 +626,7 @@
|
|||||||
"keep": "Behalten",
|
"keep": "Behalten",
|
||||||
"keep_daemon": "Daemon weiterlaufen lassen",
|
"keep_daemon": "Daemon weiterlaufen lassen",
|
||||||
"key_export_click_retrieve": "Klicken Sie, um den Schlüssel aus Ihrer Wallet abzurufen",
|
"key_export_click_retrieve": "Klicken Sie, um den Schlüssel aus Ihrer Wallet abzurufen",
|
||||||
|
"key_export_failed": "Schlüssel konnte nicht exportiert werden — entsperren Sie die Wallet (falls verschlüsselt) und versuchen Sie es erneut.",
|
||||||
"key_export_fetching": "Schlüssel wird aus Wallet abgerufen...",
|
"key_export_fetching": "Schlüssel wird aus Wallet abgerufen...",
|
||||||
"key_export_private_key": "Privater Schlüssel:",
|
"key_export_private_key": "Privater Schlüssel:",
|
||||||
"key_export_private_warning": "Halten Sie diesen Schlüssel GEHEIM! Jeder mit diesem Schlüssel kann Ihre Gelder ausgeben. Teilen Sie ihn niemals online oder mit nicht vertrauenswürdigen Parteien.",
|
"key_export_private_warning": "Halten Sie diesen Schlüssel GEHEIM! Jeder mit diesem Schlüssel kann Ihre Gelder ausgeben. Teilen Sie ihn niemals online oder mit nicht vertrauenswürdigen Parteien.",
|
||||||
@@ -431,6 +644,7 @@
|
|||||||
"lite_birthday_backup": "Geburtstag: %llu (auch diesen sichern)",
|
"lite_birthday_backup": "Geburtstag: %llu (auch diesen sichern)",
|
||||||
"lite_birthday_hint": "Blockhöhe, ab der gescannt werden soll. Bei 0 belassen, falls unbekannt (langsamerer vollständiger Scan).",
|
"lite_birthday_hint": "Blockhöhe, ab der gescannt werden soll. Bei 0 belassen, falls unbekannt (langsamerer vollständiger Scan).",
|
||||||
"lite_birthday_label": "Geburtsblock",
|
"lite_birthday_label": "Geburtsblock",
|
||||||
|
"lite_console_backend_commands": "Backend-Befehle:",
|
||||||
"lite_console_help_passthrough": "Jede andere Eingabe wird als Lite-Wallet-Konsolenbefehl ausgeführt.",
|
"lite_console_help_passthrough": "Jede andere Eingabe wird als Lite-Wallet-Konsolenbefehl ausgeführt.",
|
||||||
"lite_copy": "Kopieren",
|
"lite_copy": "Kopieren",
|
||||||
"lite_could_not_write": "Konnte nicht schreiben ",
|
"lite_could_not_write": "Konnte nicht schreiben ",
|
||||||
@@ -448,6 +662,7 @@
|
|||||||
"lite_net_add_url_hint": "https://ihr-lite-server",
|
"lite_net_add_url_hint": "https://ihr-lite-server",
|
||||||
"lite_net_checking": "wird geprüft…",
|
"lite_net_checking": "wird geprüft…",
|
||||||
"lite_net_connected": "Verbunden",
|
"lite_net_connected": "Verbunden",
|
||||||
|
"lite_net_connecting": "Verbinde…",
|
||||||
"lite_net_custom": "Benutzerdefiniert",
|
"lite_net_custom": "Benutzerdefiniert",
|
||||||
"lite_net_disconnected": "Nicht verbunden",
|
"lite_net_disconnected": "Nicht verbunden",
|
||||||
"lite_net_hidden_section": "Ausgeblendete Server",
|
"lite_net_hidden_section": "Ausgeblendete Server",
|
||||||
@@ -534,6 +749,10 @@
|
|||||||
"market_btc_price": "BTC PREIS",
|
"market_btc_price": "BTC PREIS",
|
||||||
"market_cap": "Marktkapitalisierung",
|
"market_cap": "Marktkapitalisierung",
|
||||||
"market_cap_short": "Kap.",
|
"market_cap_short": "Kap.",
|
||||||
|
"market_chart_loading": "Preisverlauf wird geladen",
|
||||||
|
"market_col_name": "Name",
|
||||||
|
"market_col_trend": "Trend",
|
||||||
|
"market_col_value": "Wert",
|
||||||
"market_iv_1d": "1T",
|
"market_iv_1d": "1T",
|
||||||
"market_iv_1h": "1S",
|
"market_iv_1h": "1S",
|
||||||
"market_iv_1m": "1M",
|
"market_iv_1m": "1M",
|
||||||
@@ -542,11 +761,18 @@
|
|||||||
"market_no_history": "Kein Preisverlauf verfügbar",
|
"market_no_history": "Kein Preisverlauf verfügbar",
|
||||||
"market_no_price": "Keine Preisdaten",
|
"market_no_price": "Keine Preisdaten",
|
||||||
"market_now": "Jetzt",
|
"market_now": "Jetzt",
|
||||||
|
"market_opt_chart_style": "Diagrammstil",
|
||||||
"market_pct_shielded": "%.0f%% Abgeschirmt",
|
"market_pct_shielded": "%.0f%% Abgeschirmt",
|
||||||
"market_portfolio": "PORTFOLIO",
|
"market_portfolio": "PORTFOLIO",
|
||||||
"market_price_loading": "Preisdaten werden geladen...",
|
"market_price_loading": "Preisdaten werden geladen...",
|
||||||
"market_price_unavailable": "Preisdaten nicht verfügbar",
|
"market_price_unavailable": "Preisdaten nicht verfügbar",
|
||||||
"market_refresh_price": "Preisdaten aktualisieren",
|
"market_refresh_price": "Preisdaten aktualisieren",
|
||||||
|
"market_settings_tip": "Marktoptionen",
|
||||||
|
"market_settings_title": "Markteinstellungen",
|
||||||
|
"market_style_candle": "Zu Kerzenchart wechseln",
|
||||||
|
"market_style_candle_label": "Kerzen",
|
||||||
|
"market_style_line": "Zum Liniendiagramm wechseln",
|
||||||
|
"market_style_line_label": "Linie",
|
||||||
"market_trade_on": "Handeln auf %s",
|
"market_trade_on": "Handeln auf %s",
|
||||||
"market_updated": "\\xc2\\xb7 Aktualisiert %s",
|
"market_updated": "\\xc2\\xb7 Aktualisiert %s",
|
||||||
"market_vol_short": "Vol.",
|
"market_vol_short": "Vol.",
|
||||||
@@ -736,6 +962,7 @@
|
|||||||
"output_filename": "Ausgabedateiname:",
|
"output_filename": "Ausgabedateiname:",
|
||||||
"overview": "Übersicht",
|
"overview": "Übersicht",
|
||||||
"paste": "Einfügen",
|
"paste": "Einfügen",
|
||||||
|
"paste_clip_empty": "Zwischenablage ist leer",
|
||||||
"paste_from_clipboard": "Aus Zwischenablage einfügen",
|
"paste_from_clipboard": "Aus Zwischenablage einfügen",
|
||||||
"pay_from": "Zahlen von",
|
"pay_from": "Zahlen von",
|
||||||
"payment_request": "ZAHLUNGSANFRAGE",
|
"payment_request": "ZAHLUNGSANFRAGE",
|
||||||
@@ -787,7 +1014,14 @@
|
|||||||
"peers_upper": "PEERS",
|
"peers_upper": "PEERS",
|
||||||
"peers_version": "Version",
|
"peers_version": "Version",
|
||||||
"pending": "Ausstehend",
|
"pending": "Ausstehend",
|
||||||
|
"pin_change_desc": "Ändern Sie Ihre Entsperr-PIN. Sie benötigen Ihre aktuelle PIN und eine neue PIN.",
|
||||||
|
"pin_confirm_new_label": "Neue PIN bestätigen:",
|
||||||
|
"pin_current_label": "Aktuelle PIN:",
|
||||||
|
"pin_new_label": "Neue PIN (4-8 Ziffern):",
|
||||||
"pin_not_set": "PIN nicht gesetzt. Verwenden Sie das Passwort zum Entsperren.",
|
"pin_not_set": "PIN nicht gesetzt. Verwenden Sie das Passwort zum Entsperren.",
|
||||||
|
"pin_remove_desc": "Geben Sie Ihre aktuelle PIN ein, um das Entfernen zu bestätigen. Sie benötigen dann Ihre vollständige Passphrase zum Entsperren.",
|
||||||
|
"pin_setup_desc": "Legen Sie eine 4-8-stellige PIN für die schnelle Wallet-Entsperrung fest. Ihre Wallet-Passphrase wird mit dieser PIN verschlüsselt und lokal gespeichert.",
|
||||||
|
"pin_wallet_passphrase": "Wallet-Passphrase:",
|
||||||
"ping": "Ping",
|
"ping": "Ping",
|
||||||
"portfolio_add_entry": "Eintrag hinzufügen",
|
"portfolio_add_entry": "Eintrag hinzufügen",
|
||||||
"portfolio_add_to": "Zum Portfolio hinzufügen",
|
"portfolio_add_to": "Zum Portfolio hinzufügen",
|
||||||
@@ -826,6 +1060,9 @@
|
|||||||
"portfolio_remove_from": "Aus Portfolio entfernen",
|
"portfolio_remove_from": "Aus Portfolio entfernen",
|
||||||
"portfolio_revert": "Zurücksetzen",
|
"portfolio_revert": "Zurücksetzen",
|
||||||
"portfolio_save": "Speichern",
|
"portfolio_save": "Speichern",
|
||||||
|
"portfolio_save_need_address": "Fügen Sie mindestens eine Adresse hinzu, um zu speichern.",
|
||||||
|
"portfolio_save_need_name": "Geben Sie einen Namen ein, um diese Gruppe zu speichern.",
|
||||||
|
"portfolio_save_need_price": "Geben Sie einen manuellen Preis über 0 ein, um zu speichern.",
|
||||||
"portfolio_search": "Adressen durchsuchen…",
|
"portfolio_search": "Adressen durchsuchen…",
|
||||||
"portfolio_search_icons": "Symbole suchen…",
|
"portfolio_search_icons": "Symbole suchen…",
|
||||||
"portfolio_select_all": "Alle",
|
"portfolio_select_all": "Alle",
|
||||||
@@ -839,11 +1076,12 @@
|
|||||||
"portfolio_spark_min": "Minute",
|
"portfolio_spark_min": "Minute",
|
||||||
"portfolio_spark_month": "Monat",
|
"portfolio_spark_month": "Monat",
|
||||||
"portfolio_spark_week": "Woche",
|
"portfolio_spark_week": "Woche",
|
||||||
"portfolio_style_compact": "Kompakte Zeilen",
|
"portfolio_style_compact": "Tabelle",
|
||||||
"portfolio_style_detailed": "Detaillierte Zeilen",
|
"portfolio_style_detailed": "Karten",
|
||||||
"portfolio_style_featured": "Hervorgehobene Zeilen",
|
"portfolio_style_featured": "Hervorgehoben",
|
||||||
"portfolio_style_label": "Portfolio-Stil",
|
"portfolio_style_label": "Portfolio-Stil",
|
||||||
"portfolio_untitled": "Ohne Titel",
|
"portfolio_untitled": "Ohne Titel",
|
||||||
|
"portfolio_wallet_loading": "Warte, bis die Wallet fertig geladen ist, um eine Gruppe hinzuzufügen.",
|
||||||
"price_chart": "Preisdiagramm",
|
"price_chart": "Preisdiagramm",
|
||||||
"privacy_great": "Großartige Privatsphäre!",
|
"privacy_great": "Großartige Privatsphäre!",
|
||||||
"privacy_low": "Geringe Privatsphäre — Gelder abschirmen",
|
"privacy_low": "Geringe Privatsphäre — Gelder abschirmen",
|
||||||
@@ -973,6 +1211,7 @@
|
|||||||
"send_amount": "Betrag",
|
"send_amount": "Betrag",
|
||||||
"send_amount_details": "BETRAGSDETAILS",
|
"send_amount_details": "BETRAGSDETAILS",
|
||||||
"send_amount_upper": "BETRAG",
|
"send_amount_upper": "BETRAG",
|
||||||
|
"send_cannot_send_now": "Senden derzeit nicht möglich — prüfen Sie Verbindung, Synchronisierung und verfügbares Guthaben.",
|
||||||
"send_clear_fields": "Alle Formularfelder leeren?",
|
"send_clear_fields": "Alle Formularfelder leeren?",
|
||||||
"send_contacts_button": "Aus Kontakten auswählen",
|
"send_contacts_button": "Aus Kontakten auswählen",
|
||||||
"send_copy_error": "Fehler kopieren",
|
"send_copy_error": "Fehler kopieren",
|
||||||
@@ -1061,7 +1300,8 @@
|
|||||||
"settings_gradient_bg": "Hintergrund-Verlauf",
|
"settings_gradient_bg": "Hintergrund-Verlauf",
|
||||||
"settings_gradient_desc": "Strukturierte Hintergründe durch sanfte Verläufe ersetzen",
|
"settings_gradient_desc": "Strukturierte Hintergründe durch sanfte Verläufe ersetzen",
|
||||||
"settings_idle_after": "nach",
|
"settings_idle_after": "nach",
|
||||||
"settings_import_key": "Schlüssel importieren...",
|
"settings_import_key": "Privaten Schlüssel importieren...",
|
||||||
|
"settings_import_viewkey": "Anzeigeschlüssel importieren...",
|
||||||
"settings_language_note": "Hinweis: Manche Texte erfordern einen Neustart zur Aktualisierung",
|
"settings_language_note": "Hinweis: Manche Texte erfordern einen Neustart zur Aktualisierung",
|
||||||
"settings_lock_now": "Jetzt sperren",
|
"settings_lock_now": "Jetzt sperren",
|
||||||
"settings_locked": "Gesperrt",
|
"settings_locked": "Gesperrt",
|
||||||
@@ -1110,6 +1350,7 @@
|
|||||||
"shield_in_progress": "Vorgang läuft...",
|
"shield_in_progress": "Vorgang läuft...",
|
||||||
"shield_max_utxos": "Max. UTXOs pro Vorgang",
|
"shield_max_utxos": "Max. UTXOs pro Vorgang",
|
||||||
"shield_merge_done": "Abschirmung/Zusammenführung abgeschlossen!",
|
"shield_merge_done": "Abschirmung/Zusammenführung abgeschlossen!",
|
||||||
|
"shield_no_zaddr_hint": "Noch keine geschützte (z-)Adresse — erstellen Sie zuerst eine auf der Registerkarte Empfangen.",
|
||||||
"shield_op_failed": "Vorgang fehlgeschlagen: ",
|
"shield_op_failed": "Vorgang fehlgeschlagen: ",
|
||||||
"shield_op_submitted": "Vorgang übermittelt: ",
|
"shield_op_submitted": "Vorgang übermittelt: ",
|
||||||
"shield_operation_id": "Vorgangs-ID: %s",
|
"shield_operation_id": "Vorgangs-ID: %s",
|
||||||
@@ -1148,6 +1389,31 @@
|
|||||||
"submitting_transaction": "Transaktion wird übermittelt...",
|
"submitting_transaction": "Transaktion wird übermittelt...",
|
||||||
"success": "Erfolg",
|
"success": "Erfolg",
|
||||||
"summary": "Zusammenfassung",
|
"summary": "Zusammenfassung",
|
||||||
|
"sweep_button": "Fegen",
|
||||||
|
"sweep_caveat": "Importiert den Schlüssel, um eine Transaktion zu signieren, die alle Mittel an Ihre Adresse verschiebt. Der Schlüssel bleibt mit leerem Guthaben in Ihrer Wallet.",
|
||||||
|
"sweep_dest_label": "Gefegte Mittel senden an",
|
||||||
|
"sweep_dest_new": "Neue abgeschirmte Adresse (empfohlen)",
|
||||||
|
"sweep_done": "Fertig — Mittel an Ihre Adresse gefegt.",
|
||||||
|
"sweep_to": "Gefegt an:",
|
||||||
|
"sweep_toggle": "In meine Wallet fegen (Schlüssel nicht behalten)",
|
||||||
|
"sweep_tx": "Transaktion:",
|
||||||
|
"switch_corrupt_body": "Diese Wallet scheint beschädigt zu sein – der Knoten konnte sie nicht öffnen. Aus einem Backup wiederherstellen, neu erstellen oder eine Reparatur versuchen.",
|
||||||
|
"switch_corrupt_repair": "Reparatur versuchen (Salvage)",
|
||||||
|
"switch_progress_background": "Im Hintergrund fortsetzen",
|
||||||
|
"switch_progress_default_wallet": "Standard-Wallet",
|
||||||
|
"switch_progress_elapsed": "Vergangen",
|
||||||
|
"switch_progress_external_wallet": "Externe Wallet",
|
||||||
|
"switch_progress_failed_title": "Wallet-Wechsel fehlgeschlagen",
|
||||||
|
"switch_progress_from_label": "von",
|
||||||
|
"switch_progress_hint": "Ein sauberes Herunterfahren kann bis zu einer Minute dauern.",
|
||||||
|
"switch_progress_reconnecting": "Neu verbinden",
|
||||||
|
"switch_progress_starting": "Knoten wird mit der neuen Wallet gestartet",
|
||||||
|
"switch_progress_stopping": "Aktueller Knoten wird gestoppt",
|
||||||
|
"switch_progress_title": "Wallet wird gewechselt",
|
||||||
|
"switch_stopnode_body": "Beim Wallet-Wechsel wird der Knoten mit der ausgewählten Wallet neu gestartet. Der laufende Knoten wird gestoppt und mit der neuen Wallet neu gestartet – wenn Sie ihn absichtlich laufen ließen, startet er automatisch wieder.",
|
||||||
|
"switch_stopnode_confirm": "Knoten stoppen & wechseln",
|
||||||
|
"switch_stopnode_title": "Laufenden Knoten stoppen?",
|
||||||
|
"switch_stopnode_warn": "Es läuft bereits ein Knoten, den diese Wallet nicht gestartet hat.",
|
||||||
"syncing": "Synchronisiere...",
|
"syncing": "Synchronisiere...",
|
||||||
"t_address": "T-Adresse",
|
"t_address": "T-Adresse",
|
||||||
"t_addresses": "T-Adressen",
|
"t_addresses": "T-Adressen",
|
||||||
@@ -1183,8 +1449,10 @@
|
|||||||
"transfer_to": "Überweisen an:",
|
"transfer_to": "Überweisen an:",
|
||||||
"transparent": "Transparent",
|
"transparent": "Transparent",
|
||||||
"transparent_address": "Transparente Adresse",
|
"transparent_address": "Transparente Adresse",
|
||||||
|
"try_again": "Erneut versuchen",
|
||||||
"tt_addr_url": "Basis-URL zum Anzeigen von Adressen in einem Block-Explorer",
|
"tt_addr_url": "Basis-URL zum Anzeigen von Adressen in einem Block-Explorer",
|
||||||
"tt_address_book": "Gespeicherte Adressen für schnelles Senden verwalten",
|
"tt_address_book": "Gespeicherte Adressen für schnelles Senden verwalten",
|
||||||
|
"tt_animate_avatars": "Animierte Kontakt-Avatare (GIF / WebP) abspielen; aus zeigt nur das erste Bild",
|
||||||
"tt_auto_lock": "Wallet nach dieser Inaktivitätszeit sperren",
|
"tt_auto_lock": "Wallet nach dieser Inaktivitätszeit sperren",
|
||||||
"tt_auto_shield": "Transparentes Guthaben automatisch an geschirmte Adressen für Datenschutz verschieben",
|
"tt_auto_shield": "Transparentes Guthaben automatisch an geschirmte Adressen für Datenschutz verschieben",
|
||||||
"tt_backup": "Eine Sicherungskopie Ihrer wallet.dat erstellen",
|
"tt_backup": "Eine Sicherungskopie Ihrer wallet.dat erstellen",
|
||||||
@@ -1192,10 +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_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",
|
||||||
@@ -1209,10 +1487,34 @@
|
|||||||
"tt_font_scale": "Alle Texte und UI skalieren (1.0x = Standard, bis 1.5x).",
|
"tt_font_scale": "Alle Texte und UI skalieren (1.0x = Standard, bis 1.5x).",
|
||||||
"tt_idle_delay": "Wie lange vor dem Start des Minings gewartet werden soll",
|
"tt_idle_delay": "Wie lange vor dem Start des Minings gewartet werden soll",
|
||||||
"tt_import_key": "Einen privaten Schlüssel (zkey oder tkey) in diese Wallet importieren",
|
"tt_import_key": "Einen privaten Schlüssel (zkey oder tkey) in diese Wallet importieren",
|
||||||
|
"tt_import_viewkey": "Einen abgeschirmten Anzeigeschlüssel importieren, um eine Adresse anzusehen (nur Lesen)",
|
||||||
"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",
|
||||||
@@ -1233,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",
|
||||||
@@ -1257,6 +1564,7 @@
|
|||||||
"tt_website": "Die DragonX-Website öffnen",
|
"tt_website": "Die DragonX-Website öffnen",
|
||||||
"tt_window_opacity": "Hintergrund-Deckkraft (niedriger = Desktop durch Fenster sichtbar)",
|
"tt_window_opacity": "Hintergrund-Deckkraft (niedriger = Desktop durch Fenster sichtbar)",
|
||||||
"tt_wizard": "Den Ersteinrichtungsassistenten erneut ausführen\\nDer Daemon wird neu gestartet",
|
"tt_wizard": "Den Ersteinrichtungsassistenten erneut ausführen\\nDer Daemon wird neu gestartet",
|
||||||
|
"tx_chat_badge": "Nachricht",
|
||||||
"tx_confirmations": "%d Bestätigungen",
|
"tx_confirmations": "%d Bestätigungen",
|
||||||
"tx_details_title": "Transaktionsdetails",
|
"tx_details_title": "Transaktionsdetails",
|
||||||
"tx_from_address": "Von Adresse:",
|
"tx_from_address": "Von Adresse:",
|
||||||
@@ -1325,6 +1633,8 @@
|
|||||||
"wallets_add_folder_toggle": "+ Weiteren Ordner nach Wallets durchsuchen…",
|
"wallets_add_folder_toggle": "+ Weiteren Ordner nach Wallets durchsuchen…",
|
||||||
"wallets_badge_encrypted": "Verschlüsselt (passphrasengeschützt)",
|
"wallets_badge_encrypted": "Verschlüsselt (passphrasengeschützt)",
|
||||||
"wallets_badge_encrypted_short": "Verschlüsselt",
|
"wallets_badge_encrypted_short": "Verschlüsselt",
|
||||||
|
"wallets_badge_hd": "HD-Wallet – Seed-Phrase ohne Öffnen nicht bestätigbar",
|
||||||
|
"wallets_badge_hd_short": "HD-Wallet",
|
||||||
"wallets_badge_legacy": "Legacy-Wallet (keine Seed-Phrase)",
|
"wallets_badge_legacy": "Legacy-Wallet (keine Seed-Phrase)",
|
||||||
"wallets_badge_legacy_short": "Legacy",
|
"wallets_badge_legacy_short": "Legacy",
|
||||||
"wallets_badge_seed": "Seed-Phrase-Wallet (HD)",
|
"wallets_badge_seed": "Seed-Phrase-Wallet (HD)",
|
||||||
@@ -1343,6 +1653,7 @@
|
|||||||
"wallets_created": "erstellt",
|
"wallets_created": "erstellt",
|
||||||
"wallets_creating": "Wallet wird erstellt — der Node wird neu gestartet…",
|
"wallets_creating": "Wallet wird erstellt — der Node wird neu gestartet…",
|
||||||
"wallets_current": "aktuell",
|
"wallets_current": "aktuell",
|
||||||
|
"wallets_empty_hint": "Ordner durchsuchen, um weitere Wallets zu finden",
|
||||||
"wallets_exists": "Eine Wallet mit diesem Namen existiert bereits.",
|
"wallets_exists": "Eine Wallet mit diesem Namen existiert bereits.",
|
||||||
"wallets_external_tt": "Außerhalb deines Datenverzeichnisses — Öffnen verlinkt sie an ihrem Ort (keine Kopie).",
|
"wallets_external_tt": "Außerhalb deines Datenverzeichnisses — Öffnen verlinkt sie an ihrem Ort (keine Kopie).",
|
||||||
"wallets_folder_hint": "/pfad/zum/ordner mit .dat-Wallet-Dateien",
|
"wallets_folder_hint": "/pfad/zum/ordner mit .dat-Wallet-Dateien",
|
||||||
@@ -1356,7 +1667,9 @@
|
|||||||
"wallets_open_failed": "Diese Wallet konnte nicht an ihrem Ort geöffnet werden. Sie liegt vermutlich auf einem anderen Laufwerk als dein Datenverzeichnis — verschiebe sie auf dasselbe Laufwerk (unter Windows erlaubt auch der aktivierte Entwicklermodus laufwerkübergreifende Verknüpfungen).",
|
"wallets_open_failed": "Diese Wallet konnte nicht an ihrem Ort geöffnet werden. Sie liegt vermutlich auf einem anderen Laufwerk als dein Datenverzeichnis — verschiebe sie auf dasselbe Laufwerk (unter Windows erlaubt auch der aktivierte Entwicklermodus laufwerkübergreifende Verknüpfungen).",
|
||||||
"wallets_open_folder": "Ordnerpfad öffnen",
|
"wallets_open_folder": "Ordnerpfad öffnen",
|
||||||
"wallets_open_inplace_tt": "Öffnet diese Wallet an ihrem Ort — im Datenverzeichnis verlinkt (keine Kopie)",
|
"wallets_open_inplace_tt": "Öffnet diese Wallet an ihrem Ort — im Datenverzeichnis verlinkt (keine Kopie)",
|
||||||
|
"wallets_remove_folder": "Diesen Ordner nicht mehr durchsuchen",
|
||||||
"wallets_reveal": "Ordner anzeigen",
|
"wallets_reveal": "Ordner anzeigen",
|
||||||
|
"wallets_scanned_folders": "Durchsuchte Ordner:",
|
||||||
"wallets_sort_addresses": "Adressen",
|
"wallets_sort_addresses": "Adressen",
|
||||||
"wallets_sort_asc": "Aufsteigend (ältestes / wenigste / kleinstes zuerst)",
|
"wallets_sort_asc": "Aufsteigend (ältestes / wenigste / kleinstes zuerst)",
|
||||||
"wallets_sort_by": "Sortieren:",
|
"wallets_sort_by": "Sortieren:",
|
||||||
|
|||||||
325
res/lang/es.json
325
res/lang/es.json
@@ -30,6 +30,7 @@
|
|||||||
"address_book_added": "Dirección agregada a la libreta",
|
"address_book_added": "Dirección agregada a la libreta",
|
||||||
"address_book_confirm_delete": "¿Confirmar eliminación?",
|
"address_book_confirm_delete": "¿Confirmar eliminación?",
|
||||||
"address_book_count": "%zu direcciones guardadas",
|
"address_book_count": "%zu direcciones guardadas",
|
||||||
|
"address_book_count_one": "%zu dirección guardada",
|
||||||
"address_book_deleted": "Entrada eliminada",
|
"address_book_deleted": "Entrada eliminada",
|
||||||
"address_book_edit": "Editar Dirección",
|
"address_book_edit": "Editar Dirección",
|
||||||
"address_book_empty": "No hay direcciones guardadas. Haz clic en 'Agregar Nueva' para añadir una.",
|
"address_book_empty": "No hay direcciones guardadas. Haz clic en 'Agregar Nueva' para añadir una.",
|
||||||
@@ -53,8 +54,20 @@
|
|||||||
"amount_details": "DETALLES DE CANTIDAD",
|
"amount_details": "DETALLES DE CANTIDAD",
|
||||||
"amount_exceeds_balance": "La cantidad excede el saldo",
|
"amount_exceeds_balance": "La cantidad excede el saldo",
|
||||||
"amount_label": "Cantidad:",
|
"amount_label": "Cantidad:",
|
||||||
|
"animate_avatars": "Animar avatares",
|
||||||
"appearance": "APARIENCIA",
|
"appearance": "APARIENCIA",
|
||||||
"auto_shield": "Auto-proteger minería",
|
"auto_shield": "Auto-proteger minería",
|
||||||
|
"av_intro": "El software de minería suele marcarse como potencialmente no deseado. Sigue estos pasos para habilitar la minería en pool:",
|
||||||
|
"av_open_security": "Abrir Seguridad de Windows",
|
||||||
|
"av_step1": "Paso 1: Agregar exclusión",
|
||||||
|
"av_step1_b1": "Abre Seguridad de Windows > Protección antivirus y contra amenazas",
|
||||||
|
"av_step1_b2": "Haz clic en Administrar configuración > Exclusiones > Agregar o quitar",
|
||||||
|
"av_step1_b3": "Agrega la carpeta: %APPDATA%\\ObsidianDragon\\",
|
||||||
|
"av_step2": "Paso 2: Restaurar desde cuarentena (si es necesario)",
|
||||||
|
"av_step2_b1": "Seguridad de Windows > Historial de protección",
|
||||||
|
"av_step2_b2": "Busca xmrig.exe y haz clic en Restaurar",
|
||||||
|
"av_step3": "Paso 3: Reinicia el monedero e inténtalo de nuevo",
|
||||||
|
"av_title": "Windows Defender bloqueó el minero",
|
||||||
"available": "Disponible",
|
"available": "Disponible",
|
||||||
"backup_backing_up": "Respaldando...",
|
"backup_backing_up": "Respaldando...",
|
||||||
"backup_create": "Crear Respaldo",
|
"backup_create": "Crear Respaldo",
|
||||||
@@ -62,6 +75,7 @@
|
|||||||
"backup_data": "RESPALDO Y DATOS",
|
"backup_data": "RESPALDO Y DATOS",
|
||||||
"backup_description": "Crea un respaldo de tu archivo wallet.dat. Este archivo contiene todas tus claves privadas e historial de transacciones. Guarda el respaldo en un lugar seguro.",
|
"backup_description": "Crea un respaldo de tu archivo wallet.dat. Este archivo contiene todas tus claves privadas e historial de transacciones. Guarda el respaldo en un lugar seguro.",
|
||||||
"backup_destination": "Destino del respaldo:",
|
"backup_destination": "Destino del respaldo:",
|
||||||
|
"backup_overwrite_confirm": "Ya existe un archivo ahí: guarda de nuevo para sobrescribirlo.",
|
||||||
"backup_source": "Origen: %s",
|
"backup_source": "Origen: %s",
|
||||||
"backup_tip_external": "Guarda respaldos en unidades externas o almacenamiento en la nube",
|
"backup_tip_external": "Guarda respaldos en unidades externas o almacenamiento en la nube",
|
||||||
"backup_tip_multiple": "Crea múltiples respaldos en diferentes ubicaciones",
|
"backup_tip_multiple": "Crea múltiples respaldos en diferentes ubicaciones",
|
||||||
@@ -70,6 +84,7 @@
|
|||||||
"backup_title": "Respaldar Cartera",
|
"backup_title": "Respaldar Cartera",
|
||||||
"backup_wallet": "Respaldar Cartera...",
|
"backup_wallet": "Respaldar Cartera...",
|
||||||
"backup_wallet_not_found": "Advertencia: wallet.dat no encontrado en la ubicación esperada",
|
"backup_wallet_not_found": "Advertencia: wallet.dat no encontrado en la ubicación esperada",
|
||||||
|
"backup_warn": "Este archivo contiene todas tus claves privadas: guárdalo en un lugar seguro.",
|
||||||
"balance": "Saldo",
|
"balance": "Saldo",
|
||||||
"balance_history_collecting": "Historial de saldo — recopilando datos...",
|
"balance_history_collecting": "Historial de saldo — recopilando datos...",
|
||||||
"balance_layout": "Diseño de Saldo",
|
"balance_layout": "Diseño de Saldo",
|
||||||
@@ -114,28 +129,105 @@
|
|||||||
"bootstrap_wallet_protected": "(wallet.dat está protegido)",
|
"bootstrap_wallet_protected": "(wallet.dat está protegido)",
|
||||||
"bootstrap_warning": "Los datos de bloques existentes (blocks, chainstate, notarizations) se eliminarán y reemplazarán. Su wallet.dat NO será modificado ni eliminado.",
|
"bootstrap_warning": "Los datos de bloques existentes (blocks, chainstate, notarizations) se eliminarán y reemplazarán. Su wallet.dat NO será modificado ni eliminado.",
|
||||||
"cancel": "Cancelar",
|
"cancel": "Cancelar",
|
||||||
|
"change_pass_confirm": "Confirmar nueva:",
|
||||||
|
"change_pass_current": "Frase de contraseña actual:",
|
||||||
|
"change_pass_new": "Nueva frase de contraseña:",
|
||||||
|
"change_pass_title": "Cambiar frase de contraseña",
|
||||||
"characters": "caracteres",
|
"characters": "caracteres",
|
||||||
"chat": "Chat",
|
"chat": "Chat",
|
||||||
|
"chat_accent_amber": "Ámbar",
|
||||||
|
"chat_accent_blue": "Azul",
|
||||||
|
"chat_accent_green": "Verde",
|
||||||
|
"chat_accent_pink": "Rosa",
|
||||||
|
"chat_accent_purple": "Morado",
|
||||||
|
"chat_accent_theme": "Tema",
|
||||||
|
"chat_add_contact": "Añadir contacto",
|
||||||
|
"chat_awaiting_key": "Esperando respuesta",
|
||||||
|
"chat_bubble_minimal": "Mínima",
|
||||||
|
"chat_bubble_rounded": "Redondeada",
|
||||||
|
"chat_bubble_square": "Cuadrada",
|
||||||
|
"chat_buffer_loading": "Búfer de chat: …",
|
||||||
|
"chat_buffer_preparing": "Búfer de chat: preparando %d/%d…",
|
||||||
|
"chat_buffer_ready": "Búfer de chat: %d/%d listos",
|
||||||
|
"chat_buffer_sending": "Chat: enviando %d mensajes…",
|
||||||
|
"chat_buffer_sending_one": "Chat: enviando %d mensaje…",
|
||||||
"chat_cancel": "Cancelar",
|
"chat_cancel": "Cancelar",
|
||||||
|
"chat_contact_added": "Contacto añadido: renómbralo en Contactos",
|
||||||
"chat_contact_request": "solicitud de contacto",
|
"chat_contact_request": "solicitud de contacto",
|
||||||
|
"chat_copy_address_tip": "Clic para copiar la dirección",
|
||||||
|
"chat_density_comfortable": "Cómoda",
|
||||||
|
"chat_density_compact": "Compacta",
|
||||||
|
"chat_emoji_color": "Color",
|
||||||
|
"chat_emoji_mono": "Monocromo",
|
||||||
|
"chat_emoji_search": "Buscar emoji",
|
||||||
"chat_empty_hint": "Aún no hay conversaciones. Los mensajes que recibas aparecerán aquí.",
|
"chat_empty_hint": "Aún no hay conversaciones. Los mensajes que recibas aparecerán aquí.",
|
||||||
|
"chat_empty_start": "Inicia una con \"Nueva conversación\".",
|
||||||
|
"chat_empty_title": "Aún no hay conversaciones",
|
||||||
|
"chat_export": "Exportar chat…",
|
||||||
|
"chat_export_done": "Conversación exportada",
|
||||||
|
"chat_export_failed": "No se pudo escribir el archivo de exportación.",
|
||||||
|
"chat_export_warn": "Guarda los mensajes descifrados como texto sin cifrar. Guarda el archivo de forma segura.",
|
||||||
|
"chat_filter": "Chat",
|
||||||
|
"chat_hidden_toast": "Conversación oculta: un mensaje nuevo la recupera",
|
||||||
|
"chat_hide": "Ocultar",
|
||||||
|
"chat_hide_hidden": "Ocultar ocultos",
|
||||||
|
"chat_jump_latest": "Recientes",
|
||||||
|
"chat_len_over": "Mensaje demasiado largo",
|
||||||
"chat_locked_hint": "Desbloquea tu monedero para cargar tus chats.",
|
"chat_locked_hint": "Desbloquea tu monedero para cargar tus chats.",
|
||||||
"chat_new_button": "Nueva conversación",
|
"chat_mute": "Silenciar",
|
||||||
|
"chat_new_button": "Nuevo chat",
|
||||||
"chat_new_message": "Mensaje",
|
"chat_new_message": "Mensaje",
|
||||||
|
"chat_new_message_toast": "Nuevo mensaje de chat cifrado",
|
||||||
"chat_new_send": "Enviar solicitud",
|
"chat_new_send": "Enviar solicitud",
|
||||||
"chat_new_title": "Nueva conversación",
|
"chat_new_title": "Nuevo chat",
|
||||||
"chat_new_zaddr": "Dirección z del destinatario",
|
"chat_new_zaddr": "Dirección z del destinatario",
|
||||||
|
"chat_no_matches": "Ninguna conversación coincide con tu búsqueda.",
|
||||||
|
"chat_no_z_contacts": "Aún no hay contactos con dirección blindada",
|
||||||
|
"chat_opt_bubble_accent": "Color de burbuja",
|
||||||
|
"chat_opt_bubble_style": "Estilo de burbuja",
|
||||||
|
"chat_opt_density": "Densidad de mensajes",
|
||||||
|
"chat_opt_emoji": "Estilo de emoji",
|
||||||
|
"chat_opt_enter_sends": "Enter envía el mensaje",
|
||||||
|
"chat_opt_font_size": "Tamaño del texto",
|
||||||
|
"chat_opt_global_clock": "Formato de reloj global",
|
||||||
|
"chat_opt_poll": "Frecuencia de sondeo",
|
||||||
|
"chat_opt_timestamp": "Marcas de tiempo",
|
||||||
|
"chat_pick_contact": "Elegir de contactos…",
|
||||||
|
"chat_rename": "Renombrar contacto",
|
||||||
|
"chat_rename_hint": "Nombre del contacto",
|
||||||
|
"chat_renamed": "Contacto renombrado",
|
||||||
|
"chat_retry": "Reintentar",
|
||||||
|
"chat_search": "Buscar conversaciones",
|
||||||
|
"chat_sec_appearance": "APARIENCIA",
|
||||||
|
"chat_sec_messaging": "MENSAJES",
|
||||||
"chat_select_hint": "Selecciona una conversación para verla.",
|
"chat_select_hint": "Selecciona una conversación para verla.",
|
||||||
"chat_send": "Enviar",
|
"chat_send": "Enviar",
|
||||||
"chat_send_failed": "no enviado",
|
"chat_send_failed": "no enviado",
|
||||||
|
"chat_sending": "enviando…",
|
||||||
|
"chat_settings_done": "Listo",
|
||||||
|
"chat_settings_section": "CHAT Y CONTACTOS",
|
||||||
|
"chat_settings_tip": "Personalizar chat",
|
||||||
|
"chat_settings_title": "Ajustes de chat",
|
||||||
|
"chat_show_hidden": "Ver ocultos",
|
||||||
|
"chat_time_now": "ahora",
|
||||||
"chat_toast_compose_failed": "No se pudo componer el mensaje (¿demasiado largo?).",
|
"chat_toast_compose_failed": "No se pudo componer el mensaje (¿demasiado largo?).",
|
||||||
"chat_toast_lite_busy": "Ya hay un envío en curso, o no hay ningún monedero abierto.",
|
"chat_toast_lite_busy": "Ya hay un envío en curso, o no hay ningún monedero abierto.",
|
||||||
|
"chat_toast_need_funds": "Necesitas un pequeño saldo blindado para enviar chats (para cubrir la comisión).",
|
||||||
"chat_toast_no_zaddr": "No hay ninguna dirección z disponible desde la que enviar el chat.",
|
"chat_toast_no_zaddr": "No hay ninguna dirección z disponible desde la que enviar el chat.",
|
||||||
"chat_toast_not_connected": "Sin conexión: mensaje de chat no enviado.",
|
"chat_toast_not_connected": "Sin conexión: mensaje de chat no enviado.",
|
||||||
"chat_toast_request_compose_failed": "No se pudo componer la solicitud de contacto (¿dirección o texto no válidos?).",
|
"chat_toast_request_compose_failed": "No se pudo componer la solicitud de contacto (¿dirección o texto no válidos?).",
|
||||||
"chat_toast_request_queued": "Solicitud de contacto en cola.",
|
"chat_toast_request_queued": "Solicitud de contacto en cola.",
|
||||||
"chat_toast_waiting_reply": "Espera a que este contacto responda antes de poder escribirle.",
|
"chat_toast_waiting_reply": "Espera a que este contacto responda antes de poder escribirle.",
|
||||||
|
"chat_today": "Hoy",
|
||||||
|
"chat_ts_12h": "12 horas",
|
||||||
|
"chat_ts_24h": "24 horas",
|
||||||
|
"chat_ts_global": "Seguir global",
|
||||||
|
"chat_ts_global_short": "Global",
|
||||||
|
"chat_unhide": "Mostrar",
|
||||||
|
"chat_unmute": "Reactivar",
|
||||||
|
"chat_verify_key": "Clave de identidad: compárala para verificar",
|
||||||
"chat_waiting_reply": "Esperando a que este contacto responda: podrás escribirle una vez lo haga.",
|
"chat_waiting_reply": "Esperando a que este contacto responda: podrás escribirle una vez lo haga.",
|
||||||
|
"chat_yesterday": "Ayer",
|
||||||
"chat_you": "Tú",
|
"chat_you": "Tú",
|
||||||
"choose_icon": "Elegir Icono",
|
"choose_icon": "Elegir Icono",
|
||||||
"clear": "Limpiar",
|
"clear": "Limpiar",
|
||||||
@@ -147,6 +239,7 @@
|
|||||||
"click_copy_address": "Clic para copiar dirección",
|
"click_copy_address": "Clic para copiar dirección",
|
||||||
"click_copy_uri": "Clic para copiar URI",
|
"click_copy_uri": "Clic para copiar URI",
|
||||||
"click_to_copy": "Clic para copiar",
|
"click_to_copy": "Clic para copiar",
|
||||||
|
"clock_format": "Formato de hora",
|
||||||
"close": "Cerrar",
|
"close": "Cerrar",
|
||||||
"conf_count": "%d conf",
|
"conf_count": "%d conf",
|
||||||
"confirm_and_send": "Confirmar y Enviar",
|
"confirm_and_send": "Confirmar y Enviar",
|
||||||
@@ -180,10 +273,24 @@
|
|||||||
"connected_peers": "Nodos Conectados",
|
"connected_peers": "Nodos Conectados",
|
||||||
"connecting": "Conectando...",
|
"connecting": "Conectando...",
|
||||||
"console": "Consola",
|
"console": "Consola",
|
||||||
|
"console_accents": "Acentos de color",
|
||||||
"console_app": "App",
|
"console_app": "App",
|
||||||
"console_auto_scroll": "Auto-desplazamiento",
|
"console_auto_scroll": "Auto-desplazamiento",
|
||||||
"console_available_commands": "Comandos disponibles:",
|
"console_available_commands": "Comandos disponibles:",
|
||||||
|
"console_backend_reference": "Referencia de Comandos del Backend",
|
||||||
|
"console_backend_unavailable": "Sin backend",
|
||||||
"console_capturing_output": "Capturando salida del daemon...",
|
"console_capturing_output": "Capturando salida del daemon...",
|
||||||
|
"console_cat_advanced": "Avanzado",
|
||||||
|
"console_cat_blockchain": "Blockchain",
|
||||||
|
"console_cat_control": "Control",
|
||||||
|
"console_cat_keys": "Claves y seguridad",
|
||||||
|
"console_cat_mining": "Minería",
|
||||||
|
"console_cat_network": "Red",
|
||||||
|
"console_cat_raw_transactions": "Transacciones sin procesar",
|
||||||
|
"console_cat_send": "Enviar",
|
||||||
|
"console_cat_sync": "Sincronización",
|
||||||
|
"console_cat_utility": "Utilidades",
|
||||||
|
"console_cat_wallet": "Cartera",
|
||||||
"console_clear": "Limpiar",
|
"console_clear": "Limpiar",
|
||||||
"console_clear_console": "Limpiar Consola",
|
"console_clear_console": "Limpiar Consola",
|
||||||
"console_cleared": "Consola limpiada",
|
"console_cleared": "Consola limpiada",
|
||||||
@@ -215,18 +322,37 @@
|
|||||||
"console_help_help": " help - Mostrar este mensaje de ayuda",
|
"console_help_help": " help - Mostrar este mensaje de ayuda",
|
||||||
"console_help_setgenerate": " setgenerate - Controlar minería",
|
"console_help_setgenerate": " setgenerate - Controlar minería",
|
||||||
"console_help_stop": " stop - Detener el daemon",
|
"console_help_stop": " stop - Detener el daemon",
|
||||||
|
"console_last_error": "Último error:",
|
||||||
"console_line_count": "%zu líneas",
|
"console_line_count": "%zu líneas",
|
||||||
"console_matches": "coincidencias",
|
"console_matches": "coincidencias",
|
||||||
"console_new_lines": "%d nuevas líneas",
|
"console_new_lines": "%d nuevas líneas",
|
||||||
"console_no_daemon": "Sin daemon",
|
"console_no_daemon": "Sin daemon",
|
||||||
|
"console_no_output": "(sin salida)",
|
||||||
"console_not_connected": "Error: No conectado al daemon",
|
"console_not_connected": "Error: No conectado al daemon",
|
||||||
|
"console_not_connected_lite": "Error: No hay ninguna cartera abierta",
|
||||||
"console_quit_note": "'quit'/'exit' no son necesarios aquí — simplemente cierra la ventana.",
|
"console_quit_note": "'quit'/'exit' no son necesarios aquí — simplemente cierra la ventana.",
|
||||||
|
"console_ref_builds": "Genera",
|
||||||
|
"console_ref_cancel": "Cancelar",
|
||||||
|
"console_ref_destructive": "Delicado",
|
||||||
|
"console_ref_example": "Ejemplo",
|
||||||
|
"console_ref_insert": "Insertar en la consola",
|
||||||
|
"console_ref_insert_run": "Insertar y ejecutar",
|
||||||
|
"console_ref_no_match": "Ningún comando coincide.",
|
||||||
|
"console_ref_no_params": "No requiere parámetros.",
|
||||||
|
"console_ref_optional": "opcional",
|
||||||
|
"console_ref_parameters": "Parámetros",
|
||||||
|
"console_ref_run": "Ejecutar",
|
||||||
|
"console_ref_run_confirm": "¿Ejecutar %s ahora? Es un comando con consecuencias.",
|
||||||
|
"console_ref_search_hint": "Buscar por nombre o tarea…",
|
||||||
|
"console_ref_select_hint": "Selecciona un comando para ver qué hace.",
|
||||||
|
"console_ref_value": "valor",
|
||||||
"console_rpc_reference": "Referencia de Comandos RPC",
|
"console_rpc_reference": "Referencia de Comandos RPC",
|
||||||
"console_rpc_trace": "RPC",
|
"console_rpc_trace": "RPC",
|
||||||
"console_scanline": "Líneas de consola",
|
"console_scanline": "Líneas de consola",
|
||||||
"console_search_commands": "Buscar comandos...",
|
"console_search_commands": "Buscar comandos...",
|
||||||
"console_select_all": "Seleccionar Todo",
|
"console_select_all": "Seleccionar Todo",
|
||||||
"console_show_app_output": "Mostrar las líneas de registro de la cartera [app]",
|
"console_show_app_output": "Mostrar las líneas de registro de la cartera [app]",
|
||||||
|
"console_show_backend_ref": "Mostrar referencia de comandos del backend",
|
||||||
"console_show_daemon_output": "Mostrar salida del daemon",
|
"console_show_daemon_output": "Mostrar salida del daemon",
|
||||||
"console_show_errors_only": "Mostrar solo errores",
|
"console_show_errors_only": "Mostrar solo errores",
|
||||||
"console_show_rpc_ref": "Mostrar referencia de comandos RPC",
|
"console_show_rpc_ref": "Mostrar referencia de comandos RPC",
|
||||||
@@ -239,17 +365,43 @@
|
|||||||
"console_status_stopped": "Detenido",
|
"console_status_stopped": "Detenido",
|
||||||
"console_status_stopping": "Deteniendo",
|
"console_status_stopping": "Deteniendo",
|
||||||
"console_status_unknown": "Desconocido",
|
"console_status_unknown": "Desconocido",
|
||||||
|
"console_stop_confirm_node": "'stop' apagará el nodo y desconectará la cartera. Escribe 'stop' de nuevo para confirmar.",
|
||||||
"console_tab_completion": "Tab para completar",
|
"console_tab_completion": "Tab para completar",
|
||||||
|
"console_text_colors": "Colores de texto",
|
||||||
|
"console_toggle_accents": "Alternar acentos de color de línea",
|
||||||
|
"console_toggle_text_color": "Alternar colores de texto de línea",
|
||||||
"console_type_help": "Escribe 'help' para ver los comandos disponibles",
|
"console_type_help": "Escribe 'help' para ver los comandos disponibles",
|
||||||
"console_welcome": "Bienvenido a la Consola de ObsidianDragon",
|
"console_welcome": "Bienvenido a la Consola de ObsidianDragon",
|
||||||
"console_zoom_in": "Acercar",
|
"console_zoom_in": "Acercar",
|
||||||
"console_zoom_out": "Alejar",
|
"console_zoom_out": "Alejar",
|
||||||
|
"contact_avatar": "AVATAR",
|
||||||
|
"contact_avatar_bad_image": "No se pudo cargar esa imagen.",
|
||||||
|
"contact_avatar_badge": "Insignia",
|
||||||
|
"contact_avatar_badge_hint": "La insignia se elige automáticamente según el tipo de dirección.",
|
||||||
|
"contact_avatar_choose": "Elegir imagen…",
|
||||||
|
"contact_avatar_copy_failed": "No se pudo copiar esa imagen.",
|
||||||
|
"contact_avatar_icon": "Icono",
|
||||||
|
"contact_avatar_image": "Imagen",
|
||||||
|
"contact_avatar_image_hint": "La imagen se copia en la app para que siga disponible si el original se mueve.",
|
||||||
|
"contact_avatar_remove": "Quitar",
|
||||||
|
"contact_avatar_shielded": "Blindada",
|
||||||
|
"contact_avatar_transparent": "Transparente",
|
||||||
"contact_global": "Mostrar en todas las carteras (contacto global)",
|
"contact_global": "Mostrar en todas las carteras (contacto global)",
|
||||||
"contact_global_badge_tt": "Contacto global — visible en todas las carteras",
|
"contact_global_badge_tt": "Contacto global — visible en todas las carteras",
|
||||||
"contact_global_tt": "Activado: este contacto permanece visible sin importar qué cartera cargues. Desactivado: pertenece solo a la cartera actual.",
|
"contact_global_tt": "Activado: este contacto permanece visible sin importar qué cartera cargues. Desactivado: pertenece solo a la cartera actual.",
|
||||||
|
"contact_preview_addr": "La dirección aparecerá aquí",
|
||||||
|
"contact_preview_name": "Nombre del contacto",
|
||||||
|
"contact_wallet_loading": "La cartera aún se está cargando: marca «Mostrar en todas las carteras» o inténtalo de nuevo en un momento.",
|
||||||
"contacts": "Contactos",
|
"contacts": "Contactos",
|
||||||
|
"contacts_avatar_shape": "Forma del avatar",
|
||||||
|
"contacts_list_scale": "Escala de la lista",
|
||||||
"contacts_search_no_match": "No hay contactos coincidentes",
|
"contacts_search_no_match": "No hay contactos coincidentes",
|
||||||
"contacts_search_placeholder": "Buscar contactos...",
|
"contacts_search_placeholder": "Buscar contactos...",
|
||||||
|
"contacts_settings_tip": "Personalizar contactos",
|
||||||
|
"contacts_settings_title": "Ajustes de contactos",
|
||||||
|
"contacts_shape_circle": "Círculo",
|
||||||
|
"contacts_shape_square": "Cuadrado",
|
||||||
|
"contacts_shape_tab": "Pestaña",
|
||||||
"copied": "¡Copiado!",
|
"copied": "¡Copiado!",
|
||||||
"copy": "Copiar",
|
"copy": "Copiar",
|
||||||
"copy_address": "Copiar Dirección Completa",
|
"copy_address": "Copiar Dirección Completa",
|
||||||
@@ -302,6 +454,19 @@
|
|||||||
"date": "Fecha",
|
"date": "Fecha",
|
||||||
"date_label": "Fecha:",
|
"date_label": "Fecha:",
|
||||||
"debug_logging": "REGISTRO DE DEPURACIÓN",
|
"debug_logging": "REGISTRO DE DEPURACIÓN",
|
||||||
|
"decrypt_desc": "Se exportará el monedero, se reiniciará el daemon con un monedero nuevo sin cifrar y se reimportarán todas las claves. Esto puede tardar varios minutos según el tamaño del monedero.",
|
||||||
|
"decrypt_error_title": "Error al descifrar",
|
||||||
|
"decrypt_step_backup": "Respaldando el monedero cifrado",
|
||||||
|
"decrypt_step_export": "Exportando las claves del monedero",
|
||||||
|
"decrypt_step_restart": "Reiniciando el daemon",
|
||||||
|
"decrypt_step_stop": "Deteniendo el daemon",
|
||||||
|
"decrypt_step_unlock": "Desbloqueando el monedero",
|
||||||
|
"decrypt_success_desc": "Tu monedero ahora está sin cifrar. Se guardó una copia de seguridad del monedero cifrado como wallet.dat.encrypted.bak en tu directorio de datos.",
|
||||||
|
"decrypt_success_title": "¡Monedero descifrado correctamente!",
|
||||||
|
"decrypt_title": "Quitar el cifrado del monedero",
|
||||||
|
"decrypt_wait_general": "Espera. El daemon está exportando claves, reiniciando y reimportando. Esto puede tardar varios minutos.",
|
||||||
|
"decrypt_wait_restart": "Esperando a que el daemon termine de iniciarse...",
|
||||||
|
"decrypt_warning": "Esto quitará el cifrado de tu monedero. Tus claves privadas se almacenarán sin protección en el disco.",
|
||||||
"delete": "Eliminar",
|
"delete": "Eliminar",
|
||||||
"delete_blockchain": "Eliminar Blockchain",
|
"delete_blockchain": "Eliminar Blockchain",
|
||||||
"delete_blockchain_confirm": "Eliminar y Resincronizar",
|
"delete_blockchain_confirm": "Eliminar y Resincronizar",
|
||||||
@@ -314,6 +479,15 @@
|
|||||||
"download_bootstrap": "Descargar Bootstrap",
|
"download_bootstrap": "Descargar Bootstrap",
|
||||||
"dragonx_green": "DragonX (Verde)",
|
"dragonx_green": "DragonX (Verde)",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
|
"enc_confirm": "Confirmar:",
|
||||||
|
"enc_desc": "Cifrar tu monedero protege tus claves privadas con una frase de contraseña. Tras el cifrado, el daemon se reiniciará.",
|
||||||
|
"enc_encrypting": "Cifrando el monedero...",
|
||||||
|
"enc_pin_desc": "Un PIN de 4-8 dígitos te permite desbloquear tu monedero sin escribir la frase de contraseña completa cada vez.",
|
||||||
|
"enc_pin_set_ok": "PIN configurado correctamente",
|
||||||
|
"enc_pin_skipped": "PIN omitido. Puedes configurar uno más tarde en Ajustes.",
|
||||||
|
"enc_pin_vault_fail": "No se pudo crear la bóveda del PIN",
|
||||||
|
"enc_success": "¡Monedero cifrado correctamente!",
|
||||||
|
"enc_wait": "Espera, no cierres la aplicación.",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"error_format": "Error: %s",
|
"error_format": "Error: %s",
|
||||||
"est_time_to_block": "Tiempo Est. al Bloque",
|
"est_time_to_block": "Tiempo Est. al Bloque",
|
||||||
@@ -333,23 +507,34 @@
|
|||||||
"explorer_mempool": "Mempool",
|
"explorer_mempool": "Mempool",
|
||||||
"explorer_mempool_size": "Tamaño",
|
"explorer_mempool_size": "Tamaño",
|
||||||
"explorer_mempool_txs": "Transacciones",
|
"explorer_mempool_txs": "Transacciones",
|
||||||
|
"explorer_next_block": "Bloque siguiente",
|
||||||
"explorer_no_results": "No hay bloques en caché que coincidan",
|
"explorer_no_results": "No hay bloques en caché que coincidan",
|
||||||
"explorer_not_connected": "Sin conexión al daemon: no se puede buscar un bloque ni un hash de transacción",
|
"explorer_not_connected": "Sin conexión al daemon: no se puede buscar un bloque ni un hash de transacción",
|
||||||
|
"explorer_prev_block": "Bloque anterior",
|
||||||
"explorer_recent_blocks": "Bloques Recientes",
|
"explorer_recent_blocks": "Bloques Recientes",
|
||||||
"explorer_search": "Buscar",
|
"explorer_search": "Buscar",
|
||||||
"explorer_section": "EXPLORADOR",
|
"explorer_section": "EXPLORADOR",
|
||||||
"explorer_tx_outputs": "Salidas",
|
"explorer_tx_outputs": "Salidas",
|
||||||
"explorer_tx_size": "Tamaño",
|
"explorer_tx_size": "Tamaño",
|
||||||
|
"explorer_txid": "TxID:",
|
||||||
"export": "Exportar",
|
"export": "Exportar",
|
||||||
"export_csv": "Exportar CSV",
|
"export_csv": "Exportar CSV",
|
||||||
"export_keys_btn": "Exportar Claves",
|
"export_keys_btn": "Exportar Claves",
|
||||||
"export_keys_danger": "PELIGRO: ¡Esto exportará TODAS las claves privadas de tu cartera! Cualquiera con acceso a este archivo puede robar tus fondos. Guárdalo de forma segura y elimínalo después de usar.",
|
"export_keys_danger": "PELIGRO: ¡Esto exportará TODAS las claves privadas de tu cartera! Cualquiera con acceso a este archivo puede robar tus fondos. Guárdalo de forma segura y elimínalo después de usar.",
|
||||||
"export_keys_include_t": "Incluir direcciones T (transparentes)",
|
"export_keys_include_t": "Incluir direcciones T (transparentes)",
|
||||||
"export_keys_include_z": "Incluir direcciones Z (protegidas)",
|
"export_keys_include_z": "Incluir direcciones Z (protegidas)",
|
||||||
|
"export_keys_none_addrs": "No hay direcciones para exportar",
|
||||||
|
"export_keys_none_result": "No se exportaron claves (0 de %d): desbloquea el monedero e inténtalo de nuevo.",
|
||||||
|
"export_keys_none_toast": "No se pudieron exportar claves: ¿está desbloqueado el monedero?",
|
||||||
|
"export_keys_not_connected": "Sin conexión con el daemon",
|
||||||
"export_keys_options": "Opciones de exportación:",
|
"export_keys_options": "Opciones de exportación:",
|
||||||
|
"export_keys_partial": "Se exportaron %d de %d claves: incompleto (algunas no tenían clave de gasto o el monedero está bloqueado).",
|
||||||
|
"export_keys_partial_toast": "Exportación parcial: %d de %d claves",
|
||||||
"export_keys_progress": "Exportando %d/%d...",
|
"export_keys_progress": "Exportando %d/%d...",
|
||||||
|
"export_keys_select_type": "Selecciona al menos un tipo de dirección",
|
||||||
"export_keys_success": "Claves exportadas exitosamente",
|
"export_keys_success": "Claves exportadas exitosamente",
|
||||||
"export_keys_title": "Exportar Todas las Claves Privadas",
|
"export_keys_title": "Exportar Todas las Claves Privadas",
|
||||||
|
"export_keys_write_fail": "No se pudo escribir el archivo de claves.",
|
||||||
"export_private_key": "Exportar Clave Privada",
|
"export_private_key": "Exportar Clave Privada",
|
||||||
"export_tx_count": "Exportar %zu transacciones a archivo CSV.",
|
"export_tx_count": "Exportar %zu transacciones a archivo CSV.",
|
||||||
"export_tx_file_fail": "Error al crear archivo CSV",
|
"export_tx_file_fail": "Error al crear archivo CSV",
|
||||||
@@ -389,23 +574,50 @@
|
|||||||
"hide_qr": "Ocultar QR",
|
"hide_qr": "Ocultar QR",
|
||||||
"hide_zero_balances": "Ocultar saldos 0",
|
"hide_zero_balances": "Ocultar saldos 0",
|
||||||
"history": "Historial",
|
"history": "Historial",
|
||||||
|
"img_picker_count": "%d imagen(es) en esta carpeta",
|
||||||
|
"img_picker_empty": "Esta carpeta no tiene subcarpetas ni imágenes.",
|
||||||
|
"img_picker_none": "No hay imágenes en esta carpeta",
|
||||||
|
"img_picker_pictures": "Carpeta de imágenes",
|
||||||
|
"img_picker_title": "Elegir una imagen",
|
||||||
|
"img_picker_use": "Usar imagen",
|
||||||
"immature_type": "Inmaduro",
|
"immature_type": "Inmaduro",
|
||||||
"import": "Importar",
|
"import": "Importar",
|
||||||
|
"import_key_address": "Dirección:",
|
||||||
"import_key_btn": "Importar Clave(s)",
|
"import_key_btn": "Importar Clave(s)",
|
||||||
|
"import_key_done": "Importada. El monedero está reescaneando.",
|
||||||
|
"import_key_field": "Clave",
|
||||||
"import_key_formats": "Formatos de clave soportados:",
|
"import_key_formats": "Formatos de clave soportados:",
|
||||||
"import_key_full_rescan": "(0 = re-escaneo completo)",
|
"import_key_full_rescan": "(0 = re-escaneo completo)",
|
||||||
|
"import_key_import": "Importar",
|
||||||
"import_key_label": "Clave(s) Privada(s):",
|
"import_key_label": "Clave(s) Privada(s):",
|
||||||
|
"import_key_need_node": "Conecta un nodo en ejecución para importar una clave.",
|
||||||
"import_key_no_valid": "No se encontraron claves válidas en la entrada",
|
"import_key_no_valid": "No se encontraron claves válidas en la entrada",
|
||||||
"import_key_progress": "Importando %d/%d...",
|
"import_key_progress": "Importando %d/%d...",
|
||||||
"import_key_rescan": "Re-escanear blockchain después de importar",
|
"import_key_rescan": "Re-escanear blockchain después de importar",
|
||||||
|
"import_key_rescanning": "Importando y reescaneando: puede tardar varios minutos",
|
||||||
|
"import_key_reveal_tip": "Mostrar/ocultar la clave",
|
||||||
"import_key_start_height": "Altura inicial:",
|
"import_key_start_height": "Altura inicial:",
|
||||||
"import_key_success": "Claves importadas exitosamente",
|
"import_key_success": "Claves importadas exitosamente",
|
||||||
"import_key_t_format": "Claves privadas WIF de direcciones T",
|
"import_key_t_format": "Claves privadas WIF de direcciones T",
|
||||||
"import_key_title": "Importar Clave Privada",
|
"import_key_title": "Importar Clave Privada",
|
||||||
"import_key_tooltip": "Ingresa una o más claves privadas, una por línea.\nSoporta claves de direcciones z y t.\nLas líneas que empiezan con # se tratan como comentarios.",
|
"import_key_tooltip": "Ingresa una o más claves privadas, una por línea.\nSoporta claves de direcciones z y t.\nLas líneas que empiezan con # se tratan como comentarios.",
|
||||||
|
"import_key_type_tkey": "Clave privada transparente",
|
||||||
|
"import_key_type_unknown": "Formato de clave no reconocido",
|
||||||
|
"import_key_type_zspend": "Clave de gasto blindada",
|
||||||
|
"import_key_type_zview": "Clave de visualización blindada (solo lectura)",
|
||||||
|
"import_key_warn": "Importa solo una clave que te pertenezca: da acceso a sus fondos.",
|
||||||
"import_key_warning": "Advertencia: ¡Nunca compartas tus claves privadas! Importar claves de fuentes no confiables puede comprometer tu cartera.",
|
"import_key_warning": "Advertencia: ¡Nunca compartas tus claves privadas! Importar claves de fuentes no confiables puede comprometer tu cartera.",
|
||||||
|
"import_key_wrong_type": "Esto parece una clave de visualización. Usa \"Importar clave de visualización\" en su lugar.",
|
||||||
"import_key_z_format": "Claves de gasto de direcciones Z (secret-extended-key-...)",
|
"import_key_z_format": "Claves de gasto de direcciones Z (secret-extended-key-...)",
|
||||||
"import_private_key": "Importar Clave Privada...",
|
"import_private_key": "Importar Clave Privada...",
|
||||||
|
"import_scan_hint": "0 = volver a escanear desde el inicio",
|
||||||
|
"import_scan_label": "Escanear desde la altura de bloque (opcional)",
|
||||||
|
"import_scan_tip": "altura actual",
|
||||||
|
"import_scan_transparent": "Las claves transparentes siempre se reescanean por completo",
|
||||||
|
"import_viewkey_field": "Clave de visualización",
|
||||||
|
"import_viewkey_note": "Solo lectura: una clave de visualización revela el saldo y las transacciones de una dirección, pero no puede gastar sus fondos.",
|
||||||
|
"import_viewkey_title": "Importar clave de visualización",
|
||||||
|
"import_viewkey_wrong_type": "Esto parece una clave de gasto. Usa \"Importar Clave Privada\" en su lugar.",
|
||||||
"incorrect_passphrase": "Contraseña incorrecta",
|
"incorrect_passphrase": "Contraseña incorrecta",
|
||||||
"incorrect_pin": "PIN incorrecto",
|
"incorrect_pin": "PIN incorrecto",
|
||||||
"insufficient_funds": "Fondos insuficientes para este monto más la comisión.",
|
"insufficient_funds": "Fondos insuficientes para este monto más la comisión.",
|
||||||
@@ -414,6 +626,7 @@
|
|||||||
"keep": "Mantener",
|
"keep": "Mantener",
|
||||||
"keep_daemon": "Mantener daemon activo",
|
"keep_daemon": "Mantener daemon activo",
|
||||||
"key_export_click_retrieve": "Haga clic para recuperar la clave de su billetera",
|
"key_export_click_retrieve": "Haga clic para recuperar la clave de su billetera",
|
||||||
|
"key_export_failed": "No se pudo exportar la clave: desbloquea el monedero (si está cifrado) e inténtalo de nuevo.",
|
||||||
"key_export_fetching": "Obteniendo clave de la cartera...",
|
"key_export_fetching": "Obteniendo clave de la cartera...",
|
||||||
"key_export_private_key": "Clave Privada:",
|
"key_export_private_key": "Clave Privada:",
|
||||||
"key_export_private_warning": "¡Mantén esta clave en SECRETO! Cualquiera con esta clave puede gastar tus fondos. Nunca la compartas en línea ni con personas no confiables.",
|
"key_export_private_warning": "¡Mantén esta clave en SECRETO! Cualquiera con esta clave puede gastar tus fondos. Nunca la compartas en línea ni con personas no confiables.",
|
||||||
@@ -431,6 +644,7 @@
|
|||||||
"lite_birthday_backup": "Cumpleaños: %llu (respalda esto también)",
|
"lite_birthday_backup": "Cumpleaños: %llu (respalda esto también)",
|
||||||
"lite_birthday_hint": "Altura de bloque desde la que empezar a escanear. Deja 0 si se desconoce (escaneo completo más lento).",
|
"lite_birthday_hint": "Altura de bloque desde la que empezar a escanear. Deja 0 si se desconoce (escaneo completo más lento).",
|
||||||
"lite_birthday_label": "Fecha de creación",
|
"lite_birthday_label": "Fecha de creación",
|
||||||
|
"lite_console_backend_commands": "Comandos del backend:",
|
||||||
"lite_console_help_passthrough": "Cualquier otra entrada se ejecuta como un comando de consola de la cartera lite.",
|
"lite_console_help_passthrough": "Cualquier otra entrada se ejecuta como un comando de consola de la cartera lite.",
|
||||||
"lite_copy": "Copiar",
|
"lite_copy": "Copiar",
|
||||||
"lite_could_not_write": "No se pudo escribir ",
|
"lite_could_not_write": "No se pudo escribir ",
|
||||||
@@ -448,6 +662,7 @@
|
|||||||
"lite_net_add_url_hint": "https://tu-servidor-lite",
|
"lite_net_add_url_hint": "https://tu-servidor-lite",
|
||||||
"lite_net_checking": "comprobando…",
|
"lite_net_checking": "comprobando…",
|
||||||
"lite_net_connected": "Conectado",
|
"lite_net_connected": "Conectado",
|
||||||
|
"lite_net_connecting": "Conectando…",
|
||||||
"lite_net_custom": "Personalizado",
|
"lite_net_custom": "Personalizado",
|
||||||
"lite_net_disconnected": "No conectado",
|
"lite_net_disconnected": "No conectado",
|
||||||
"lite_net_hidden_section": "Servidores ocultos",
|
"lite_net_hidden_section": "Servidores ocultos",
|
||||||
@@ -534,6 +749,10 @@
|
|||||||
"market_btc_price": "PRECIO BTC",
|
"market_btc_price": "PRECIO BTC",
|
||||||
"market_cap": "Cap. de Mercado",
|
"market_cap": "Cap. de Mercado",
|
||||||
"market_cap_short": "Cap.",
|
"market_cap_short": "Cap.",
|
||||||
|
"market_chart_loading": "Cargando historial de precios",
|
||||||
|
"market_col_name": "Nombre",
|
||||||
|
"market_col_trend": "Tendencia",
|
||||||
|
"market_col_value": "Valor",
|
||||||
"market_iv_1d": "1D",
|
"market_iv_1d": "1D",
|
||||||
"market_iv_1h": "1H",
|
"market_iv_1h": "1H",
|
||||||
"market_iv_1m": "1M",
|
"market_iv_1m": "1M",
|
||||||
@@ -542,11 +761,18 @@
|
|||||||
"market_no_history": "No hay historial de precios disponible",
|
"market_no_history": "No hay historial de precios disponible",
|
||||||
"market_no_price": "Sin datos de precio",
|
"market_no_price": "Sin datos de precio",
|
||||||
"market_now": "Ahora",
|
"market_now": "Ahora",
|
||||||
|
"market_opt_chart_style": "Estilo de gráfico",
|
||||||
"market_pct_shielded": "%.0f%% Protegido",
|
"market_pct_shielded": "%.0f%% Protegido",
|
||||||
"market_portfolio": "PORTAFOLIO",
|
"market_portfolio": "PORTAFOLIO",
|
||||||
"market_price_loading": "Cargando datos de precio...",
|
"market_price_loading": "Cargando datos de precio...",
|
||||||
"market_price_unavailable": "Datos de precio no disponibles",
|
"market_price_unavailable": "Datos de precio no disponibles",
|
||||||
"market_refresh_price": "Actualizar datos de precio",
|
"market_refresh_price": "Actualizar datos de precio",
|
||||||
|
"market_settings_tip": "Opciones de mercado",
|
||||||
|
"market_settings_title": "Ajustes de mercado",
|
||||||
|
"market_style_candle": "Cambiar a velas",
|
||||||
|
"market_style_candle_label": "Velas",
|
||||||
|
"market_style_line": "Cambiar a gráfico de líneas",
|
||||||
|
"market_style_line_label": "Línea",
|
||||||
"market_trade_on": "Operar en %s",
|
"market_trade_on": "Operar en %s",
|
||||||
"market_updated": "\\xc2\\xb7 Actualizado %s",
|
"market_updated": "\\xc2\\xb7 Actualizado %s",
|
||||||
"market_vol_short": "Vol",
|
"market_vol_short": "Vol",
|
||||||
@@ -736,6 +962,7 @@
|
|||||||
"output_filename": "Nombre del archivo:",
|
"output_filename": "Nombre del archivo:",
|
||||||
"overview": "Resumen",
|
"overview": "Resumen",
|
||||||
"paste": "Pegar",
|
"paste": "Pegar",
|
||||||
|
"paste_clip_empty": "El portapapeles está vacío",
|
||||||
"paste_from_clipboard": "Pegar del Portapapeles",
|
"paste_from_clipboard": "Pegar del Portapapeles",
|
||||||
"pay_from": "Pagar desde",
|
"pay_from": "Pagar desde",
|
||||||
"payment_request": "SOLICITUD DE PAGO",
|
"payment_request": "SOLICITUD DE PAGO",
|
||||||
@@ -787,7 +1014,14 @@
|
|||||||
"peers_upper": "NODOS",
|
"peers_upper": "NODOS",
|
||||||
"peers_version": "Versión",
|
"peers_version": "Versión",
|
||||||
"pending": "Pendiente",
|
"pending": "Pendiente",
|
||||||
|
"pin_change_desc": "Cambia tu PIN de desbloqueo. Necesitas tu PIN actual y un nuevo PIN.",
|
||||||
|
"pin_confirm_new_label": "Confirmar nuevo PIN:",
|
||||||
|
"pin_current_label": "PIN actual:",
|
||||||
|
"pin_new_label": "Nuevo PIN (4-8 dígitos):",
|
||||||
"pin_not_set": "PIN no configurado. Use la contraseña para desbloquear.",
|
"pin_not_set": "PIN no configurado. Use la contraseña para desbloquear.",
|
||||||
|
"pin_remove_desc": "Introduce tu PIN actual para confirmar la eliminación. Necesitarás tu frase de contraseña completa para desbloquear.",
|
||||||
|
"pin_setup_desc": "Establece un PIN de 4-8 dígitos para desbloquear el monedero rápidamente. Tu frase de contraseña se cifrará con este PIN y se almacenará localmente.",
|
||||||
|
"pin_wallet_passphrase": "Frase de contraseña del monedero:",
|
||||||
"ping": "Ping",
|
"ping": "Ping",
|
||||||
"portfolio_add_entry": "Añadir entrada",
|
"portfolio_add_entry": "Añadir entrada",
|
||||||
"portfolio_add_to": "Agregar a la cartera de inversiones",
|
"portfolio_add_to": "Agregar a la cartera de inversiones",
|
||||||
@@ -826,6 +1060,9 @@
|
|||||||
"portfolio_remove_from": "Quitar del portafolio",
|
"portfolio_remove_from": "Quitar del portafolio",
|
||||||
"portfolio_revert": "Revertir",
|
"portfolio_revert": "Revertir",
|
||||||
"portfolio_save": "Guardar",
|
"portfolio_save": "Guardar",
|
||||||
|
"portfolio_save_need_address": "Añade al menos una dirección para guardar.",
|
||||||
|
"portfolio_save_need_name": "Introduce un nombre para guardar este grupo.",
|
||||||
|
"portfolio_save_need_price": "Introduce un precio manual mayor que 0 para guardar.",
|
||||||
"portfolio_search": "Buscar direcciones…",
|
"portfolio_search": "Buscar direcciones…",
|
||||||
"portfolio_search_icons": "Buscar iconos…",
|
"portfolio_search_icons": "Buscar iconos…",
|
||||||
"portfolio_select_all": "Todos",
|
"portfolio_select_all": "Todos",
|
||||||
@@ -839,11 +1076,12 @@
|
|||||||
"portfolio_spark_min": "Minuto",
|
"portfolio_spark_min": "Minuto",
|
||||||
"portfolio_spark_month": "Mes",
|
"portfolio_spark_month": "Mes",
|
||||||
"portfolio_spark_week": "Semana",
|
"portfolio_spark_week": "Semana",
|
||||||
"portfolio_style_compact": "Filas compactas",
|
"portfolio_style_compact": "Tabla",
|
||||||
"portfolio_style_detailed": "Filas detalladas",
|
"portfolio_style_detailed": "Tarjetas",
|
||||||
"portfolio_style_featured": "Filas destacadas",
|
"portfolio_style_featured": "Destacado",
|
||||||
"portfolio_style_label": "Estilo de cartera",
|
"portfolio_style_label": "Estilo de cartera",
|
||||||
"portfolio_untitled": "Sin título",
|
"portfolio_untitled": "Sin título",
|
||||||
|
"portfolio_wallet_loading": "Espera a que la cartera termine de cargar para añadir un grupo.",
|
||||||
"price_chart": "Gráfico de Precios",
|
"price_chart": "Gráfico de Precios",
|
||||||
"privacy_great": "¡Excelente privacidad!",
|
"privacy_great": "¡Excelente privacidad!",
|
||||||
"privacy_low": "Privacidad baja: protege los fondos",
|
"privacy_low": "Privacidad baja: protege los fondos",
|
||||||
@@ -973,6 +1211,7 @@
|
|||||||
"send_amount": "Cantidad",
|
"send_amount": "Cantidad",
|
||||||
"send_amount_details": "DETALLES DE CANTIDAD",
|
"send_amount_details": "DETALLES DE CANTIDAD",
|
||||||
"send_amount_upper": "CANTIDAD",
|
"send_amount_upper": "CANTIDAD",
|
||||||
|
"send_cannot_send_now": "No se puede enviar ahora — revisa la conexión, la sincronización y el saldo disponible.",
|
||||||
"send_clear_fields": "¿Limpiar todos los campos del formulario?",
|
"send_clear_fields": "¿Limpiar todos los campos del formulario?",
|
||||||
"send_contacts_button": "Elegir de contactos",
|
"send_contacts_button": "Elegir de contactos",
|
||||||
"send_copy_error": "Copiar Error",
|
"send_copy_error": "Copiar Error",
|
||||||
@@ -1061,7 +1300,8 @@
|
|||||||
"settings_gradient_bg": "Fondo degradado",
|
"settings_gradient_bg": "Fondo degradado",
|
||||||
"settings_gradient_desc": "Reemplazar fondos con texturas por degradados suaves",
|
"settings_gradient_desc": "Reemplazar fondos con texturas por degradados suaves",
|
||||||
"settings_idle_after": "después de",
|
"settings_idle_after": "después de",
|
||||||
"settings_import_key": "Importar clave...",
|
"settings_import_key": "Importar Clave Privada...",
|
||||||
|
"settings_import_viewkey": "Importar clave de visualización...",
|
||||||
"settings_language_note": "Nota: Parte del texto requiere reinicio para actualizarse",
|
"settings_language_note": "Nota: Parte del texto requiere reinicio para actualizarse",
|
||||||
"settings_lock_now": "Bloquear ahora",
|
"settings_lock_now": "Bloquear ahora",
|
||||||
"settings_locked": "Bloqueado",
|
"settings_locked": "Bloqueado",
|
||||||
@@ -1110,6 +1350,7 @@
|
|||||||
"shield_in_progress": "Operación en progreso...",
|
"shield_in_progress": "Operación en progreso...",
|
||||||
"shield_max_utxos": "UTXOs máximos por operación",
|
"shield_max_utxos": "UTXOs máximos por operación",
|
||||||
"shield_merge_done": "¡Protección/fusión completada!",
|
"shield_merge_done": "¡Protección/fusión completada!",
|
||||||
|
"shield_no_zaddr_hint": "Aún no hay dirección blindada (z) — crea una primero en la pestaña Recibir.",
|
||||||
"shield_op_failed": "Operación fallida: ",
|
"shield_op_failed": "Operación fallida: ",
|
||||||
"shield_op_submitted": "Operación enviada: ",
|
"shield_op_submitted": "Operación enviada: ",
|
||||||
"shield_operation_id": "ID de operación: %s",
|
"shield_operation_id": "ID de operación: %s",
|
||||||
@@ -1148,6 +1389,31 @@
|
|||||||
"submitting_transaction": "Enviando transacción...",
|
"submitting_transaction": "Enviando transacción...",
|
||||||
"success": "Éxito",
|
"success": "Éxito",
|
||||||
"summary": "Resumen",
|
"summary": "Resumen",
|
||||||
|
"sweep_button": "Barrer",
|
||||||
|
"sweep_caveat": "Importa la clave para firmar una transacción que mueve todos sus fondos a tu dirección. La clave permanece en tu monedero con saldo cero.",
|
||||||
|
"sweep_dest_label": "Enviar los fondos barridos a",
|
||||||
|
"sweep_dest_new": "Nueva dirección blindada (recomendado)",
|
||||||
|
"sweep_done": "Listo — fondos barridos a tu dirección.",
|
||||||
|
"sweep_to": "Barrido a:",
|
||||||
|
"sweep_toggle": "Barrer a mi monedero (no conservar la clave)",
|
||||||
|
"sweep_tx": "Transacción:",
|
||||||
|
"switch_corrupt_body": "Esta cartera parece dañada: el nodo no pudo abrirla. Restáurala desde una copia de seguridad, vuelve a crearla o intenta repararla.",
|
||||||
|
"switch_corrupt_repair": "Intentar reparar (salvage)",
|
||||||
|
"switch_progress_background": "Continuar en segundo plano",
|
||||||
|
"switch_progress_default_wallet": "Cartera predeterminada",
|
||||||
|
"switch_progress_elapsed": "Transcurrido",
|
||||||
|
"switch_progress_external_wallet": "Cartera externa",
|
||||||
|
"switch_progress_failed_title": "Error al cambiar de cartera",
|
||||||
|
"switch_progress_from_label": "desde",
|
||||||
|
"switch_progress_hint": "Un apagado ordenado puede tardar hasta un minuto.",
|
||||||
|
"switch_progress_reconnecting": "Reconectando",
|
||||||
|
"switch_progress_starting": "Iniciando el nodo con la nueva cartera",
|
||||||
|
"switch_progress_stopping": "Deteniendo el nodo actual",
|
||||||
|
"switch_progress_title": "Cambiando de cartera",
|
||||||
|
"switch_stopnode_body": "Cambiar de cartera reinicia el nodo con la cartera seleccionada. El nodo en ejecución se detendrá y se reiniciará con la nueva cartera; si lo dejaste en marcha a propósito, volverá a iniciarse automáticamente.",
|
||||||
|
"switch_stopnode_confirm": "Detener nodo y cambiar",
|
||||||
|
"switch_stopnode_title": "¿Detener el nodo en ejecución?",
|
||||||
|
"switch_stopnode_warn": "Ya hay un nodo en ejecución que esta cartera no inició.",
|
||||||
"syncing": "Sincronizando...",
|
"syncing": "Sincronizando...",
|
||||||
"t_address": "Dirección T",
|
"t_address": "Dirección T",
|
||||||
"t_addresses": "Direcciones T",
|
"t_addresses": "Direcciones T",
|
||||||
@@ -1183,8 +1449,10 @@
|
|||||||
"transfer_to": "Transferir a:",
|
"transfer_to": "Transferir a:",
|
||||||
"transparent": "Transparente",
|
"transparent": "Transparente",
|
||||||
"transparent_address": "Dirección Transparente",
|
"transparent_address": "Dirección Transparente",
|
||||||
|
"try_again": "Reintentar",
|
||||||
"tt_addr_url": "URL base para ver direcciones en un explorador de bloques",
|
"tt_addr_url": "URL base para ver direcciones en un explorador de bloques",
|
||||||
"tt_address_book": "Administrar direcciones guardadas para envío rápido",
|
"tt_address_book": "Administrar direcciones guardadas para envío rápido",
|
||||||
|
"tt_animate_avatars": "Reproduce avatares de contacto animados (GIF / WebP); desactivado muestra solo el primer fotograma",
|
||||||
"tt_auto_lock": "Bloquear billetera después de este tiempo de inactividad",
|
"tt_auto_lock": "Bloquear billetera después de este tiempo de inactividad",
|
||||||
"tt_auto_shield": "Mover automáticamente el saldo transparente a direcciones blindadas para privacidad",
|
"tt_auto_shield": "Mover automáticamente el saldo transparente a direcciones blindadas para privacidad",
|
||||||
"tt_backup": "Crear una copia de seguridad de su wallet.dat",
|
"tt_backup": "Crear una copia de seguridad de su wallet.dat",
|
||||||
@@ -1192,10 +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_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",
|
||||||
@@ -1209,10 +1487,34 @@
|
|||||||
"tt_font_scale": "Escalar todo el texto y la interfaz (1.0x = predeterminado, hasta 1.5x).",
|
"tt_font_scale": "Escalar todo el texto y la interfaz (1.0x = predeterminado, hasta 1.5x).",
|
||||||
"tt_idle_delay": "Cuánto tiempo esperar antes de empezar a minar",
|
"tt_idle_delay": "Cuánto tiempo esperar antes de empezar a minar",
|
||||||
"tt_import_key": "Importar una clave privada (zkey o tkey) en esta billetera",
|
"tt_import_key": "Importar una clave privada (zkey o tkey) en esta billetera",
|
||||||
|
"tt_import_viewkey": "Importar una clave de visualización blindada para observar una dirección (solo lectura)",
|
||||||
"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",
|
||||||
@@ -1233,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",
|
||||||
@@ -1257,6 +1564,7 @@
|
|||||||
"tt_website": "Abrir el sitio web de DragonX",
|
"tt_website": "Abrir el sitio web de DragonX",
|
||||||
"tt_window_opacity": "Opacidad del fondo (menor = escritorio visible a través de la ventana)",
|
"tt_window_opacity": "Opacidad del fondo (menor = escritorio visible a través de la ventana)",
|
||||||
"tt_wizard": "Volver a ejecutar el asistente de configuración inicial\\nEl daemon será reiniciado",
|
"tt_wizard": "Volver a ejecutar el asistente de configuración inicial\\nEl daemon será reiniciado",
|
||||||
|
"tx_chat_badge": "Mensaje",
|
||||||
"tx_confirmations": "%d confirmaciones",
|
"tx_confirmations": "%d confirmaciones",
|
||||||
"tx_details_title": "Detalles de Transacción",
|
"tx_details_title": "Detalles de Transacción",
|
||||||
"tx_from_address": "Dirección Origen:",
|
"tx_from_address": "Dirección Origen:",
|
||||||
@@ -1325,6 +1633,8 @@
|
|||||||
"wallets_add_folder_toggle": "+ Buscar wallets en otra carpeta…",
|
"wallets_add_folder_toggle": "+ Buscar wallets en otra carpeta…",
|
||||||
"wallets_badge_encrypted": "Cifrada (protegida con contraseña)",
|
"wallets_badge_encrypted": "Cifrada (protegida con contraseña)",
|
||||||
"wallets_badge_encrypted_short": "Cifrada",
|
"wallets_badge_encrypted_short": "Cifrada",
|
||||||
|
"wallets_badge_hd": "Cartera HD: no se puede confirmar la frase semilla sin abrirla",
|
||||||
|
"wallets_badge_hd_short": "Cartera HD",
|
||||||
"wallets_badge_legacy": "Billetera heredada (sin frase semilla)",
|
"wallets_badge_legacy": "Billetera heredada (sin frase semilla)",
|
||||||
"wallets_badge_legacy_short": "Heredada",
|
"wallets_badge_legacy_short": "Heredada",
|
||||||
"wallets_badge_seed": "Billetera con frase semilla (HD)",
|
"wallets_badge_seed": "Billetera con frase semilla (HD)",
|
||||||
@@ -1343,6 +1653,7 @@
|
|||||||
"wallets_created": "creada",
|
"wallets_created": "creada",
|
||||||
"wallets_creating": "Creando cartera — el nodo se reiniciará…",
|
"wallets_creating": "Creando cartera — el nodo se reiniciará…",
|
||||||
"wallets_current": "actual",
|
"wallets_current": "actual",
|
||||||
|
"wallets_empty_hint": "Escanea una carpeta para encontrar más carteras",
|
||||||
"wallets_exists": "Ya existe una cartera con ese nombre.",
|
"wallets_exists": "Ya existe una cartera con ese nombre.",
|
||||||
"wallets_external_tt": "Fuera de tu directorio de datos — Abrir lo enlaza en su lugar (sin copiar).",
|
"wallets_external_tt": "Fuera de tu directorio de datos — Abrir lo enlaza en su lugar (sin copiar).",
|
||||||
"wallets_folder_hint": "/ruta/a/carpeta con archivos .dat de cartera",
|
"wallets_folder_hint": "/ruta/a/carpeta con archivos .dat de cartera",
|
||||||
@@ -1356,7 +1667,9 @@
|
|||||||
"wallets_open_failed": "No se pudo abrir esta cartera en su ubicación. Probablemente está en una unidad distinta a tu directorio de datos: muévela a la misma unidad (en Windows, activar el Modo de desarrollador también permite enlazar entre unidades).",
|
"wallets_open_failed": "No se pudo abrir esta cartera en su ubicación. Probablemente está en una unidad distinta a tu directorio de datos: muévela a la misma unidad (en Windows, activar el Modo de desarrollador también permite enlazar entre unidades).",
|
||||||
"wallets_open_folder": "Abrir ubicación de la carpeta",
|
"wallets_open_folder": "Abrir ubicación de la carpeta",
|
||||||
"wallets_open_inplace_tt": "Abre esta cartera donde está, enlazándola al directorio de datos (sin copiar)",
|
"wallets_open_inplace_tt": "Abre esta cartera donde está, enlazándola al directorio de datos (sin copiar)",
|
||||||
|
"wallets_remove_folder": "Dejar de escanear esta carpeta",
|
||||||
"wallets_reveal": "Mostrar carpeta",
|
"wallets_reveal": "Mostrar carpeta",
|
||||||
|
"wallets_scanned_folders": "Carpetas escaneadas:",
|
||||||
"wallets_sort_addresses": "Direcciones",
|
"wallets_sort_addresses": "Direcciones",
|
||||||
"wallets_sort_asc": "Ascendente (más antiguo / menos / más pequeño primero)",
|
"wallets_sort_asc": "Ascendente (más antiguo / menos / más pequeño primero)",
|
||||||
"wallets_sort_by": "Ordenar:",
|
"wallets_sort_by": "Ordenar:",
|
||||||
|
|||||||
325
res/lang/fr.json
325
res/lang/fr.json
@@ -30,6 +30,7 @@
|
|||||||
"address_book_added": "Adresse ajoutée au carnet",
|
"address_book_added": "Adresse ajoutée au carnet",
|
||||||
"address_book_confirm_delete": "Confirmer la suppression ?",
|
"address_book_confirm_delete": "Confirmer la suppression ?",
|
||||||
"address_book_count": "%zu adresses enregistrées",
|
"address_book_count": "%zu adresses enregistrées",
|
||||||
|
"address_book_count_one": "%zu adresse enregistrée",
|
||||||
"address_book_deleted": "Entrée supprimée",
|
"address_book_deleted": "Entrée supprimée",
|
||||||
"address_book_edit": "Modifier l'adresse",
|
"address_book_edit": "Modifier l'adresse",
|
||||||
"address_book_empty": "Aucune adresse enregistrée. Cliquez sur 'Ajouter' pour en créer une.",
|
"address_book_empty": "Aucune adresse enregistrée. Cliquez sur 'Ajouter' pour en créer une.",
|
||||||
@@ -53,8 +54,20 @@
|
|||||||
"amount_details": "DÉTAILS DU MONTANT",
|
"amount_details": "DÉTAILS DU MONTANT",
|
||||||
"amount_exceeds_balance": "Le montant dépasse le solde",
|
"amount_exceeds_balance": "Le montant dépasse le solde",
|
||||||
"amount_label": "Montant :",
|
"amount_label": "Montant :",
|
||||||
|
"animate_avatars": "Animer les avatars",
|
||||||
"appearance": "APPARENCE",
|
"appearance": "APPARENCE",
|
||||||
"auto_shield": "Auto-blindage du minage",
|
"auto_shield": "Auto-blindage du minage",
|
||||||
|
"av_intro": "Les logiciels de minage sont souvent signalés comme potentiellement indésirables. Suivez ces étapes pour activer le minage en pool :",
|
||||||
|
"av_open_security": "Ouvrir Sécurité Windows",
|
||||||
|
"av_step1": "Étape 1 : Ajouter une exclusion",
|
||||||
|
"av_step1_b1": "Ouvrez Sécurité Windows > Protection contre les virus et menaces",
|
||||||
|
"av_step1_b2": "Cliquez sur Gérer les paramètres > Exclusions > Ajouter ou supprimer",
|
||||||
|
"av_step1_b3": "Ajoutez le dossier : %APPDATA%\\ObsidianDragon\\",
|
||||||
|
"av_step2": "Étape 2 : Restaurer depuis la quarantaine (si nécessaire)",
|
||||||
|
"av_step2_b1": "Sécurité Windows > Historique de protection",
|
||||||
|
"av_step2_b2": "Trouvez xmrig.exe et cliquez sur Restaurer",
|
||||||
|
"av_step3": "Étape 3 : Redémarrez le portefeuille et réessayez",
|
||||||
|
"av_title": "Windows Defender a bloqué le mineur",
|
||||||
"available": "Disponible",
|
"available": "Disponible",
|
||||||
"backup_backing_up": "Sauvegarde en cours...",
|
"backup_backing_up": "Sauvegarde en cours...",
|
||||||
"backup_create": "Créer une sauvegarde",
|
"backup_create": "Créer une sauvegarde",
|
||||||
@@ -62,6 +75,7 @@
|
|||||||
"backup_data": "SAUVEGARDE & DONNÉES",
|
"backup_data": "SAUVEGARDE & DONNÉES",
|
||||||
"backup_description": "Créez une sauvegarde de votre fichier wallet.dat. Ce fichier contient toutes vos clés privées et l'historique des transactions. Conservez la sauvegarde dans un endroit sûr.",
|
"backup_description": "Créez une sauvegarde de votre fichier wallet.dat. Ce fichier contient toutes vos clés privées et l'historique des transactions. Conservez la sauvegarde dans un endroit sûr.",
|
||||||
"backup_destination": "Destination de sauvegarde :",
|
"backup_destination": "Destination de sauvegarde :",
|
||||||
|
"backup_overwrite_confirm": "Un fichier existe déjà là — enregistrez à nouveau pour l'écraser.",
|
||||||
"backup_source": "Source : %s",
|
"backup_source": "Source : %s",
|
||||||
"backup_tip_external": "Stockez les sauvegardes sur des disques externes ou un stockage cloud",
|
"backup_tip_external": "Stockez les sauvegardes sur des disques externes ou un stockage cloud",
|
||||||
"backup_tip_multiple": "Créez plusieurs sauvegardes à différents endroits",
|
"backup_tip_multiple": "Créez plusieurs sauvegardes à différents endroits",
|
||||||
@@ -70,6 +84,7 @@
|
|||||||
"backup_title": "Sauvegarder le portefeuille",
|
"backup_title": "Sauvegarder le portefeuille",
|
||||||
"backup_wallet": "Sauvegarder le portefeuille...",
|
"backup_wallet": "Sauvegarder le portefeuille...",
|
||||||
"backup_wallet_not_found": "Attention : wallet.dat introuvable à l'emplacement prévu",
|
"backup_wallet_not_found": "Attention : wallet.dat introuvable à l'emplacement prévu",
|
||||||
|
"backup_warn": "Ce fichier contient toutes vos clés privées — conservez-le en lieu sûr.",
|
||||||
"balance": "Solde",
|
"balance": "Solde",
|
||||||
"balance_history_collecting": "Historique du solde — collecte des données...",
|
"balance_history_collecting": "Historique du solde — collecte des données...",
|
||||||
"balance_layout": "Disposition du solde",
|
"balance_layout": "Disposition du solde",
|
||||||
@@ -114,28 +129,105 @@
|
|||||||
"bootstrap_wallet_protected": "(wallet.dat est protégé)",
|
"bootstrap_wallet_protected": "(wallet.dat est protégé)",
|
||||||
"bootstrap_warning": "Les données de blocs existantes (blocks, chainstate, notarizations) seront supprimées et remplacées. Votre wallet.dat ne sera PAS modifié ni supprimé.",
|
"bootstrap_warning": "Les données de blocs existantes (blocks, chainstate, notarizations) seront supprimées et remplacées. Votre wallet.dat ne sera PAS modifié ni supprimé.",
|
||||||
"cancel": "Annuler",
|
"cancel": "Annuler",
|
||||||
|
"change_pass_confirm": "Confirmer la nouvelle :",
|
||||||
|
"change_pass_current": "Phrase secrète actuelle :",
|
||||||
|
"change_pass_new": "Nouvelle phrase secrète :",
|
||||||
|
"change_pass_title": "Changer la phrase secrète",
|
||||||
"characters": "caractères",
|
"characters": "caractères",
|
||||||
"chat": "Discussion",
|
"chat": "Discussion",
|
||||||
|
"chat_accent_amber": "Ambre",
|
||||||
|
"chat_accent_blue": "Bleu",
|
||||||
|
"chat_accent_green": "Vert",
|
||||||
|
"chat_accent_pink": "Rose",
|
||||||
|
"chat_accent_purple": "Violet",
|
||||||
|
"chat_accent_theme": "Thème",
|
||||||
|
"chat_add_contact": "Ajouter un contact",
|
||||||
|
"chat_awaiting_key": "En attente de réponse",
|
||||||
|
"chat_bubble_minimal": "Minimale",
|
||||||
|
"chat_bubble_rounded": "Arrondie",
|
||||||
|
"chat_bubble_square": "Carrée",
|
||||||
|
"chat_buffer_loading": "Tampon de chat: …",
|
||||||
|
"chat_buffer_preparing": "Tampon de chat: préparation %d/%d…",
|
||||||
|
"chat_buffer_ready": "Tampon de chat: %d/%d prêts",
|
||||||
|
"chat_buffer_sending": "Chat: envoi de %d messages…",
|
||||||
|
"chat_buffer_sending_one": "Chat: envoi de %d message…",
|
||||||
"chat_cancel": "Annuler",
|
"chat_cancel": "Annuler",
|
||||||
|
"chat_contact_added": "Contact ajouté — renommez-le dans Contacts",
|
||||||
"chat_contact_request": "demande de contact",
|
"chat_contact_request": "demande de contact",
|
||||||
|
"chat_copy_address_tip": "Cliquer pour copier l'adresse",
|
||||||
|
"chat_density_comfortable": "Confortable",
|
||||||
|
"chat_density_compact": "Compacte",
|
||||||
|
"chat_emoji_color": "Couleur",
|
||||||
|
"chat_emoji_mono": "Monochrome",
|
||||||
|
"chat_emoji_search": "Rechercher un emoji",
|
||||||
"chat_empty_hint": "Aucune conversation pour l'instant. Les messages que vous recevez apparaîtront ici.",
|
"chat_empty_hint": "Aucune conversation pour l'instant. Les messages que vous recevez apparaîtront ici.",
|
||||||
|
"chat_empty_start": "Commencez-en une avec « Nouvelle conversation ».",
|
||||||
|
"chat_empty_title": "Aucune conversation pour l'instant",
|
||||||
|
"chat_export": "Exporter le chat…",
|
||||||
|
"chat_export_done": "Conversation exportée",
|
||||||
|
"chat_export_failed": "Impossible d'écrire le fichier d'exportation.",
|
||||||
|
"chat_export_warn": "Enregistre les messages déchiffrés en texte clair. Conservez le fichier en lieu sûr.",
|
||||||
|
"chat_filter": "Chat",
|
||||||
|
"chat_hidden_toast": "Conversation masquée — un nouveau message la fait réapparaître",
|
||||||
|
"chat_hide": "Masquer",
|
||||||
|
"chat_hide_hidden": "Masquer masqués",
|
||||||
|
"chat_jump_latest": "Récents",
|
||||||
|
"chat_len_over": "Message trop long",
|
||||||
"chat_locked_hint": "Déverrouillez votre portefeuille pour charger vos discussions.",
|
"chat_locked_hint": "Déverrouillez votre portefeuille pour charger vos discussions.",
|
||||||
"chat_new_button": "Nouvelle conversation",
|
"chat_mute": "Muet",
|
||||||
|
"chat_new_button": "Nouvelle discussion",
|
||||||
"chat_new_message": "Message",
|
"chat_new_message": "Message",
|
||||||
|
"chat_new_message_toast": "Nouveau message chiffré",
|
||||||
"chat_new_send": "Envoyer la demande",
|
"chat_new_send": "Envoyer la demande",
|
||||||
"chat_new_title": "Nouvelle conversation",
|
"chat_new_title": "Nouvelle discussion",
|
||||||
"chat_new_zaddr": "Adresse Z du destinataire",
|
"chat_new_zaddr": "Adresse Z du destinataire",
|
||||||
|
"chat_no_matches": "Aucune conversation ne correspond à votre recherche.",
|
||||||
|
"chat_no_z_contacts": "Aucun contact avec adresse blindée pour l'instant",
|
||||||
|
"chat_opt_bubble_accent": "Couleur de bulle",
|
||||||
|
"chat_opt_bubble_style": "Style de bulle",
|
||||||
|
"chat_opt_density": "Densité des messages",
|
||||||
|
"chat_opt_emoji": "Style d'emoji",
|
||||||
|
"chat_opt_enter_sends": "Entrée envoie le message",
|
||||||
|
"chat_opt_font_size": "Taille du texte",
|
||||||
|
"chat_opt_global_clock": "Format d'horloge global",
|
||||||
|
"chat_opt_poll": "Fréquence d'actualisation",
|
||||||
|
"chat_opt_timestamp": "Horodatage",
|
||||||
|
"chat_pick_contact": "Choisir dans les contacts…",
|
||||||
|
"chat_rename": "Renommer le contact",
|
||||||
|
"chat_rename_hint": "Nom du contact",
|
||||||
|
"chat_renamed": "Contact renommé",
|
||||||
|
"chat_retry": "Réessayer",
|
||||||
|
"chat_search": "Rechercher des conversations",
|
||||||
|
"chat_sec_appearance": "APPARENCE",
|
||||||
|
"chat_sec_messaging": "MESSAGERIE",
|
||||||
"chat_select_hint": "Sélectionnez une conversation pour l'afficher.",
|
"chat_select_hint": "Sélectionnez une conversation pour l'afficher.",
|
||||||
"chat_send": "Envoyer",
|
"chat_send": "Envoyer",
|
||||||
"chat_send_failed": "non envoyé",
|
"chat_send_failed": "non envoyé",
|
||||||
|
"chat_sending": "envoi…",
|
||||||
|
"chat_settings_done": "Terminé",
|
||||||
|
"chat_settings_section": "CHAT ET CONTACTS",
|
||||||
|
"chat_settings_tip": "Personnaliser le chat",
|
||||||
|
"chat_settings_title": "Paramètres du chat",
|
||||||
|
"chat_show_hidden": "Afficher masqués",
|
||||||
|
"chat_time_now": "à l'instant",
|
||||||
"chat_toast_compose_failed": "Impossible de composer le message (trop long ?).",
|
"chat_toast_compose_failed": "Impossible de composer le message (trop long ?).",
|
||||||
"chat_toast_lite_busy": "Un envoi est déjà en cours, ou aucun portefeuille n'est ouvert.",
|
"chat_toast_lite_busy": "Un envoi est déjà en cours, ou aucun portefeuille n'est ouvert.",
|
||||||
|
"chat_toast_need_funds": "Un petit solde blindé est nécessaire pour envoyer des messages (pour couvrir les frais).",
|
||||||
"chat_toast_no_zaddr": "Aucune adresse Z disponible pour envoyer le message.",
|
"chat_toast_no_zaddr": "Aucune adresse Z disponible pour envoyer le message.",
|
||||||
"chat_toast_not_connected": "Non connecté — message non envoyé.",
|
"chat_toast_not_connected": "Non connecté — message non envoyé.",
|
||||||
"chat_toast_request_compose_failed": "Impossible de composer la demande de contact (adresse / texte invalide ?).",
|
"chat_toast_request_compose_failed": "Impossible de composer la demande de contact (adresse / texte invalide ?).",
|
||||||
"chat_toast_request_queued": "Demande de contact mise en file d'attente.",
|
"chat_toast_request_queued": "Demande de contact mise en file d'attente.",
|
||||||
"chat_toast_waiting_reply": "En attente de la réponse de ce contact avant de pouvoir lui écrire.",
|
"chat_toast_waiting_reply": "En attente de la réponse de ce contact avant de pouvoir lui écrire.",
|
||||||
|
"chat_today": "Aujourd'hui",
|
||||||
|
"chat_ts_12h": "12 heures",
|
||||||
|
"chat_ts_24h": "24 heures",
|
||||||
|
"chat_ts_global": "Suivre global",
|
||||||
|
"chat_ts_global_short": "Global",
|
||||||
|
"chat_unhide": "Afficher",
|
||||||
|
"chat_unmute": "Réactiver",
|
||||||
|
"chat_verify_key": "Clé d'identité — comparez pour vérifier",
|
||||||
"chat_waiting_reply": "En attente de la réponse de ce contact — vous pourrez lui écrire dès qu'il aura répondu.",
|
"chat_waiting_reply": "En attente de la réponse de ce contact — vous pourrez lui écrire dès qu'il aura répondu.",
|
||||||
|
"chat_yesterday": "Hier",
|
||||||
"chat_you": "Vous",
|
"chat_you": "Vous",
|
||||||
"choose_icon": "Choisir une icône",
|
"choose_icon": "Choisir une icône",
|
||||||
"clear": "Effacer",
|
"clear": "Effacer",
|
||||||
@@ -147,6 +239,7 @@
|
|||||||
"click_copy_address": "Cliquez pour copier l'adresse",
|
"click_copy_address": "Cliquez pour copier l'adresse",
|
||||||
"click_copy_uri": "Cliquez pour copier l'URI",
|
"click_copy_uri": "Cliquez pour copier l'URI",
|
||||||
"click_to_copy": "Cliquez pour copier",
|
"click_to_copy": "Cliquez pour copier",
|
||||||
|
"clock_format": "Format d'horloge",
|
||||||
"close": "Fermer",
|
"close": "Fermer",
|
||||||
"conf_count": "%d conf.",
|
"conf_count": "%d conf.",
|
||||||
"confirm_and_send": "Confirmer & Envoyer",
|
"confirm_and_send": "Confirmer & Envoyer",
|
||||||
@@ -180,10 +273,24 @@
|
|||||||
"connected_peers": "Pairs connectés",
|
"connected_peers": "Pairs connectés",
|
||||||
"connecting": "Connexion...",
|
"connecting": "Connexion...",
|
||||||
"console": "Console",
|
"console": "Console",
|
||||||
|
"console_accents": "Accents de couleur",
|
||||||
"console_app": "App",
|
"console_app": "App",
|
||||||
"console_auto_scroll": "Défilement auto",
|
"console_auto_scroll": "Défilement auto",
|
||||||
"console_available_commands": "Commandes disponibles :",
|
"console_available_commands": "Commandes disponibles :",
|
||||||
|
"console_backend_reference": "Référence des commandes du backend",
|
||||||
|
"console_backend_unavailable": "Aucun backend",
|
||||||
"console_capturing_output": "Capture de la sortie du daemon...",
|
"console_capturing_output": "Capture de la sortie du daemon...",
|
||||||
|
"console_cat_advanced": "Avancé",
|
||||||
|
"console_cat_blockchain": "Blockchain",
|
||||||
|
"console_cat_control": "Contrôle",
|
||||||
|
"console_cat_keys": "Clés et sécurité",
|
||||||
|
"console_cat_mining": "Minage",
|
||||||
|
"console_cat_network": "Réseau",
|
||||||
|
"console_cat_raw_transactions": "Transactions brutes",
|
||||||
|
"console_cat_send": "Envoyer",
|
||||||
|
"console_cat_sync": "Synchronisation",
|
||||||
|
"console_cat_utility": "Utilitaires",
|
||||||
|
"console_cat_wallet": "Portefeuille",
|
||||||
"console_clear": "Effacer",
|
"console_clear": "Effacer",
|
||||||
"console_clear_console": "Effacer la console",
|
"console_clear_console": "Effacer la console",
|
||||||
"console_cleared": "Console effacée",
|
"console_cleared": "Console effacée",
|
||||||
@@ -215,18 +322,37 @@
|
|||||||
"console_help_help": " help - Afficher ce message d'aide",
|
"console_help_help": " help - Afficher ce message d'aide",
|
||||||
"console_help_setgenerate": " setgenerate - Contrôler le minage",
|
"console_help_setgenerate": " setgenerate - Contrôler le minage",
|
||||||
"console_help_stop": " stop - Arrêter le daemon",
|
"console_help_stop": " stop - Arrêter le daemon",
|
||||||
|
"console_last_error": "Dernière erreur :",
|
||||||
"console_line_count": "%zu lignes",
|
"console_line_count": "%zu lignes",
|
||||||
"console_matches": "correspondances",
|
"console_matches": "correspondances",
|
||||||
"console_new_lines": "%d nouvelles lignes",
|
"console_new_lines": "%d nouvelles lignes",
|
||||||
"console_no_daemon": "Pas de daemon",
|
"console_no_daemon": "Pas de daemon",
|
||||||
|
"console_no_output": "(aucune sortie)",
|
||||||
"console_not_connected": "Erreur : Non connecté au daemon",
|
"console_not_connected": "Erreur : Non connecté au daemon",
|
||||||
|
"console_not_connected_lite": "Erreur : Aucun portefeuille ouvert",
|
||||||
"console_quit_note": "'quit'/'exit' ne sont pas nécessaires ici — fermez simplement la fenêtre.",
|
"console_quit_note": "'quit'/'exit' ne sont pas nécessaires ici — fermez simplement la fenêtre.",
|
||||||
|
"console_ref_builds": "Génère",
|
||||||
|
"console_ref_cancel": "Annuler",
|
||||||
|
"console_ref_destructive": "Sensible",
|
||||||
|
"console_ref_example": "Exemple",
|
||||||
|
"console_ref_insert": "Insérer dans la console",
|
||||||
|
"console_ref_insert_run": "Insérer et exécuter",
|
||||||
|
"console_ref_no_match": "Aucune commande ne correspond.",
|
||||||
|
"console_ref_no_params": "Ne prend aucun paramètre.",
|
||||||
|
"console_ref_optional": "optionnel",
|
||||||
|
"console_ref_parameters": "Paramètres",
|
||||||
|
"console_ref_run": "Exécuter",
|
||||||
|
"console_ref_run_confirm": "Exécuter %s maintenant ? C'est une commande à conséquences.",
|
||||||
|
"console_ref_search_hint": "Rechercher par nom ou tâche…",
|
||||||
|
"console_ref_select_hint": "Sélectionnez une commande pour voir ce qu'elle fait.",
|
||||||
|
"console_ref_value": "valeur",
|
||||||
"console_rpc_reference": "Référence des commandes RPC",
|
"console_rpc_reference": "Référence des commandes RPC",
|
||||||
"console_rpc_trace": "RPC",
|
"console_rpc_trace": "RPC",
|
||||||
"console_scanline": "Scanline de la console",
|
"console_scanline": "Scanline de la console",
|
||||||
"console_search_commands": "Rechercher des commandes...",
|
"console_search_commands": "Rechercher des commandes...",
|
||||||
"console_select_all": "Tout sélectionner",
|
"console_select_all": "Tout sélectionner",
|
||||||
"console_show_app_output": "Afficher les lignes du journal du portefeuille [app]",
|
"console_show_app_output": "Afficher les lignes du journal du portefeuille [app]",
|
||||||
|
"console_show_backend_ref": "Afficher la référence des commandes du backend",
|
||||||
"console_show_daemon_output": "Afficher la sortie du daemon",
|
"console_show_daemon_output": "Afficher la sortie du daemon",
|
||||||
"console_show_errors_only": "Afficher uniquement les erreurs",
|
"console_show_errors_only": "Afficher uniquement les erreurs",
|
||||||
"console_show_rpc_ref": "Afficher la référence des commandes RPC",
|
"console_show_rpc_ref": "Afficher la référence des commandes RPC",
|
||||||
@@ -239,17 +365,43 @@
|
|||||||
"console_status_stopped": "Arrêté",
|
"console_status_stopped": "Arrêté",
|
||||||
"console_status_stopping": "Arrêt",
|
"console_status_stopping": "Arrêt",
|
||||||
"console_status_unknown": "Inconnu",
|
"console_status_unknown": "Inconnu",
|
||||||
|
"console_stop_confirm_node": "'stop' arrêtera le nœud et déconnectera le portefeuille. Tapez à nouveau 'stop' pour confirmer.",
|
||||||
"console_tab_completion": "Tab pour compléter",
|
"console_tab_completion": "Tab pour compléter",
|
||||||
|
"console_text_colors": "Couleurs du texte",
|
||||||
|
"console_toggle_accents": "Basculer les accents de couleur des lignes",
|
||||||
|
"console_toggle_text_color": "Basculer les couleurs du texte des lignes",
|
||||||
"console_type_help": "Tapez 'help' pour les commandes disponibles",
|
"console_type_help": "Tapez 'help' pour les commandes disponibles",
|
||||||
"console_welcome": "Bienvenue dans la console ObsidianDragon",
|
"console_welcome": "Bienvenue dans la console ObsidianDragon",
|
||||||
"console_zoom_in": "Agrandir",
|
"console_zoom_in": "Agrandir",
|
||||||
"console_zoom_out": "Réduire",
|
"console_zoom_out": "Réduire",
|
||||||
|
"contact_avatar": "AVATAR",
|
||||||
|
"contact_avatar_bad_image": "Cette image n'a pas pu être chargée.",
|
||||||
|
"contact_avatar_badge": "Badge",
|
||||||
|
"contact_avatar_badge_hint": "Le badge est choisi automatiquement selon le type d'adresse.",
|
||||||
|
"contact_avatar_choose": "Choisir une image…",
|
||||||
|
"contact_avatar_copy_failed": "Impossible de copier cette image.",
|
||||||
|
"contact_avatar_icon": "Icône",
|
||||||
|
"contact_avatar_image": "Image",
|
||||||
|
"contact_avatar_image_hint": "L'image est copiée dans l'application pour rester disponible si l'original est déplacé.",
|
||||||
|
"contact_avatar_remove": "Retirer",
|
||||||
|
"contact_avatar_shielded": "Blindée",
|
||||||
|
"contact_avatar_transparent": "Transparente",
|
||||||
"contact_global": "Afficher dans tous les portefeuilles (contact global)",
|
"contact_global": "Afficher dans tous les portefeuilles (contact global)",
|
||||||
"contact_global_badge_tt": "Contact global — visible dans tous les portefeuilles",
|
"contact_global_badge_tt": "Contact global — visible dans tous les portefeuilles",
|
||||||
"contact_global_tt": "Activé : ce contact reste visible quel que soit le portefeuille chargé. Désactivé : il appartient au portefeuille actuel uniquement.",
|
"contact_global_tt": "Activé : ce contact reste visible quel que soit le portefeuille chargé. Désactivé : il appartient au portefeuille actuel uniquement.",
|
||||||
|
"contact_preview_addr": "L'adresse apparaîtra ici",
|
||||||
|
"contact_preview_name": "Nom du contact",
|
||||||
|
"contact_wallet_loading": "Le portefeuille se charge encore — cochez « Afficher dans chaque portefeuille » ou réessayez dans un instant.",
|
||||||
"contacts": "Contacts",
|
"contacts": "Contacts",
|
||||||
|
"contacts_avatar_shape": "Forme de l'avatar",
|
||||||
|
"contacts_list_scale": "Échelle de la liste",
|
||||||
"contacts_search_no_match": "Aucun contact correspondant",
|
"contacts_search_no_match": "Aucun contact correspondant",
|
||||||
"contacts_search_placeholder": "Rechercher des contacts...",
|
"contacts_search_placeholder": "Rechercher des contacts...",
|
||||||
|
"contacts_settings_tip": "Personnaliser les contacts",
|
||||||
|
"contacts_settings_title": "Paramètres des contacts",
|
||||||
|
"contacts_shape_circle": "Cercle",
|
||||||
|
"contacts_shape_square": "Carré",
|
||||||
|
"contacts_shape_tab": "Onglet",
|
||||||
"copied": "Copié !",
|
"copied": "Copié !",
|
||||||
"copy": "Copier",
|
"copy": "Copier",
|
||||||
"copy_address": "Copier l'adresse complète",
|
"copy_address": "Copier l'adresse complète",
|
||||||
@@ -302,6 +454,19 @@
|
|||||||
"date": "Date",
|
"date": "Date",
|
||||||
"date_label": "Date :",
|
"date_label": "Date :",
|
||||||
"debug_logging": "JOURNALISATION DE DÉBOGAGE",
|
"debug_logging": "JOURNALISATION DE DÉBOGAGE",
|
||||||
|
"decrypt_desc": "Le portefeuille sera exporté, le daemon redémarré avec un nouveau portefeuille non chiffré, et toutes les clés réimportées. Cela peut prendre plusieurs minutes selon la taille du portefeuille.",
|
||||||
|
"decrypt_error_title": "Échec du déchiffrement",
|
||||||
|
"decrypt_step_backup": "Sauvegarde du portefeuille chiffré",
|
||||||
|
"decrypt_step_export": "Exportation des clés du portefeuille",
|
||||||
|
"decrypt_step_restart": "Redémarrage du daemon",
|
||||||
|
"decrypt_step_stop": "Arrêt du daemon",
|
||||||
|
"decrypt_step_unlock": "Déverrouillage du portefeuille",
|
||||||
|
"decrypt_success_desc": "Votre portefeuille est maintenant non chiffré. Une sauvegarde du portefeuille chiffré a été enregistrée sous wallet.dat.encrypted.bak dans votre répertoire de données.",
|
||||||
|
"decrypt_success_title": "Portefeuille déchiffré avec succès !",
|
||||||
|
"decrypt_title": "Supprimer le chiffrement du portefeuille",
|
||||||
|
"decrypt_wait_general": "Veuillez patienter. Le daemon exporte les clés, redémarre et réimporte. Cela peut prendre plusieurs minutes.",
|
||||||
|
"decrypt_wait_restart": "En attente du démarrage complet du daemon...",
|
||||||
|
"decrypt_warning": "Cela supprimera le chiffrement de votre portefeuille. Vos clés privées seront stockées sans protection sur le disque.",
|
||||||
"delete": "Supprimer",
|
"delete": "Supprimer",
|
||||||
"delete_blockchain": "Supprimer Blockchain",
|
"delete_blockchain": "Supprimer Blockchain",
|
||||||
"delete_blockchain_confirm": "Supprimer & Resynchroniser",
|
"delete_blockchain_confirm": "Supprimer & Resynchroniser",
|
||||||
@@ -314,6 +479,15 @@
|
|||||||
"download_bootstrap": "Télécharger Bootstrap",
|
"download_bootstrap": "Télécharger Bootstrap",
|
||||||
"dragonx_green": "DragonX (Vert)",
|
"dragonx_green": "DragonX (Vert)",
|
||||||
"edit": "Modifier",
|
"edit": "Modifier",
|
||||||
|
"enc_confirm": "Confirmer :",
|
||||||
|
"enc_desc": "Chiffrer votre portefeuille protège vos clés privées avec une phrase secrète. Après le chiffrement, le daemon redémarrera.",
|
||||||
|
"enc_encrypting": "Chiffrement du portefeuille...",
|
||||||
|
"enc_pin_desc": "Un code PIN de 4 à 8 chiffres vous permet de déverrouiller votre portefeuille sans saisir la phrase secrète complète à chaque fois.",
|
||||||
|
"enc_pin_set_ok": "Code PIN défini avec succès",
|
||||||
|
"enc_pin_skipped": "Code PIN ignoré. Vous pourrez en définir un plus tard dans les Paramètres.",
|
||||||
|
"enc_pin_vault_fail": "Échec de la création du coffre du code PIN",
|
||||||
|
"enc_success": "Portefeuille chiffré avec succès !",
|
||||||
|
"enc_wait": "Veuillez patienter, ne fermez pas l'application.",
|
||||||
"error": "Erreur",
|
"error": "Erreur",
|
||||||
"error_format": "Erreur : %s",
|
"error_format": "Erreur : %s",
|
||||||
"est_time_to_block": "Temps est. par bloc",
|
"est_time_to_block": "Temps est. par bloc",
|
||||||
@@ -333,23 +507,34 @@
|
|||||||
"explorer_mempool": "Mempool",
|
"explorer_mempool": "Mempool",
|
||||||
"explorer_mempool_size": "Taille",
|
"explorer_mempool_size": "Taille",
|
||||||
"explorer_mempool_txs": "Transactions",
|
"explorer_mempool_txs": "Transactions",
|
||||||
|
"explorer_next_block": "Bloc suivant",
|
||||||
"explorer_no_results": "Aucun bloc en cache correspondant",
|
"explorer_no_results": "Aucun bloc en cache correspondant",
|
||||||
"explorer_not_connected": "Non connecté au daemon — impossible de rechercher un hash de bloc ou de transaction",
|
"explorer_not_connected": "Non connecté au daemon — impossible de rechercher un hash de bloc ou de transaction",
|
||||||
|
"explorer_prev_block": "Bloc précédent",
|
||||||
"explorer_recent_blocks": "Blocs récents",
|
"explorer_recent_blocks": "Blocs récents",
|
||||||
"explorer_search": "Rechercher",
|
"explorer_search": "Rechercher",
|
||||||
"explorer_section": "EXPLORATEUR",
|
"explorer_section": "EXPLORATEUR",
|
||||||
"explorer_tx_outputs": "Sorties",
|
"explorer_tx_outputs": "Sorties",
|
||||||
"explorer_tx_size": "Taille",
|
"explorer_tx_size": "Taille",
|
||||||
|
"explorer_txid": "TxID :",
|
||||||
"export": "Exporter",
|
"export": "Exporter",
|
||||||
"export_csv": "Exporter en CSV",
|
"export_csv": "Exporter en CSV",
|
||||||
"export_keys_btn": "Exporter les clés",
|
"export_keys_btn": "Exporter les clés",
|
||||||
"export_keys_danger": "DANGER : Ceci exportera TOUTES les clés privées de votre portefeuille ! Toute personne ayant accès à ce fichier peut voler vos fonds. Conservez-le en sécurité et supprimez-le après utilisation.",
|
"export_keys_danger": "DANGER : Ceci exportera TOUTES les clés privées de votre portefeuille ! Toute personne ayant accès à ce fichier peut voler vos fonds. Conservez-le en sécurité et supprimez-le après utilisation.",
|
||||||
"export_keys_include_t": "Inclure les adresses T (transparentes)",
|
"export_keys_include_t": "Inclure les adresses T (transparentes)",
|
||||||
"export_keys_include_z": "Inclure les adresses Z (blindées)",
|
"export_keys_include_z": "Inclure les adresses Z (blindées)",
|
||||||
|
"export_keys_none_addrs": "Aucune adresse à exporter",
|
||||||
|
"export_keys_none_result": "Aucune clé exportée (0 sur %d) — déverrouillez le portefeuille et réessayez.",
|
||||||
|
"export_keys_none_toast": "Aucune clé n'a pu être exportée — le portefeuille est-il déverrouillé ?",
|
||||||
|
"export_keys_not_connected": "Non connecté au daemon",
|
||||||
"export_keys_options": "Options d'exportation :",
|
"export_keys_options": "Options d'exportation :",
|
||||||
|
"export_keys_partial": "%d clés sur %d exportées — incomplet (certaines sans clé de dépense, ou le portefeuille est verrouillé).",
|
||||||
|
"export_keys_partial_toast": "Export partiel : %d clés sur %d",
|
||||||
"export_keys_progress": "Exportation %d/%d...",
|
"export_keys_progress": "Exportation %d/%d...",
|
||||||
|
"export_keys_select_type": "Sélectionnez au moins un type d'adresse",
|
||||||
"export_keys_success": "Clés exportées avec succès",
|
"export_keys_success": "Clés exportées avec succès",
|
||||||
"export_keys_title": "Exporter toutes les clés privées",
|
"export_keys_title": "Exporter toutes les clés privées",
|
||||||
|
"export_keys_write_fail": "Échec de l'écriture du fichier de clés.",
|
||||||
"export_private_key": "Exporter la clé privée",
|
"export_private_key": "Exporter la clé privée",
|
||||||
"export_tx_count": "Exporter %zu transactions en fichier CSV.",
|
"export_tx_count": "Exporter %zu transactions en fichier CSV.",
|
||||||
"export_tx_file_fail": "Impossible de créer le fichier CSV",
|
"export_tx_file_fail": "Impossible de créer le fichier CSV",
|
||||||
@@ -389,23 +574,50 @@
|
|||||||
"hide_qr": "Masquer le QR",
|
"hide_qr": "Masquer le QR",
|
||||||
"hide_zero_balances": "Masquer les soldes à 0",
|
"hide_zero_balances": "Masquer les soldes à 0",
|
||||||
"history": "Historique",
|
"history": "Historique",
|
||||||
|
"img_picker_count": "%d image(s) dans ce dossier",
|
||||||
|
"img_picker_empty": "Ce dossier ne contient ni sous-dossiers ni images.",
|
||||||
|
"img_picker_none": "Aucune image dans ce dossier",
|
||||||
|
"img_picker_pictures": "Dossier Images",
|
||||||
|
"img_picker_title": "Choisir une image",
|
||||||
|
"img_picker_use": "Utiliser l'image",
|
||||||
"immature_type": "Immature",
|
"immature_type": "Immature",
|
||||||
"import": "Importer",
|
"import": "Importer",
|
||||||
|
"import_key_address": "Adresse :",
|
||||||
"import_key_btn": "Importer clé(s)",
|
"import_key_btn": "Importer clé(s)",
|
||||||
|
"import_key_done": "Importée. Le portefeuille réanalyse.",
|
||||||
|
"import_key_field": "Clé",
|
||||||
"import_key_formats": "Formats de clés pris en charge :",
|
"import_key_formats": "Formats de clés pris en charge :",
|
||||||
"import_key_full_rescan": "(0 = rescan complet)",
|
"import_key_full_rescan": "(0 = rescan complet)",
|
||||||
|
"import_key_import": "Importer",
|
||||||
"import_key_label": "Clé(s) privée(s) :",
|
"import_key_label": "Clé(s) privée(s) :",
|
||||||
|
"import_key_need_node": "Connectez un nœud en cours d'exécution pour importer une clé.",
|
||||||
"import_key_no_valid": "Aucune clé valide trouvée dans l'entrée",
|
"import_key_no_valid": "Aucune clé valide trouvée dans l'entrée",
|
||||||
"import_key_progress": "Importation %d/%d...",
|
"import_key_progress": "Importation %d/%d...",
|
||||||
"import_key_rescan": "Re-scanner la blockchain après l'importation",
|
"import_key_rescan": "Re-scanner la blockchain après l'importation",
|
||||||
|
"import_key_rescanning": "Import et réanalyse — cela peut prendre plusieurs minutes",
|
||||||
|
"import_key_reveal_tip": "Afficher/masquer la clé",
|
||||||
"import_key_start_height": "Hauteur de départ :",
|
"import_key_start_height": "Hauteur de départ :",
|
||||||
"import_key_success": "Clés importées avec succès",
|
"import_key_success": "Clés importées avec succès",
|
||||||
"import_key_t_format": "Clés privées WIF d'adresses T",
|
"import_key_t_format": "Clés privées WIF d'adresses T",
|
||||||
"import_key_title": "Importer une clé privée",
|
"import_key_title": "Importer une clé privée",
|
||||||
"import_key_tooltip": "Entrez une ou plusieurs clés privées, une par ligne.\nPrend en charge les clés z-adresse et t-adresse.\nLes lignes commençant par # sont traitées comme des commentaires.",
|
"import_key_tooltip": "Entrez une ou plusieurs clés privées, une par ligne.\nPrend en charge les clés z-adresse et t-adresse.\nLes lignes commençant par # sont traitées comme des commentaires.",
|
||||||
|
"import_key_type_tkey": "Clé privée transparente",
|
||||||
|
"import_key_type_unknown": "Format de clé non reconnu",
|
||||||
|
"import_key_type_zspend": "Clé de dépense blindée",
|
||||||
|
"import_key_type_zview": "Clé de visualisation blindée (lecture seule)",
|
||||||
|
"import_key_warn": "N'importez qu'une clé qui vous appartient — elle donne accès à ses fonds.",
|
||||||
"import_key_warning": "Attention : Ne partagez jamais vos clés privées ! L'importation de clés provenant de sources non fiables peut compromettre votre portefeuille.",
|
"import_key_warning": "Attention : Ne partagez jamais vos clés privées ! L'importation de clés provenant de sources non fiables peut compromettre votre portefeuille.",
|
||||||
|
"import_key_wrong_type": "Ceci ressemble à une clé de visualisation. Utilisez plutôt « Importer la clé de visualisation ».",
|
||||||
"import_key_z_format": "Clés de dépenses z-adresse (secret-extended-key-...)",
|
"import_key_z_format": "Clés de dépenses z-adresse (secret-extended-key-...)",
|
||||||
"import_private_key": "Importer une clé privée...",
|
"import_private_key": "Importer une clé privée...",
|
||||||
|
"import_scan_hint": "0 = réanalyser depuis le début",
|
||||||
|
"import_scan_label": "Analyser à partir de la hauteur de bloc (facultatif)",
|
||||||
|
"import_scan_tip": "hauteur actuelle",
|
||||||
|
"import_scan_transparent": "Les clés transparentes réanalysent toujours entièrement",
|
||||||
|
"import_viewkey_field": "Clé de visualisation",
|
||||||
|
"import_viewkey_note": "Lecture seule : une clé de visualisation révèle le solde et les transactions d'une adresse, mais ne peut pas dépenser ses fonds.",
|
||||||
|
"import_viewkey_title": "Importer la clé de visualisation",
|
||||||
|
"import_viewkey_wrong_type": "Ceci ressemble à une clé de dépense. Utilisez plutôt « Importer une clé privée ».",
|
||||||
"incorrect_passphrase": "Mot de passe incorrect",
|
"incorrect_passphrase": "Mot de passe incorrect",
|
||||||
"incorrect_pin": "PIN incorrect",
|
"incorrect_pin": "PIN incorrect",
|
||||||
"insufficient_funds": "Fonds insuffisants pour ce montant plus les frais.",
|
"insufficient_funds": "Fonds insuffisants pour ce montant plus les frais.",
|
||||||
@@ -414,6 +626,7 @@
|
|||||||
"keep": "Conserver",
|
"keep": "Conserver",
|
||||||
"keep_daemon": "Garder le daemon en marche",
|
"keep_daemon": "Garder le daemon en marche",
|
||||||
"key_export_click_retrieve": "Cliquez pour récupérer la clé de votre portefeuille",
|
"key_export_click_retrieve": "Cliquez pour récupérer la clé de votre portefeuille",
|
||||||
|
"key_export_failed": "Impossible d'exporter la clé — déverrouillez le portefeuille (s'il est chiffré) et réessayez.",
|
||||||
"key_export_fetching": "Récupération de la clé depuis le portefeuille...",
|
"key_export_fetching": "Récupération de la clé depuis le portefeuille...",
|
||||||
"key_export_private_key": "Clé privée :",
|
"key_export_private_key": "Clé privée :",
|
||||||
"key_export_private_warning": "Gardez cette clé SECRÈTE ! Toute personne possédant cette clé peut dépenser vos fonds. Ne la partagez jamais en ligne ou avec des tiers non fiables.",
|
"key_export_private_warning": "Gardez cette clé SECRÈTE ! Toute personne possédant cette clé peut dépenser vos fonds. Ne la partagez jamais en ligne ou avec des tiers non fiables.",
|
||||||
@@ -431,6 +644,7 @@
|
|||||||
"lite_birthday_backup": "Date de création : %llu (à sauvegarder également)",
|
"lite_birthday_backup": "Date de création : %llu (à sauvegarder également)",
|
||||||
"lite_birthday_hint": "Hauteur de bloc à partir de laquelle commencer l'analyse. Laissez 0 si inconnue (analyse complète plus lente).",
|
"lite_birthday_hint": "Hauteur de bloc à partir de laquelle commencer l'analyse. Laissez 0 si inconnue (analyse complète plus lente).",
|
||||||
"lite_birthday_label": "Bloc de création",
|
"lite_birthday_label": "Bloc de création",
|
||||||
|
"lite_console_backend_commands": "Commandes du backend :",
|
||||||
"lite_console_help_passthrough": "Toute autre entrée est exécutée comme une commande de la console du portefeuille lite.",
|
"lite_console_help_passthrough": "Toute autre entrée est exécutée comme une commande de la console du portefeuille lite.",
|
||||||
"lite_copy": "Copier",
|
"lite_copy": "Copier",
|
||||||
"lite_could_not_write": "Impossible d'écrire ",
|
"lite_could_not_write": "Impossible d'écrire ",
|
||||||
@@ -448,6 +662,7 @@
|
|||||||
"lite_net_add_url_hint": "https://votre-serveur-lite",
|
"lite_net_add_url_hint": "https://votre-serveur-lite",
|
||||||
"lite_net_checking": "vérification…",
|
"lite_net_checking": "vérification…",
|
||||||
"lite_net_connected": "Connecté",
|
"lite_net_connected": "Connecté",
|
||||||
|
"lite_net_connecting": "Connexion…",
|
||||||
"lite_net_custom": "Personnalisé",
|
"lite_net_custom": "Personnalisé",
|
||||||
"lite_net_disconnected": "Non connecté",
|
"lite_net_disconnected": "Non connecté",
|
||||||
"lite_net_hidden_section": "Serveurs masqués",
|
"lite_net_hidden_section": "Serveurs masqués",
|
||||||
@@ -534,6 +749,10 @@
|
|||||||
"market_btc_price": "PRIX BTC",
|
"market_btc_price": "PRIX BTC",
|
||||||
"market_cap": "Capitalisation",
|
"market_cap": "Capitalisation",
|
||||||
"market_cap_short": "Cap.",
|
"market_cap_short": "Cap.",
|
||||||
|
"market_chart_loading": "Chargement de l'historique des prix",
|
||||||
|
"market_col_name": "Nom",
|
||||||
|
"market_col_trend": "Tendance",
|
||||||
|
"market_col_value": "Valeur",
|
||||||
"market_iv_1d": "1J",
|
"market_iv_1d": "1J",
|
||||||
"market_iv_1h": "1H",
|
"market_iv_1h": "1H",
|
||||||
"market_iv_1m": "1M",
|
"market_iv_1m": "1M",
|
||||||
@@ -542,11 +761,18 @@
|
|||||||
"market_no_history": "Aucun historique de prix disponible",
|
"market_no_history": "Aucun historique de prix disponible",
|
||||||
"market_no_price": "Pas de données de prix",
|
"market_no_price": "Pas de données de prix",
|
||||||
"market_now": "Maintenant",
|
"market_now": "Maintenant",
|
||||||
|
"market_opt_chart_style": "Style du graphique",
|
||||||
"market_pct_shielded": "%.0f%% Blindé",
|
"market_pct_shielded": "%.0f%% Blindé",
|
||||||
"market_portfolio": "PORTEFEUILLE",
|
"market_portfolio": "PORTEFEUILLE",
|
||||||
"market_price_loading": "Chargement des données de prix...",
|
"market_price_loading": "Chargement des données de prix...",
|
||||||
"market_price_unavailable": "Données de prix indisponibles",
|
"market_price_unavailable": "Données de prix indisponibles",
|
||||||
"market_refresh_price": "Actualiser les données de prix",
|
"market_refresh_price": "Actualiser les données de prix",
|
||||||
|
"market_settings_tip": "Options du marché",
|
||||||
|
"market_settings_title": "Paramètres du marché",
|
||||||
|
"market_style_candle": "Passer aux chandeliers",
|
||||||
|
"market_style_candle_label": "Chandelier",
|
||||||
|
"market_style_line": "Passer au graphique en ligne",
|
||||||
|
"market_style_line_label": "Ligne",
|
||||||
"market_trade_on": "Échanger sur %s",
|
"market_trade_on": "Échanger sur %s",
|
||||||
"market_updated": "\\xc2\\xb7 Mis à jour %s",
|
"market_updated": "\\xc2\\xb7 Mis à jour %s",
|
||||||
"market_vol_short": "Vol",
|
"market_vol_short": "Vol",
|
||||||
@@ -736,6 +962,7 @@
|
|||||||
"output_filename": "Nom du fichier de sortie :",
|
"output_filename": "Nom du fichier de sortie :",
|
||||||
"overview": "Aperçu",
|
"overview": "Aperçu",
|
||||||
"paste": "Coller",
|
"paste": "Coller",
|
||||||
|
"paste_clip_empty": "Le presse-papiers est vide",
|
||||||
"paste_from_clipboard": "Coller depuis le presse-papiers",
|
"paste_from_clipboard": "Coller depuis le presse-papiers",
|
||||||
"pay_from": "Payer depuis",
|
"pay_from": "Payer depuis",
|
||||||
"payment_request": "DEMANDE DE PAIEMENT",
|
"payment_request": "DEMANDE DE PAIEMENT",
|
||||||
@@ -787,7 +1014,14 @@
|
|||||||
"peers_upper": "PAIRS",
|
"peers_upper": "PAIRS",
|
||||||
"peers_version": "Version",
|
"peers_version": "Version",
|
||||||
"pending": "En attente",
|
"pending": "En attente",
|
||||||
|
"pin_change_desc": "Modifiez votre code PIN de déverrouillage. Vous avez besoin de votre code PIN actuel et d'un nouveau code PIN.",
|
||||||
|
"pin_confirm_new_label": "Confirmer le nouveau code PIN :",
|
||||||
|
"pin_current_label": "Code PIN actuel :",
|
||||||
|
"pin_new_label": "Nouveau code PIN (4 à 8 chiffres) :",
|
||||||
"pin_not_set": "PIN non défini. Utilisez le mot de passe pour déverrouiller.",
|
"pin_not_set": "PIN non défini. Utilisez le mot de passe pour déverrouiller.",
|
||||||
|
"pin_remove_desc": "Saisissez votre code PIN actuel pour confirmer la suppression. Vous devrez utiliser votre phrase secrète complète pour déverrouiller.",
|
||||||
|
"pin_setup_desc": "Définissez un code PIN de 4 à 8 chiffres pour déverrouiller rapidement votre portefeuille. Votre phrase secrète sera chiffrée avec ce code PIN et stockée localement.",
|
||||||
|
"pin_wallet_passphrase": "Phrase secrète du portefeuille :",
|
||||||
"ping": "Ping",
|
"ping": "Ping",
|
||||||
"portfolio_add_entry": "Ajouter une entrée",
|
"portfolio_add_entry": "Ajouter une entrée",
|
||||||
"portfolio_add_to": "Ajouter au portefeuille",
|
"portfolio_add_to": "Ajouter au portefeuille",
|
||||||
@@ -826,6 +1060,9 @@
|
|||||||
"portfolio_remove_from": "Retirer du portefeuille d'actifs",
|
"portfolio_remove_from": "Retirer du portefeuille d'actifs",
|
||||||
"portfolio_revert": "Annuler",
|
"portfolio_revert": "Annuler",
|
||||||
"portfolio_save": "Enregistrer",
|
"portfolio_save": "Enregistrer",
|
||||||
|
"portfolio_save_need_address": "Ajoutez au moins une adresse pour enregistrer.",
|
||||||
|
"portfolio_save_need_name": "Saisissez un nom pour enregistrer ce groupe.",
|
||||||
|
"portfolio_save_need_price": "Saisissez un prix manuel supérieur à 0 pour enregistrer.",
|
||||||
"portfolio_search": "Rechercher des adresses\\xE2\\x80\\xA6",
|
"portfolio_search": "Rechercher des adresses\\xE2\\x80\\xA6",
|
||||||
"portfolio_search_icons": "Rechercher des icônes\\xE2\\x80\\xA6",
|
"portfolio_search_icons": "Rechercher des icônes\\xE2\\x80\\xA6",
|
||||||
"portfolio_select_all": "Tout",
|
"portfolio_select_all": "Tout",
|
||||||
@@ -839,11 +1076,12 @@
|
|||||||
"portfolio_spark_min": "Minute",
|
"portfolio_spark_min": "Minute",
|
||||||
"portfolio_spark_month": "Mois",
|
"portfolio_spark_month": "Mois",
|
||||||
"portfolio_spark_week": "Semaine",
|
"portfolio_spark_week": "Semaine",
|
||||||
"portfolio_style_compact": "Lignes compactes",
|
"portfolio_style_compact": "Tableau",
|
||||||
"portfolio_style_detailed": "Lignes détaillées",
|
"portfolio_style_detailed": "Cartes",
|
||||||
"portfolio_style_featured": "Lignes en vedette",
|
"portfolio_style_featured": "En vedette",
|
||||||
"portfolio_style_label": "Style du portefeuille",
|
"portfolio_style_label": "Style du portefeuille",
|
||||||
"portfolio_untitled": "Sans titre",
|
"portfolio_untitled": "Sans titre",
|
||||||
|
"portfolio_wallet_loading": "Attendez la fin du chargement du portefeuille pour ajouter un groupe.",
|
||||||
"price_chart": "Graphique des prix",
|
"price_chart": "Graphique des prix",
|
||||||
"privacy_great": "Excellente confidentialité !",
|
"privacy_great": "Excellente confidentialité !",
|
||||||
"privacy_low": "Faible confidentialité — blindez vos fonds",
|
"privacy_low": "Faible confidentialité — blindez vos fonds",
|
||||||
@@ -973,6 +1211,7 @@
|
|||||||
"send_amount": "Montant",
|
"send_amount": "Montant",
|
||||||
"send_amount_details": "DÉTAILS DU MONTANT",
|
"send_amount_details": "DÉTAILS DU MONTANT",
|
||||||
"send_amount_upper": "MONTANT",
|
"send_amount_upper": "MONTANT",
|
||||||
|
"send_cannot_send_now": "Envoi impossible pour le moment — vérifiez la connexion, la synchronisation et le solde disponible.",
|
||||||
"send_clear_fields": "Effacer tous les champs du formulaire ?",
|
"send_clear_fields": "Effacer tous les champs du formulaire ?",
|
||||||
"send_contacts_button": "Choisir parmi les contacts",
|
"send_contacts_button": "Choisir parmi les contacts",
|
||||||
"send_copy_error": "Copier l'erreur",
|
"send_copy_error": "Copier l'erreur",
|
||||||
@@ -1061,7 +1300,8 @@
|
|||||||
"settings_gradient_bg": "Fond dégradé",
|
"settings_gradient_bg": "Fond dégradé",
|
||||||
"settings_gradient_desc": "Remplacer les arrière-plans texturés par des dégradés lisses",
|
"settings_gradient_desc": "Remplacer les arrière-plans texturés par des dégradés lisses",
|
||||||
"settings_idle_after": "après",
|
"settings_idle_after": "après",
|
||||||
"settings_import_key": "Importer la clé...",
|
"settings_import_key": "Importer une clé privée...",
|
||||||
|
"settings_import_viewkey": "Importer la clé de visualisation...",
|
||||||
"settings_language_note": "Remarque : Certains textes nécessitent un redémarrage pour se mettre à jour",
|
"settings_language_note": "Remarque : Certains textes nécessitent un redémarrage pour se mettre à jour",
|
||||||
"settings_lock_now": "Verrouiller maintenant",
|
"settings_lock_now": "Verrouiller maintenant",
|
||||||
"settings_locked": "Verrouillé",
|
"settings_locked": "Verrouillé",
|
||||||
@@ -1110,6 +1350,7 @@
|
|||||||
"shield_in_progress": "Opération en cours...",
|
"shield_in_progress": "Opération en cours...",
|
||||||
"shield_max_utxos": "UTXOs max par opération",
|
"shield_max_utxos": "UTXOs max par opération",
|
||||||
"shield_merge_done": "Blindage/fusion terminé !",
|
"shield_merge_done": "Blindage/fusion terminé !",
|
||||||
|
"shield_no_zaddr_hint": "Aucune adresse blindée (z) pour l'instant — créez-en une d'abord dans l'onglet Recevoir.",
|
||||||
"shield_op_failed": "Échec de l'opération : ",
|
"shield_op_failed": "Échec de l'opération : ",
|
||||||
"shield_op_submitted": "Opération soumise : ",
|
"shield_op_submitted": "Opération soumise : ",
|
||||||
"shield_operation_id": "ID d'opération : %s",
|
"shield_operation_id": "ID d'opération : %s",
|
||||||
@@ -1148,6 +1389,31 @@
|
|||||||
"submitting_transaction": "Soumission de la transaction...",
|
"submitting_transaction": "Soumission de la transaction...",
|
||||||
"success": "Succès",
|
"success": "Succès",
|
||||||
"summary": "Résumé",
|
"summary": "Résumé",
|
||||||
|
"sweep_button": "Balayer",
|
||||||
|
"sweep_caveat": "Importe la clé pour signer une transaction qui déplace tous ses fonds vers votre adresse. La clé reste dans votre portefeuille avec un solde vide.",
|
||||||
|
"sweep_dest_label": "Envoyer les fonds balayés vers",
|
||||||
|
"sweep_dest_new": "Nouvelle adresse blindée (recommandé)",
|
||||||
|
"sweep_done": "Terminé — fonds balayés vers votre adresse.",
|
||||||
|
"sweep_to": "Balayé vers :",
|
||||||
|
"sweep_toggle": "Balayer vers mon portefeuille (ne pas conserver la clé)",
|
||||||
|
"sweep_tx": "Transaction :",
|
||||||
|
"switch_corrupt_body": "Ce portefeuille semble corrompu — le nœud n'a pas pu l'ouvrir. Restaurez-le depuis une sauvegarde, recréez-le ou tentez de le réparer.",
|
||||||
|
"switch_corrupt_repair": "Tenter une réparation (salvage)",
|
||||||
|
"switch_progress_background": "Continuer en arrière-plan",
|
||||||
|
"switch_progress_default_wallet": "Portefeuille par défaut",
|
||||||
|
"switch_progress_elapsed": "Écoulé",
|
||||||
|
"switch_progress_external_wallet": "Portefeuille externe",
|
||||||
|
"switch_progress_failed_title": "Échec du changement de portefeuille",
|
||||||
|
"switch_progress_from_label": "depuis",
|
||||||
|
"switch_progress_hint": "Un arrêt propre peut prendre jusqu'à une minute.",
|
||||||
|
"switch_progress_reconnecting": "Reconnexion",
|
||||||
|
"switch_progress_starting": "Démarrage du nœud sur le nouveau portefeuille",
|
||||||
|
"switch_progress_stopping": "Arrêt du nœud actuel",
|
||||||
|
"switch_progress_title": "Changement de portefeuille",
|
||||||
|
"switch_stopnode_body": "Changer de portefeuille redémarre le nœud sur le portefeuille sélectionné. Le nœud en cours sera arrêté puis relancé sur le nouveau portefeuille — si vous l'avez laissé tourner exprès, il redémarre automatiquement.",
|
||||||
|
"switch_stopnode_confirm": "Arrêter le nœud et changer",
|
||||||
|
"switch_stopnode_title": "Arrêter le nœud en cours d'exécution ?",
|
||||||
|
"switch_stopnode_warn": "Un nœud que ce portefeuille n'a pas démarré est déjà en cours d'exécution.",
|
||||||
"syncing": "Synchronisation...",
|
"syncing": "Synchronisation...",
|
||||||
"t_address": "Adresse T",
|
"t_address": "Adresse T",
|
||||||
"t_addresses": "Adresses T",
|
"t_addresses": "Adresses T",
|
||||||
@@ -1183,8 +1449,10 @@
|
|||||||
"transfer_to": "Transférer à :",
|
"transfer_to": "Transférer à :",
|
||||||
"transparent": "Transparent",
|
"transparent": "Transparent",
|
||||||
"transparent_address": "Adresse transparente",
|
"transparent_address": "Adresse transparente",
|
||||||
|
"try_again": "Réessayer",
|
||||||
"tt_addr_url": "URL de base pour consulter les adresses dans un explorateur de blocs",
|
"tt_addr_url": "URL de base pour consulter les adresses dans un explorateur de blocs",
|
||||||
"tt_address_book": "Gérer les adresses enregistrées pour un envoi rapide",
|
"tt_address_book": "Gérer les adresses enregistrées pour un envoi rapide",
|
||||||
|
"tt_animate_avatars": "Lit les avatars de contact animés (GIF / WebP) ; désactivé n'affiche que la première image",
|
||||||
"tt_auto_lock": "Verrouiller le portefeuille après cette durée d'inactivité",
|
"tt_auto_lock": "Verrouiller le portefeuille après cette durée d'inactivité",
|
||||||
"tt_auto_shield": "Déplacer automatiquement le solde transparent vers des adresses blindées pour la confidentialité",
|
"tt_auto_shield": "Déplacer automatiquement le solde transparent vers des adresses blindées pour la confidentialité",
|
||||||
"tt_backup": "Créer une sauvegarde de votre wallet.dat",
|
"tt_backup": "Créer une sauvegarde de votre wallet.dat",
|
||||||
@@ -1192,10 +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_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",
|
||||||
@@ -1209,10 +1487,34 @@
|
|||||||
"tt_font_scale": "Mettre à l'échelle tout le texte et l'interface (1.0x = par défaut, jusqu'à 1.5x).",
|
"tt_font_scale": "Mettre à l'échelle tout le texte et l'interface (1.0x = par défaut, jusqu'à 1.5x).",
|
||||||
"tt_idle_delay": "Combien de temps attendre avant de commencer le minage",
|
"tt_idle_delay": "Combien de temps attendre avant de commencer le minage",
|
||||||
"tt_import_key": "Importer une clé privée (zkey ou tkey) dans ce portefeuille",
|
"tt_import_key": "Importer une clé privée (zkey ou tkey) dans ce portefeuille",
|
||||||
|
"tt_import_viewkey": "Importer une clé de visualisation blindée pour observer une adresse (lecture seule)",
|
||||||
"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",
|
||||||
@@ -1233,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",
|
||||||
@@ -1257,6 +1564,7 @@
|
|||||||
"tt_website": "Ouvrir le site web DragonX",
|
"tt_website": "Ouvrir le site web DragonX",
|
||||||
"tt_window_opacity": "Opacité de l'arrière-plan (plus bas = bureau visible à travers la fenêtre)",
|
"tt_window_opacity": "Opacité de l'arrière-plan (plus bas = bureau visible à travers la fenêtre)",
|
||||||
"tt_wizard": "Relancer l'assistant de configuration initiale\\nLe daemon sera redémarré",
|
"tt_wizard": "Relancer l'assistant de configuration initiale\\nLe daemon sera redémarré",
|
||||||
|
"tx_chat_badge": "Message",
|
||||||
"tx_confirmations": "%d confirmations",
|
"tx_confirmations": "%d confirmations",
|
||||||
"tx_details_title": "Détails de la transaction",
|
"tx_details_title": "Détails de la transaction",
|
||||||
"tx_from_address": "Adresse d'origine :",
|
"tx_from_address": "Adresse d'origine :",
|
||||||
@@ -1325,6 +1633,8 @@
|
|||||||
"wallets_add_folder_toggle": "+ Analyser un autre dossier pour les portefeuilles…",
|
"wallets_add_folder_toggle": "+ Analyser un autre dossier pour les portefeuilles…",
|
||||||
"wallets_badge_encrypted": "Chiffré (protégé par phrase secrète)",
|
"wallets_badge_encrypted": "Chiffré (protégé par phrase secrète)",
|
||||||
"wallets_badge_encrypted_short": "Chiffré",
|
"wallets_badge_encrypted_short": "Chiffré",
|
||||||
|
"wallets_badge_hd": "Portefeuille HD — impossible de confirmer une phrase de récupération sans l'ouvrir",
|
||||||
|
"wallets_badge_hd_short": "Portefeuille HD",
|
||||||
"wallets_badge_legacy": "Portefeuille hérité (sans phrase de récupération)",
|
"wallets_badge_legacy": "Portefeuille hérité (sans phrase de récupération)",
|
||||||
"wallets_badge_legacy_short": "Hérité",
|
"wallets_badge_legacy_short": "Hérité",
|
||||||
"wallets_badge_seed": "Portefeuille à phrase de récupération (HD)",
|
"wallets_badge_seed": "Portefeuille à phrase de récupération (HD)",
|
||||||
@@ -1343,6 +1653,7 @@
|
|||||||
"wallets_created": "créé",
|
"wallets_created": "créé",
|
||||||
"wallets_creating": "Création du portefeuille — le nœud va redémarrer…",
|
"wallets_creating": "Création du portefeuille — le nœud va redémarrer…",
|
||||||
"wallets_current": "actuel",
|
"wallets_current": "actuel",
|
||||||
|
"wallets_empty_hint": "Analysez un dossier pour trouver d'autres portefeuilles",
|
||||||
"wallets_exists": "Un portefeuille portant ce nom existe déjà.",
|
"wallets_exists": "Un portefeuille portant ce nom existe déjà.",
|
||||||
"wallets_external_tt": "Hors de votre répertoire de données — Ouvrir le lie sur place (sans copie).",
|
"wallets_external_tt": "Hors de votre répertoire de données — Ouvrir le lie sur place (sans copie).",
|
||||||
"wallets_folder_hint": "/chemin/vers/le/dossier contenant les fichiers .dat",
|
"wallets_folder_hint": "/chemin/vers/le/dossier contenant les fichiers .dat",
|
||||||
@@ -1356,7 +1667,9 @@
|
|||||||
"wallets_open_failed": "Impossible d'ouvrir ce portefeuille à son emplacement. Il se trouve probablement sur un lecteur différent de votre répertoire de données — déplacez-le sur le même lecteur (sous Windows, activer le mode développeur permet aussi de créer des liens entre lecteurs).",
|
"wallets_open_failed": "Impossible d'ouvrir ce portefeuille à son emplacement. Il se trouve probablement sur un lecteur différent de votre répertoire de données — déplacez-le sur le même lecteur (sous Windows, activer le mode développeur permet aussi de créer des liens entre lecteurs).",
|
||||||
"wallets_open_folder": "Ouvrir l'emplacement du dossier",
|
"wallets_open_folder": "Ouvrir l'emplacement du dossier",
|
||||||
"wallets_open_inplace_tt": "Ouvre ce portefeuille à son emplacement — lié au répertoire de données (sans copie)",
|
"wallets_open_inplace_tt": "Ouvre ce portefeuille à son emplacement — lié au répertoire de données (sans copie)",
|
||||||
|
"wallets_remove_folder": "Ne plus analyser ce dossier",
|
||||||
"wallets_reveal": "Afficher le dossier",
|
"wallets_reveal": "Afficher le dossier",
|
||||||
|
"wallets_scanned_folders": "Dossiers analysés :",
|
||||||
"wallets_sort_addresses": "Adresses",
|
"wallets_sort_addresses": "Adresses",
|
||||||
"wallets_sort_asc": "Croissant (plus ancien / moins / plus petit d'abord)",
|
"wallets_sort_asc": "Croissant (plus ancien / moins / plus petit d'abord)",
|
||||||
"wallets_sort_by": "Trier :",
|
"wallets_sort_by": "Trier :",
|
||||||
|
|||||||
325
res/lang/ja.json
325
res/lang/ja.json
@@ -30,6 +30,7 @@
|
|||||||
"address_book_added": "アドレスをアドレス帳に追加しました",
|
"address_book_added": "アドレスをアドレス帳に追加しました",
|
||||||
"address_book_confirm_delete": "削除しますか?",
|
"address_book_confirm_delete": "削除しますか?",
|
||||||
"address_book_count": "%zu 件のアドレスを保存済み",
|
"address_book_count": "%zu 件のアドレスを保存済み",
|
||||||
|
"address_book_count_one": "%zu 件のアドレスを保存",
|
||||||
"address_book_deleted": "エントリを削除しました",
|
"address_book_deleted": "エントリを削除しました",
|
||||||
"address_book_edit": "アドレスを編集",
|
"address_book_edit": "アドレスを編集",
|
||||||
"address_book_empty": "保存されたアドレスがありません。「新規追加」をクリックして追加してください。",
|
"address_book_empty": "保存されたアドレスがありません。「新規追加」をクリックして追加してください。",
|
||||||
@@ -53,8 +54,20 @@
|
|||||||
"amount_details": "金額の詳細",
|
"amount_details": "金額の詳細",
|
||||||
"amount_exceeds_balance": "金額が残高を超えています",
|
"amount_exceeds_balance": "金額が残高を超えています",
|
||||||
"amount_label": "金額:",
|
"amount_label": "金額:",
|
||||||
|
"animate_avatars": "アバターをアニメーション",
|
||||||
"appearance": "外観",
|
"appearance": "外観",
|
||||||
"auto_shield": "マイニング自動シールド",
|
"auto_shield": "マイニング自動シールド",
|
||||||
|
"av_intro": "マイニングソフトウェアは、望ましくない可能性があるものとしてフラグが立てられることがよくあります。プールマイニングを有効にするには、次の手順に従ってください。",
|
||||||
|
"av_open_security": "Windows セキュリティを開く",
|
||||||
|
"av_step1": "ステップ1: 除外の追加",
|
||||||
|
"av_step1_b1": "Windows セキュリティ > ウイルスと脅威の防止 を開く",
|
||||||
|
"av_step1_b2": "設定の管理 > 除外 > 追加または削除 をクリック",
|
||||||
|
"av_step1_b3": "フォルダーを追加: %APPDATA%\\ObsidianDragon\\",
|
||||||
|
"av_step2": "ステップ2: 検疫から復元(必要な場合)",
|
||||||
|
"av_step2_b1": "Windows セキュリティ > 保護の履歴",
|
||||||
|
"av_step2_b2": "xmrig.exe を見つけて「復元」をクリック",
|
||||||
|
"av_step3": "ステップ3: ウォレットを再起動して再試行",
|
||||||
|
"av_title": "Windows Defender がマイナーをブロックしました",
|
||||||
"available": "利用可能",
|
"available": "利用可能",
|
||||||
"backup_backing_up": "バックアップ中...",
|
"backup_backing_up": "バックアップ中...",
|
||||||
"backup_create": "バックアップを作成",
|
"backup_create": "バックアップを作成",
|
||||||
@@ -62,6 +75,7 @@
|
|||||||
"backup_data": "バックアップとデータ",
|
"backup_data": "バックアップとデータ",
|
||||||
"backup_description": "wallet.datファイルのバックアップを作成します。このファイルにはすべての秘密鍵と取引履歴が含まれています。バックアップは安全な場所に保管してください。",
|
"backup_description": "wallet.datファイルのバックアップを作成します。このファイルにはすべての秘密鍵と取引履歴が含まれています。バックアップは安全な場所に保管してください。",
|
||||||
"backup_destination": "バックアップ先:",
|
"backup_destination": "バックアップ先:",
|
||||||
|
"backup_overwrite_confirm": "そこには既にファイルがあります。もう一度保存すると上書きされます。",
|
||||||
"backup_source": "ソース:%s",
|
"backup_source": "ソース:%s",
|
||||||
"backup_tip_external": "外部ドライブまたはクラウドストレージにバックアップを保存",
|
"backup_tip_external": "外部ドライブまたはクラウドストレージにバックアップを保存",
|
||||||
"backup_tip_multiple": "異なる場所に複数のバックアップを作成",
|
"backup_tip_multiple": "異なる場所に複数のバックアップを作成",
|
||||||
@@ -70,6 +84,7 @@
|
|||||||
"backup_title": "ウォレットのバックアップ",
|
"backup_title": "ウォレットのバックアップ",
|
||||||
"backup_wallet": "ウォレットをバックアップ...",
|
"backup_wallet": "ウォレットをバックアップ...",
|
||||||
"backup_wallet_not_found": "警告:予想される場所にwallet.datが見つかりません",
|
"backup_wallet_not_found": "警告:予想される場所にwallet.datが見つかりません",
|
||||||
|
"backup_warn": "このファイルにはすべての秘密鍵が含まれています。安全な場所に保管してください。",
|
||||||
"balance": "残高",
|
"balance": "残高",
|
||||||
"balance_history_collecting": "残高履歴 — データを収集中...",
|
"balance_history_collecting": "残高履歴 — データを収集中...",
|
||||||
"balance_layout": "残高レイアウト",
|
"balance_layout": "残高レイアウト",
|
||||||
@@ -114,28 +129,105 @@
|
|||||||
"bootstrap_wallet_protected": "(wallet.dat は保護されています)",
|
"bootstrap_wallet_protected": "(wallet.dat は保護されています)",
|
||||||
"bootstrap_warning": "既存のブロックデータ(blocks、chainstate、notarizations)は削除され置き換えられます。wallet.dat は変更・削除されません。",
|
"bootstrap_warning": "既存のブロックデータ(blocks、chainstate、notarizations)は削除され置き換えられます。wallet.dat は変更・削除されません。",
|
||||||
"cancel": "キャンセル",
|
"cancel": "キャンセル",
|
||||||
|
"change_pass_confirm": "新しいパスフレーズ(確認):",
|
||||||
|
"change_pass_current": "現在のパスフレーズ:",
|
||||||
|
"change_pass_new": "新しいパスフレーズ:",
|
||||||
|
"change_pass_title": "パスフレーズを変更",
|
||||||
"characters": "文字",
|
"characters": "文字",
|
||||||
"chat": "チャット",
|
"chat": "チャット",
|
||||||
|
"chat_accent_amber": "琥珀",
|
||||||
|
"chat_accent_blue": "青",
|
||||||
|
"chat_accent_green": "緑",
|
||||||
|
"chat_accent_pink": "ピンク",
|
||||||
|
"chat_accent_purple": "紫",
|
||||||
|
"chat_accent_theme": "テーマ",
|
||||||
|
"chat_add_contact": "連絡先に追加",
|
||||||
|
"chat_awaiting_key": "返信待ち",
|
||||||
|
"chat_bubble_minimal": "ミニマル",
|
||||||
|
"chat_bubble_rounded": "角丸",
|
||||||
|
"chat_bubble_square": "角ばった",
|
||||||
|
"chat_buffer_loading": "チャットバッファ:…",
|
||||||
|
"chat_buffer_preparing": "チャットバッファ:%d/%d を準備中…",
|
||||||
|
"chat_buffer_ready": "チャットバッファ:%d/%d 準備完了",
|
||||||
|
"chat_buffer_sending": "チャット:%d 件のメッセージを送信中…",
|
||||||
|
"chat_buffer_sending_one": "チャット:%d 件のメッセージを送信中…",
|
||||||
"chat_cancel": "キャンセル",
|
"chat_cancel": "キャンセル",
|
||||||
|
"chat_contact_added": "連絡先を追加しました — 連絡先で名前を変更できます",
|
||||||
"chat_contact_request": "連絡リクエスト",
|
"chat_contact_request": "連絡リクエスト",
|
||||||
|
"chat_copy_address_tip": "クリックしてアドレスをコピー",
|
||||||
|
"chat_density_comfortable": "ゆったり",
|
||||||
|
"chat_density_compact": "コンパクト",
|
||||||
|
"chat_emoji_color": "カラー",
|
||||||
|
"chat_emoji_mono": "モノクロ",
|
||||||
|
"chat_emoji_search": "絵文字を検索",
|
||||||
"chat_empty_hint": "まだ会話はありません。受信したメッセージはここに表示されます。",
|
"chat_empty_hint": "まだ会話はありません。受信したメッセージはここに表示されます。",
|
||||||
|
"chat_empty_start": "「新しい会話」から始めましょう。",
|
||||||
|
"chat_empty_title": "会話はまだありません",
|
||||||
|
"chat_export": "チャットをエクスポート…",
|
||||||
|
"chat_export_done": "会話をエクスポートしました",
|
||||||
|
"chat_export_failed": "エクスポートファイルを書き込めませんでした。",
|
||||||
|
"chat_export_warn": "復号したメッセージを平文で保存します。ファイルは安全に保管してください。",
|
||||||
|
"chat_filter": "チャット",
|
||||||
|
"chat_hidden_toast": "会話を非表示にしました — 新しいメッセージが届くと再表示されます",
|
||||||
|
"chat_hide": "非表示",
|
||||||
|
"chat_hide_hidden": "非表示を隠す",
|
||||||
|
"chat_jump_latest": "最新",
|
||||||
|
"chat_len_over": "メッセージが長すぎます",
|
||||||
"chat_locked_hint": "チャットを読み込むにはウォレットのロックを解除してください。",
|
"chat_locked_hint": "チャットを読み込むにはウォレットのロックを解除してください。",
|
||||||
"chat_new_button": "新しい会話",
|
"chat_mute": "ミュート",
|
||||||
|
"chat_new_button": "新しいチャット",
|
||||||
"chat_new_message": "メッセージ",
|
"chat_new_message": "メッセージ",
|
||||||
|
"chat_new_message_toast": "新しい暗号化チャットメッセージ",
|
||||||
"chat_new_send": "リクエストを送信",
|
"chat_new_send": "リクエストを送信",
|
||||||
"chat_new_title": "新しい会話",
|
"chat_new_title": "新しいチャット",
|
||||||
"chat_new_zaddr": "宛先Zアドレス",
|
"chat_new_zaddr": "宛先Zアドレス",
|
||||||
|
"chat_no_matches": "検索に一致する会話がありません。",
|
||||||
|
"chat_no_z_contacts": "シールドアドレスの連絡先はまだありません",
|
||||||
|
"chat_opt_bubble_accent": "吹き出しの色",
|
||||||
|
"chat_opt_bubble_style": "吹き出しスタイル",
|
||||||
|
"chat_opt_density": "メッセージ密度",
|
||||||
|
"chat_opt_emoji": "絵文字スタイル",
|
||||||
|
"chat_opt_enter_sends": "Enterで送信",
|
||||||
|
"chat_opt_font_size": "文字サイズ",
|
||||||
|
"chat_opt_global_clock": "全体の時刻形式",
|
||||||
|
"chat_opt_poll": "取得間隔",
|
||||||
|
"chat_opt_timestamp": "タイムスタンプ",
|
||||||
|
"chat_pick_contact": "連絡先から選択…",
|
||||||
|
"chat_rename": "連絡先の名前を変更",
|
||||||
|
"chat_rename_hint": "連絡先名",
|
||||||
|
"chat_renamed": "連絡先の名前を変更しました",
|
||||||
|
"chat_retry": "再送信",
|
||||||
|
"chat_search": "会話を検索",
|
||||||
|
"chat_sec_appearance": "外観",
|
||||||
|
"chat_sec_messaging": "メッセージ",
|
||||||
"chat_select_hint": "表示する会話を選択してください。",
|
"chat_select_hint": "表示する会話を選択してください。",
|
||||||
"chat_send": "送信",
|
"chat_send": "送信",
|
||||||
"chat_send_failed": "未送信",
|
"chat_send_failed": "未送信",
|
||||||
|
"chat_sending": "送信中…",
|
||||||
|
"chat_settings_done": "完了",
|
||||||
|
"chat_settings_section": "チャットと連絡先",
|
||||||
|
"chat_settings_tip": "チャットのカスタマイズ",
|
||||||
|
"chat_settings_title": "チャット設定",
|
||||||
|
"chat_show_hidden": "非表示を表示",
|
||||||
|
"chat_time_now": "たった今",
|
||||||
"chat_toast_compose_failed": "メッセージを作成できませんでした(長すぎませんか?)。",
|
"chat_toast_compose_failed": "メッセージを作成できませんでした(長すぎませんか?)。",
|
||||||
"chat_toast_lite_busy": "すでに送信処理が進行中か、ウォレットが開かれていません。",
|
"chat_toast_lite_busy": "すでに送信処理が進行中か、ウォレットが開かれていません。",
|
||||||
|
"chat_toast_need_funds": "チャットを送信するには、手数料を賄うための少額のシールド残高が必要です。",
|
||||||
"chat_toast_no_zaddr": "送信元に使えるZアドレスがありません。",
|
"chat_toast_no_zaddr": "送信元に使えるZアドレスがありません。",
|
||||||
"chat_toast_not_connected": "未接続 — チャットメッセージは送信されませんでした。",
|
"chat_toast_not_connected": "未接続 — チャットメッセージは送信されませんでした。",
|
||||||
"chat_toast_request_compose_failed": "連絡リクエストを作成できませんでした(アドレスまたはテキストが無効?)。",
|
"chat_toast_request_compose_failed": "連絡リクエストを作成できませんでした(アドレスまたはテキストが無効?)。",
|
||||||
"chat_toast_request_queued": "連絡リクエストを送信待ちに追加しました。",
|
"chat_toast_request_queued": "連絡リクエストを送信待ちに追加しました。",
|
||||||
"chat_toast_waiting_reply": "メッセージを送るには、この相手からの返信を待つ必要があります。",
|
"chat_toast_waiting_reply": "メッセージを送るには、この相手からの返信を待つ必要があります。",
|
||||||
|
"chat_today": "今日",
|
||||||
|
"chat_ts_12h": "12時間",
|
||||||
|
"chat_ts_24h": "24時間",
|
||||||
|
"chat_ts_global": "全体設定に従う",
|
||||||
|
"chat_ts_global_short": "全体",
|
||||||
|
"chat_unhide": "再表示",
|
||||||
|
"chat_unmute": "ミュート解除",
|
||||||
|
"chat_verify_key": "識別鍵 — 照合して確認",
|
||||||
"chat_waiting_reply": "この相手からの返信を待っています — 返信があればメッセージを送れます。",
|
"chat_waiting_reply": "この相手からの返信を待っています — 返信があればメッセージを送れます。",
|
||||||
|
"chat_yesterday": "昨日",
|
||||||
"chat_you": "自分",
|
"chat_you": "自分",
|
||||||
"choose_icon": "アイコンを選択",
|
"choose_icon": "アイコンを選択",
|
||||||
"clear": "クリア",
|
"clear": "クリア",
|
||||||
@@ -147,6 +239,7 @@
|
|||||||
"click_copy_address": "クリックしてアドレスをコピー",
|
"click_copy_address": "クリックしてアドレスをコピー",
|
||||||
"click_copy_uri": "クリックしてURIをコピー",
|
"click_copy_uri": "クリックしてURIをコピー",
|
||||||
"click_to_copy": "クリックしてコピー",
|
"click_to_copy": "クリックしてコピー",
|
||||||
|
"clock_format": "時刻形式",
|
||||||
"close": "閉じる",
|
"close": "閉じる",
|
||||||
"conf_count": "%d 確認",
|
"conf_count": "%d 確認",
|
||||||
"confirm_and_send": "確認して送金",
|
"confirm_and_send": "確認して送金",
|
||||||
@@ -180,10 +273,24 @@
|
|||||||
"connected_peers": "接続中のピア",
|
"connected_peers": "接続中のピア",
|
||||||
"connecting": "接続中...",
|
"connecting": "接続中...",
|
||||||
"console": "コンソール",
|
"console": "コンソール",
|
||||||
|
"console_accents": "カラーアクセント",
|
||||||
"console_app": "アプリ",
|
"console_app": "アプリ",
|
||||||
"console_auto_scroll": "自動スクロール",
|
"console_auto_scroll": "自動スクロール",
|
||||||
"console_available_commands": "利用可能なコマンド:",
|
"console_available_commands": "利用可能なコマンド:",
|
||||||
|
"console_backend_reference": "バックエンドコマンドリファレンス",
|
||||||
|
"console_backend_unavailable": "バックエンドなし",
|
||||||
"console_capturing_output": "デーモン出力をキャプチャ中...",
|
"console_capturing_output": "デーモン出力をキャプチャ中...",
|
||||||
|
"console_cat_advanced": "詳細設定",
|
||||||
|
"console_cat_blockchain": "ブロックチェーン",
|
||||||
|
"console_cat_control": "制御",
|
||||||
|
"console_cat_keys": "鍵とセキュリティ",
|
||||||
|
"console_cat_mining": "マイニング",
|
||||||
|
"console_cat_network": "ネットワーク",
|
||||||
|
"console_cat_raw_transactions": "生トランザクション",
|
||||||
|
"console_cat_send": "送金",
|
||||||
|
"console_cat_sync": "同期",
|
||||||
|
"console_cat_utility": "ユーティリティ",
|
||||||
|
"console_cat_wallet": "ウォレット",
|
||||||
"console_clear": "クリア",
|
"console_clear": "クリア",
|
||||||
"console_clear_console": "コンソールをクリア",
|
"console_clear_console": "コンソールをクリア",
|
||||||
"console_cleared": "コンソールをクリアしました",
|
"console_cleared": "コンソールをクリアしました",
|
||||||
@@ -215,18 +322,37 @@
|
|||||||
"console_help_help": " help - このヘルプを表示",
|
"console_help_help": " help - このヘルプを表示",
|
||||||
"console_help_setgenerate": " setgenerate - マイニングを制御",
|
"console_help_setgenerate": " setgenerate - マイニングを制御",
|
||||||
"console_help_stop": " stop - デーモンを停止",
|
"console_help_stop": " stop - デーモンを停止",
|
||||||
|
"console_last_error": "最後のエラー:",
|
||||||
"console_line_count": "%zu 行",
|
"console_line_count": "%zu 行",
|
||||||
"console_matches": "件一致",
|
"console_matches": "件一致",
|
||||||
"console_new_lines": "%d 新しい行",
|
"console_new_lines": "%d 新しい行",
|
||||||
"console_no_daemon": "デーモンなし",
|
"console_no_daemon": "デーモンなし",
|
||||||
|
"console_no_output": "(出力なし)",
|
||||||
"console_not_connected": "エラー:デーモンに接続されていません",
|
"console_not_connected": "エラー:デーモンに接続されていません",
|
||||||
|
"console_not_connected_lite": "エラー:ウォレットが開かれていません",
|
||||||
"console_quit_note": "ここでは 'quit'/'exit' は不要です — ウィンドウを閉じるだけで構いません。",
|
"console_quit_note": "ここでは 'quit'/'exit' は不要です — ウィンドウを閉じるだけで構いません。",
|
||||||
|
"console_ref_builds": "生成",
|
||||||
|
"console_ref_cancel": "キャンセル",
|
||||||
|
"console_ref_destructive": "要注意",
|
||||||
|
"console_ref_example": "例",
|
||||||
|
"console_ref_insert": "コンソールに挿入",
|
||||||
|
"console_ref_insert_run": "挿入して実行",
|
||||||
|
"console_ref_no_match": "一致するコマンドはありません。",
|
||||||
|
"console_ref_no_params": "パラメーターは不要です。",
|
||||||
|
"console_ref_optional": "任意",
|
||||||
|
"console_ref_parameters": "パラメーター",
|
||||||
|
"console_ref_run": "実行",
|
||||||
|
"console_ref_run_confirm": "%s を今すぐ実行しますか? 影響の大きいコマンドです。",
|
||||||
|
"console_ref_search_hint": "名前または用途で検索…",
|
||||||
|
"console_ref_select_hint": "コマンドを選ぶと内容が表示されます。",
|
||||||
|
"console_ref_value": "値",
|
||||||
"console_rpc_reference": "RPCコマンドリファレンス",
|
"console_rpc_reference": "RPCコマンドリファレンス",
|
||||||
"console_rpc_trace": "RPC",
|
"console_rpc_trace": "RPC",
|
||||||
"console_scanline": "コンソールスキャンライン",
|
"console_scanline": "コンソールスキャンライン",
|
||||||
"console_search_commands": "コマンドを検索...",
|
"console_search_commands": "コマンドを検索...",
|
||||||
"console_select_all": "すべて選択",
|
"console_select_all": "すべて選択",
|
||||||
"console_show_app_output": "[app] ウォレットのログ行を表示",
|
"console_show_app_output": "[app] ウォレットのログ行を表示",
|
||||||
|
"console_show_backend_ref": "バックエンドコマンドリファレンスを表示",
|
||||||
"console_show_daemon_output": "デーモン出力を表示",
|
"console_show_daemon_output": "デーモン出力を表示",
|
||||||
"console_show_errors_only": "エラーのみ表示",
|
"console_show_errors_only": "エラーのみ表示",
|
||||||
"console_show_rpc_ref": "RPCコマンドリファレンスを表示",
|
"console_show_rpc_ref": "RPCコマンドリファレンスを表示",
|
||||||
@@ -239,17 +365,43 @@
|
|||||||
"console_status_stopped": "停止済み",
|
"console_status_stopped": "停止済み",
|
||||||
"console_status_stopping": "停止中",
|
"console_status_stopping": "停止中",
|
||||||
"console_status_unknown": "不明",
|
"console_status_unknown": "不明",
|
||||||
|
"console_stop_confirm_node": "'stop' はノードを停止し、ウォレットを切断します。確認するにはもう一度 'stop' と入力してください。",
|
||||||
"console_tab_completion": "Tabで補完",
|
"console_tab_completion": "Tabで補完",
|
||||||
|
"console_text_colors": "テキスト色",
|
||||||
|
"console_toggle_accents": "行のカラーアクセントを切り替え",
|
||||||
|
"console_toggle_text_color": "行のテキスト色を切り替え",
|
||||||
"console_type_help": "'help'と入力して利用可能なコマンドを表示",
|
"console_type_help": "'help'と入力して利用可能なコマンドを表示",
|
||||||
"console_welcome": "ObsidianDragonコンソールへようこそ",
|
"console_welcome": "ObsidianDragonコンソールへようこそ",
|
||||||
"console_zoom_in": "拡大",
|
"console_zoom_in": "拡大",
|
||||||
"console_zoom_out": "縮小",
|
"console_zoom_out": "縮小",
|
||||||
|
"contact_avatar": "アバター",
|
||||||
|
"contact_avatar_bad_image": "その画像を読み込めませんでした。",
|
||||||
|
"contact_avatar_badge": "バッジ",
|
||||||
|
"contact_avatar_badge_hint": "バッジはアドレスの種類に応じて自動的に選ばれます。",
|
||||||
|
"contact_avatar_choose": "画像を選択…",
|
||||||
|
"contact_avatar_copy_failed": "その画像をコピーできませんでした。",
|
||||||
|
"contact_avatar_icon": "アイコン",
|
||||||
|
"contact_avatar_image": "画像",
|
||||||
|
"contact_avatar_image_hint": "画像はアプリ内にコピーされ、元のファイルが移動しても利用できます。",
|
||||||
|
"contact_avatar_remove": "削除",
|
||||||
|
"contact_avatar_shielded": "シールド",
|
||||||
|
"contact_avatar_transparent": "透明",
|
||||||
"contact_global": "すべてのウォレットで表示(グローバル連絡先)",
|
"contact_global": "すべてのウォレットで表示(グローバル連絡先)",
|
||||||
"contact_global_badge_tt": "グローバル連絡先 — すべてのウォレットで表示",
|
"contact_global_badge_tt": "グローバル連絡先 — すべてのウォレットで表示",
|
||||||
"contact_global_tt": "オン:この連絡先はどのウォレットを読み込んでも表示されます。オフ:現在のウォレットにのみ属します。",
|
"contact_global_tt": "オン:この連絡先はどのウォレットを読み込んでも表示されます。オフ:現在のウォレットにのみ属します。",
|
||||||
|
"contact_preview_addr": "ここにアドレスが表示されます",
|
||||||
|
"contact_preview_name": "連絡先名",
|
||||||
|
"contact_wallet_loading": "ウォレットを読み込み中です。「すべてのウォレットに表示」にチェックするか、少し待ってから再試行してください。",
|
||||||
"contacts": "連絡先",
|
"contacts": "連絡先",
|
||||||
|
"contacts_avatar_shape": "アバターの形",
|
||||||
|
"contacts_list_scale": "リストの拡大率",
|
||||||
"contacts_search_no_match": "一致する連絡先がありません",
|
"contacts_search_no_match": "一致する連絡先がありません",
|
||||||
"contacts_search_placeholder": "連絡先を検索...",
|
"contacts_search_placeholder": "連絡先を検索...",
|
||||||
|
"contacts_settings_tip": "連絡先のカスタマイズ",
|
||||||
|
"contacts_settings_title": "連絡先設定",
|
||||||
|
"contacts_shape_circle": "円",
|
||||||
|
"contacts_shape_square": "四角",
|
||||||
|
"contacts_shape_tab": "左タブ",
|
||||||
"copied": "コピーしました!",
|
"copied": "コピーしました!",
|
||||||
"copy": "コピー",
|
"copy": "コピー",
|
||||||
"copy_address": "完全なアドレスをコピー",
|
"copy_address": "完全なアドレスをコピー",
|
||||||
@@ -302,6 +454,19 @@
|
|||||||
"date": "日付",
|
"date": "日付",
|
||||||
"date_label": "日付:",
|
"date_label": "日付:",
|
||||||
"debug_logging": "デバッグログ",
|
"debug_logging": "デバッグログ",
|
||||||
|
"decrypt_desc": "ウォレットがエクスポートされ、デーモンが新しい暗号化されていないウォレットで再起動し、すべての鍵が再インポートされます。ウォレットのサイズによっては数分かかる場合があります。",
|
||||||
|
"decrypt_error_title": "復号に失敗しました",
|
||||||
|
"decrypt_step_backup": "暗号化されたウォレットをバックアップしています",
|
||||||
|
"decrypt_step_export": "ウォレットの鍵をエクスポートしています",
|
||||||
|
"decrypt_step_restart": "デーモンを再起動しています",
|
||||||
|
"decrypt_step_stop": "デーモンを停止しています",
|
||||||
|
"decrypt_step_unlock": "ウォレットのロックを解除しています",
|
||||||
|
"decrypt_success_desc": "ウォレットは暗号化されていない状態になりました。暗号化されたウォレットのバックアップが、データディレクトリに wallet.dat.encrypted.bak として保存されました。",
|
||||||
|
"decrypt_success_title": "ウォレットの復号に成功しました!",
|
||||||
|
"decrypt_title": "ウォレットの暗号化を解除",
|
||||||
|
"decrypt_wait_general": "お待ちください。デーモンが鍵をエクスポートし、再起動して再インポートしています。数分かかる場合があります。",
|
||||||
|
"decrypt_wait_restart": "デーモンの起動が完了するのを待っています...",
|
||||||
|
"decrypt_warning": "ウォレットの暗号化が解除されます。秘密鍵は保護されずにディスクに保存されます。",
|
||||||
"delete": "削除",
|
"delete": "削除",
|
||||||
"delete_blockchain": "ブロックチェーンを削除",
|
"delete_blockchain": "ブロックチェーンを削除",
|
||||||
"delete_blockchain_confirm": "削除して再同期",
|
"delete_blockchain_confirm": "削除して再同期",
|
||||||
@@ -314,6 +479,15 @@
|
|||||||
"download_bootstrap": "ブートストラップをダウンロード",
|
"download_bootstrap": "ブートストラップをダウンロード",
|
||||||
"dragonx_green": "DragonX(グリーン)",
|
"dragonx_green": "DragonX(グリーン)",
|
||||||
"edit": "編集",
|
"edit": "編集",
|
||||||
|
"enc_confirm": "確認:",
|
||||||
|
"enc_desc": "ウォレットを暗号化すると、パスフレーズで秘密鍵が保護されます。暗号化後、デーモンが再起動します。",
|
||||||
|
"enc_encrypting": "ウォレットを暗号化しています...",
|
||||||
|
"enc_pin_desc": "4〜8桁のPINを使うと、毎回パスフレーズ全体を入力せずにウォレットのロックを解除できます。",
|
||||||
|
"enc_pin_set_ok": "PINを設定しました",
|
||||||
|
"enc_pin_skipped": "PINをスキップしました。後で設定で設定できます。",
|
||||||
|
"enc_pin_vault_fail": "PINボルトの作成に失敗しました",
|
||||||
|
"enc_success": "ウォレットの暗号化に成功しました!",
|
||||||
|
"enc_wait": "お待ちください。アプリケーションを閉じないでください。",
|
||||||
"error": "エラー",
|
"error": "エラー",
|
||||||
"error_format": "エラー:%s",
|
"error_format": "エラー:%s",
|
||||||
"est_time_to_block": "予測ブロック時間",
|
"est_time_to_block": "予測ブロック時間",
|
||||||
@@ -333,23 +507,34 @@
|
|||||||
"explorer_mempool": "メモリプール",
|
"explorer_mempool": "メモリプール",
|
||||||
"explorer_mempool_size": "サイズ",
|
"explorer_mempool_size": "サイズ",
|
||||||
"explorer_mempool_txs": "トランザクション",
|
"explorer_mempool_txs": "トランザクション",
|
||||||
|
"explorer_next_block": "次のブロック",
|
||||||
"explorer_no_results": "一致するキャッシュ済みブロックがありません",
|
"explorer_no_results": "一致するキャッシュ済みブロックがありません",
|
||||||
"explorer_not_connected": "デーモンに未接続 — ブロックまたはトランザクションハッシュを検索できません",
|
"explorer_not_connected": "デーモンに未接続 — ブロックまたはトランザクションハッシュを検索できません",
|
||||||
|
"explorer_prev_block": "前のブロック",
|
||||||
"explorer_recent_blocks": "最近のブロック",
|
"explorer_recent_blocks": "最近のブロック",
|
||||||
"explorer_search": "検索",
|
"explorer_search": "検索",
|
||||||
"explorer_section": "エクスプローラー",
|
"explorer_section": "エクスプローラー",
|
||||||
"explorer_tx_outputs": "出力",
|
"explorer_tx_outputs": "出力",
|
||||||
"explorer_tx_size": "サイズ",
|
"explorer_tx_size": "サイズ",
|
||||||
|
"explorer_txid": "TxID:",
|
||||||
"export": "エクスポート",
|
"export": "エクスポート",
|
||||||
"export_csv": "CSVエクスポート",
|
"export_csv": "CSVエクスポート",
|
||||||
"export_keys_btn": "鍵をエクスポート",
|
"export_keys_btn": "鍵をエクスポート",
|
||||||
"export_keys_danger": "危険:ウォレットからすべての秘密鍵がエクスポートされます!このファイルにアクセスできる人は誰でもあなたの資金を盗めます。安全に保管し、使用後は削除してください。",
|
"export_keys_danger": "危険:ウォレットからすべての秘密鍵がエクスポートされます!このファイルにアクセスできる人は誰でもあなたの資金を盗めます。安全に保管し、使用後は削除してください。",
|
||||||
"export_keys_include_t": "Tアドレスを含める(透明)",
|
"export_keys_include_t": "Tアドレスを含める(透明)",
|
||||||
"export_keys_include_z": "Zアドレスを含める(シールド)",
|
"export_keys_include_z": "Zアドレスを含める(シールド)",
|
||||||
|
"export_keys_none_addrs": "エクスポートするアドレスがありません",
|
||||||
|
"export_keys_none_result": "鍵をエクスポートできませんでした(%d 件中 0 件)。ウォレットのロックを解除して再試行してください。",
|
||||||
|
"export_keys_none_toast": "鍵をエクスポートできませんでした。ウォレットはロック解除されていますか?",
|
||||||
|
"export_keys_not_connected": "デーモンに接続されていません",
|
||||||
"export_keys_options": "エクスポートオプション:",
|
"export_keys_options": "エクスポートオプション:",
|
||||||
|
"export_keys_partial": "%d 件中 %d 件の鍵をエクスポート — 不完全(支払い鍵がない、またはウォレットがロックされています)。",
|
||||||
|
"export_keys_partial_toast": "部分エクスポート: %d 件中 %d 件",
|
||||||
"export_keys_progress": "エクスポート中 %d/%d...",
|
"export_keys_progress": "エクスポート中 %d/%d...",
|
||||||
|
"export_keys_select_type": "アドレスの種類を少なくとも1つ選択してください",
|
||||||
"export_keys_success": "鍵のエクスポートに成功しました",
|
"export_keys_success": "鍵のエクスポートに成功しました",
|
||||||
"export_keys_title": "すべての秘密鍵をエクスポート",
|
"export_keys_title": "すべての秘密鍵をエクスポート",
|
||||||
|
"export_keys_write_fail": "鍵ファイルの書き込みに失敗しました。",
|
||||||
"export_private_key": "秘密鍵をエクスポート",
|
"export_private_key": "秘密鍵をエクスポート",
|
||||||
"export_tx_count": "%zu件の取引をCSVファイルにエクスポート。",
|
"export_tx_count": "%zu件の取引をCSVファイルにエクスポート。",
|
||||||
"export_tx_file_fail": "CSVファイルの作成に失敗しました",
|
"export_tx_file_fail": "CSVファイルの作成に失敗しました",
|
||||||
@@ -389,23 +574,50 @@
|
|||||||
"hide_qr": "QRを非表示",
|
"hide_qr": "QRを非表示",
|
||||||
"hide_zero_balances": "残高0を非表示",
|
"hide_zero_balances": "残高0を非表示",
|
||||||
"history": "履歴",
|
"history": "履歴",
|
||||||
|
"img_picker_count": "このフォルダの画像:%d",
|
||||||
|
"img_picker_empty": "このフォルダにはサブフォルダも画像もありません。",
|
||||||
|
"img_picker_none": "このフォルダに画像はありません",
|
||||||
|
"img_picker_pictures": "画像フォルダ",
|
||||||
|
"img_picker_title": "画像を選択",
|
||||||
|
"img_picker_use": "この画像を使用",
|
||||||
"immature_type": "未成熟",
|
"immature_type": "未成熟",
|
||||||
"import": "インポート",
|
"import": "インポート",
|
||||||
|
"import_key_address": "アドレス:",
|
||||||
"import_key_btn": "鍵をインポート",
|
"import_key_btn": "鍵をインポート",
|
||||||
|
"import_key_done": "インポートしました。ウォレットを再スキャンしています。",
|
||||||
|
"import_key_field": "鍵",
|
||||||
"import_key_formats": "サポートされる鍵形式:",
|
"import_key_formats": "サポートされる鍵形式:",
|
||||||
"import_key_full_rescan": "(0 = 完全再スキャン)",
|
"import_key_full_rescan": "(0 = 完全再スキャン)",
|
||||||
|
"import_key_import": "インポート",
|
||||||
"import_key_label": "秘密鍵:",
|
"import_key_label": "秘密鍵:",
|
||||||
|
"import_key_need_node": "鍵をインポートするには稼働中のノードに接続してください。",
|
||||||
"import_key_no_valid": "入力に有効な鍵が見つかりません",
|
"import_key_no_valid": "入力に有効な鍵が見つかりません",
|
||||||
"import_key_progress": "インポート中 %d/%d...",
|
"import_key_progress": "インポート中 %d/%d...",
|
||||||
"import_key_rescan": "インポート後にブロックチェーンを再スキャン",
|
"import_key_rescan": "インポート後にブロックチェーンを再スキャン",
|
||||||
|
"import_key_rescanning": "インポートと再スキャン中 — 数分かかることがあります",
|
||||||
|
"import_key_reveal_tip": "鍵の表示/非表示",
|
||||||
"import_key_start_height": "開始高:",
|
"import_key_start_height": "開始高:",
|
||||||
"import_key_success": "鍵のインポートに成功しました",
|
"import_key_success": "鍵のインポートに成功しました",
|
||||||
"import_key_t_format": "TアドレスWIF秘密鍵",
|
"import_key_t_format": "TアドレスWIF秘密鍵",
|
||||||
"import_key_title": "秘密鍵をインポート",
|
"import_key_title": "秘密鍵をインポート",
|
||||||
"import_key_tooltip": "1行に1つずつ秘密鍵を入力してください。\nzアドレスとtアドレスの鍵の両方に対応しています。\n#で始まる行はコメントとして扱われます。",
|
"import_key_tooltip": "1行に1つずつ秘密鍵を入力してください。\nzアドレスとtアドレスの鍵の両方に対応しています。\n#で始まる行はコメントとして扱われます。",
|
||||||
|
"import_key_type_tkey": "透明な秘密鍵",
|
||||||
|
"import_key_type_unknown": "認識できない鍵の形式",
|
||||||
|
"import_key_type_zspend": "シールド支払鍵",
|
||||||
|
"import_key_type_zview": "シールド閲覧鍵(監視のみ)",
|
||||||
|
"import_key_warn": "自分が所有する鍵のみをインポートしてください — その資金へのアクセスを許可します。",
|
||||||
"import_key_warning": "警告:秘密鍵を決して共有しないでください!信頼できないソースからの鍵のインポートはウォレットを危険にさらす可能性があります。",
|
"import_key_warning": "警告:秘密鍵を決して共有しないでください!信頼できないソースからの鍵のインポートはウォレットを危険にさらす可能性があります。",
|
||||||
|
"import_key_wrong_type": "これは閲覧鍵のようです。代わりに「閲覧鍵をインポート」を使用してください。",
|
||||||
"import_key_z_format": "Zアドレス支出鍵 (secret-extended-key-...)",
|
"import_key_z_format": "Zアドレス支出鍵 (secret-extended-key-...)",
|
||||||
"import_private_key": "秘密鍵をインポート...",
|
"import_private_key": "秘密鍵をインポート...",
|
||||||
|
"import_scan_hint": "0 = 最初から再スキャン",
|
||||||
|
"import_scan_label": "スキャン開始ブロック高(任意)",
|
||||||
|
"import_scan_tip": "現在の高さ",
|
||||||
|
"import_scan_transparent": "透明鍵は常に全体を再スキャンします",
|
||||||
|
"import_viewkey_field": "閲覧鍵",
|
||||||
|
"import_viewkey_note": "監視のみ:閲覧鍵はアドレスの残高と取引を表示できますが、資金を送金することはできません。",
|
||||||
|
"import_viewkey_title": "閲覧鍵をインポート",
|
||||||
|
"import_viewkey_wrong_type": "これは支払い鍵のようです。代わりに「秘密鍵をインポート」を使用してください。",
|
||||||
"incorrect_passphrase": "パスフレーズが正しくありません",
|
"incorrect_passphrase": "パスフレーズが正しくありません",
|
||||||
"incorrect_pin": "PINが正しくありません",
|
"incorrect_pin": "PINが正しくありません",
|
||||||
"insufficient_funds": "この金額と手数料に対して残高が不足しています。",
|
"insufficient_funds": "この金額と手数料に対して残高が不足しています。",
|
||||||
@@ -414,6 +626,7 @@
|
|||||||
"keep": "保持",
|
"keep": "保持",
|
||||||
"keep_daemon": "デーモンを実行し続ける",
|
"keep_daemon": "デーモンを実行し続ける",
|
||||||
"key_export_click_retrieve": "クリックしてウォレットからキーを取得",
|
"key_export_click_retrieve": "クリックしてウォレットからキーを取得",
|
||||||
|
"key_export_failed": "鍵をエクスポートできませんでした。ウォレットのロックを解除して(暗号化されている場合)再試行してください。",
|
||||||
"key_export_fetching": "ウォレットから鍵を取得中...",
|
"key_export_fetching": "ウォレットから鍵を取得中...",
|
||||||
"key_export_private_key": "秘密鍵:",
|
"key_export_private_key": "秘密鍵:",
|
||||||
"key_export_private_warning": "この鍵は秘密にしてください!この鍵を持つ人は誰でもあなたの資金を使えます。オンラインや信頼できない相手と共有しないでください。",
|
"key_export_private_warning": "この鍵は秘密にしてください!この鍵を持つ人は誰でもあなたの資金を使えます。オンラインや信頼できない相手と共有しないでください。",
|
||||||
@@ -431,6 +644,7 @@
|
|||||||
"lite_birthday_backup": "誕生日:%llu (これもバックアップしてください)",
|
"lite_birthday_backup": "誕生日:%llu (これもバックアップしてください)",
|
||||||
"lite_birthday_hint": "スキャンを開始するブロック高。不明な場合は0のままにしてください(完全スキャンが遅くなります)。",
|
"lite_birthday_hint": "スキャンを開始するブロック高。不明な場合は0のままにしてください(完全スキャンが遅くなります)。",
|
||||||
"lite_birthday_label": "バースデー",
|
"lite_birthday_label": "バースデー",
|
||||||
|
"lite_console_backend_commands": "バックエンドコマンド:",
|
||||||
"lite_console_help_passthrough": "その他の入力はライトウォレットのコンソールコマンドとして実行されます。",
|
"lite_console_help_passthrough": "その他の入力はライトウォレットのコンソールコマンドとして実行されます。",
|
||||||
"lite_copy": "コピー",
|
"lite_copy": "コピー",
|
||||||
"lite_could_not_write": "書き込めませんでした ",
|
"lite_could_not_write": "書き込めませんでした ",
|
||||||
@@ -448,6 +662,7 @@
|
|||||||
"lite_net_add_url_hint": "https://your-lite-server",
|
"lite_net_add_url_hint": "https://your-lite-server",
|
||||||
"lite_net_checking": "確認中…",
|
"lite_net_checking": "確認中…",
|
||||||
"lite_net_connected": "接続済み",
|
"lite_net_connected": "接続済み",
|
||||||
|
"lite_net_connecting": "接続中…",
|
||||||
"lite_net_custom": "カスタム",
|
"lite_net_custom": "カスタム",
|
||||||
"lite_net_disconnected": "未接続",
|
"lite_net_disconnected": "未接続",
|
||||||
"lite_net_hidden_section": "非表示のサーバー",
|
"lite_net_hidden_section": "非表示のサーバー",
|
||||||
@@ -534,6 +749,10 @@
|
|||||||
"market_btc_price": "BTC価格",
|
"market_btc_price": "BTC価格",
|
||||||
"market_cap": "時価総額",
|
"market_cap": "時価総額",
|
||||||
"market_cap_short": "時価総額",
|
"market_cap_short": "時価総額",
|
||||||
|
"market_chart_loading": "価格履歴を読み込み中",
|
||||||
|
"market_col_name": "名前",
|
||||||
|
"market_col_trend": "トレンド",
|
||||||
|
"market_col_value": "価値",
|
||||||
"market_iv_1d": "1日",
|
"market_iv_1d": "1日",
|
||||||
"market_iv_1h": "1時間",
|
"market_iv_1h": "1時間",
|
||||||
"market_iv_1m": "1ヶ月",
|
"market_iv_1m": "1ヶ月",
|
||||||
@@ -542,11 +761,18 @@
|
|||||||
"market_no_history": "価格履歴がありません",
|
"market_no_history": "価格履歴がありません",
|
||||||
"market_no_price": "価格データなし",
|
"market_no_price": "価格データなし",
|
||||||
"market_now": "現在",
|
"market_now": "現在",
|
||||||
|
"market_opt_chart_style": "チャートスタイル",
|
||||||
"market_pct_shielded": "%.0f%% シールド済み",
|
"market_pct_shielded": "%.0f%% シールド済み",
|
||||||
"market_portfolio": "ポートフォリオ",
|
"market_portfolio": "ポートフォリオ",
|
||||||
"market_price_loading": "価格データを読み込み中...",
|
"market_price_loading": "価格データを読み込み中...",
|
||||||
"market_price_unavailable": "価格データが利用できません",
|
"market_price_unavailable": "価格データが利用できません",
|
||||||
"market_refresh_price": "価格データを更新",
|
"market_refresh_price": "価格データを更新",
|
||||||
|
"market_settings_tip": "マーケットオプション",
|
||||||
|
"market_settings_title": "マーケット設定",
|
||||||
|
"market_style_candle": "ローソク足に切り替え",
|
||||||
|
"market_style_candle_label": "ローソク足",
|
||||||
|
"market_style_line": "折れ線チャートに切り替え",
|
||||||
|
"market_style_line_label": "ライン",
|
||||||
"market_trade_on": "%s で取引",
|
"market_trade_on": "%s で取引",
|
||||||
"market_updated": "\\xc2\\xb7 更新: %s",
|
"market_updated": "\\xc2\\xb7 更新: %s",
|
||||||
"market_vol_short": "出来高",
|
"market_vol_short": "出来高",
|
||||||
@@ -736,6 +962,7 @@
|
|||||||
"output_filename": "出力ファイル名:",
|
"output_filename": "出力ファイル名:",
|
||||||
"overview": "概要",
|
"overview": "概要",
|
||||||
"paste": "貼り付け",
|
"paste": "貼り付け",
|
||||||
|
"paste_clip_empty": "クリップボードは空です",
|
||||||
"paste_from_clipboard": "クリップボードから貼り付け",
|
"paste_from_clipboard": "クリップボードから貼り付け",
|
||||||
"pay_from": "支払い元",
|
"pay_from": "支払い元",
|
||||||
"payment_request": "支払い請求",
|
"payment_request": "支払い請求",
|
||||||
@@ -787,7 +1014,14 @@
|
|||||||
"peers_upper": "ピア",
|
"peers_upper": "ピア",
|
||||||
"peers_version": "バージョン",
|
"peers_version": "バージョン",
|
||||||
"pending": "保留中",
|
"pending": "保留中",
|
||||||
|
"pin_change_desc": "ロック解除PINを変更します。現在のPINと新しいPINが必要です。",
|
||||||
|
"pin_confirm_new_label": "新しいPINの確認:",
|
||||||
|
"pin_current_label": "現在のPIN:",
|
||||||
|
"pin_new_label": "新しいPIN(4〜8桁):",
|
||||||
"pin_not_set": "PINが設定されていません。パスフレーズで解除してください。",
|
"pin_not_set": "PINが設定されていません。パスフレーズで解除してください。",
|
||||||
|
"pin_remove_desc": "削除を確認するには現在のPINを入力してください。ロック解除には完全なパスフレーズが必要になります。",
|
||||||
|
"pin_setup_desc": "ウォレットをすばやくロック解除するための4〜8桁のPINを設定します。ウォレットのパスフレーズはこのPINで暗号化され、ローカルに保存されます。",
|
||||||
|
"pin_wallet_passphrase": "ウォレットのパスフレーズ:",
|
||||||
"ping": "Ping",
|
"ping": "Ping",
|
||||||
"portfolio_add_entry": "エントリを追加",
|
"portfolio_add_entry": "エントリを追加",
|
||||||
"portfolio_add_to": "ポートフォリオに追加",
|
"portfolio_add_to": "ポートフォリオに追加",
|
||||||
@@ -826,6 +1060,9 @@
|
|||||||
"portfolio_remove_from": "ポートフォリオから削除",
|
"portfolio_remove_from": "ポートフォリオから削除",
|
||||||
"portfolio_revert": "元に戻す",
|
"portfolio_revert": "元に戻す",
|
||||||
"portfolio_save": "保存",
|
"portfolio_save": "保存",
|
||||||
|
"portfolio_save_need_address": "保存するにはアドレスを1つ以上追加してください。",
|
||||||
|
"portfolio_save_need_name": "このグループを保存するには名前を入力してください。",
|
||||||
|
"portfolio_save_need_price": "保存するには0より大きい手動価格を入力してください。",
|
||||||
"portfolio_search": "アドレスを検索…",
|
"portfolio_search": "アドレスを検索…",
|
||||||
"portfolio_search_icons": "アイコンを検索…",
|
"portfolio_search_icons": "アイコンを検索…",
|
||||||
"portfolio_select_all": "すべて",
|
"portfolio_select_all": "すべて",
|
||||||
@@ -839,11 +1076,12 @@
|
|||||||
"portfolio_spark_min": "分",
|
"portfolio_spark_min": "分",
|
||||||
"portfolio_spark_month": "月",
|
"portfolio_spark_month": "月",
|
||||||
"portfolio_spark_week": "週",
|
"portfolio_spark_week": "週",
|
||||||
"portfolio_style_compact": "コンパクト行",
|
"portfolio_style_compact": "テーブル",
|
||||||
"portfolio_style_detailed": "詳細行",
|
"portfolio_style_detailed": "カード",
|
||||||
"portfolio_style_featured": "注目行",
|
"portfolio_style_featured": "スポットライト",
|
||||||
"portfolio_style_label": "ポートフォリオスタイル",
|
"portfolio_style_label": "ポートフォリオスタイル",
|
||||||
"portfolio_untitled": "無題",
|
"portfolio_untitled": "無題",
|
||||||
|
"portfolio_wallet_loading": "ウォレットの読み込みが終わってからグループを追加してください。",
|
||||||
"price_chart": "価格チャート",
|
"price_chart": "価格チャート",
|
||||||
"privacy_great": "優れたプライバシーです!",
|
"privacy_great": "優れたプライバシーです!",
|
||||||
"privacy_low": "プライバシーが低い — 資金をシールドしてください",
|
"privacy_low": "プライバシーが低い — 資金をシールドしてください",
|
||||||
@@ -973,6 +1211,7 @@
|
|||||||
"send_amount": "金額",
|
"send_amount": "金額",
|
||||||
"send_amount_details": "金額の詳細",
|
"send_amount_details": "金額の詳細",
|
||||||
"send_amount_upper": "金額",
|
"send_amount_upper": "金額",
|
||||||
|
"send_cannot_send_now": "現在送信できません — 接続、同期、利用可能な残高を確認してください。",
|
||||||
"send_clear_fields": "すべてのフォームフィールドをクリアしますか?",
|
"send_clear_fields": "すべてのフォームフィールドをクリアしますか?",
|
||||||
"send_contacts_button": "連絡先から選択",
|
"send_contacts_button": "連絡先から選択",
|
||||||
"send_copy_error": "エラーをコピー",
|
"send_copy_error": "エラーをコピー",
|
||||||
@@ -1061,7 +1300,8 @@
|
|||||||
"settings_gradient_bg": "グラデーション背景",
|
"settings_gradient_bg": "グラデーション背景",
|
||||||
"settings_gradient_desc": "テクスチャ背景を滑らかなグラデーションに置換",
|
"settings_gradient_desc": "テクスチャ背景を滑らかなグラデーションに置換",
|
||||||
"settings_idle_after": "経過後",
|
"settings_idle_after": "経過後",
|
||||||
"settings_import_key": "鍵をインポート...",
|
"settings_import_key": "秘密鍵をインポート...",
|
||||||
|
"settings_import_viewkey": "閲覧鍵をインポート...",
|
||||||
"settings_language_note": "注意:一部のテキストは更新に再起動が必要です",
|
"settings_language_note": "注意:一部のテキストは更新に再起動が必要です",
|
||||||
"settings_lock_now": "今すぐロック",
|
"settings_lock_now": "今すぐロック",
|
||||||
"settings_locked": "ロック済み",
|
"settings_locked": "ロック済み",
|
||||||
@@ -1110,6 +1350,7 @@
|
|||||||
"shield_in_progress": "操作進行中...",
|
"shield_in_progress": "操作進行中...",
|
||||||
"shield_max_utxos": "1回の操作あたりの最大UTXO数",
|
"shield_max_utxos": "1回の操作あたりの最大UTXO数",
|
||||||
"shield_merge_done": "シールド/統合が完了しました!",
|
"shield_merge_done": "シールド/統合が完了しました!",
|
||||||
|
"shield_no_zaddr_hint": "シールドアドレス(z)がまだありません — まず「受取」タブで作成してください。",
|
||||||
"shield_op_failed": "操作に失敗しました: ",
|
"shield_op_failed": "操作に失敗しました: ",
|
||||||
"shield_op_submitted": "操作を送信しました: ",
|
"shield_op_submitted": "操作を送信しました: ",
|
||||||
"shield_operation_id": "オペレーション ID:%s",
|
"shield_operation_id": "オペレーション ID:%s",
|
||||||
@@ -1148,6 +1389,31 @@
|
|||||||
"submitting_transaction": "取引を送信中...",
|
"submitting_transaction": "取引を送信中...",
|
||||||
"success": "成功",
|
"success": "成功",
|
||||||
"summary": "概要",
|
"summary": "概要",
|
||||||
|
"sweep_button": "集約",
|
||||||
|
"sweep_caveat": "鍵をインポートして、すべての資金をあなたのアドレスへ移す取引に署名します。鍵は残高ゼロでウォレットに残ります。",
|
||||||
|
"sweep_dest_label": "集約先",
|
||||||
|
"sweep_dest_new": "新しいシールドアドレス(推奨)",
|
||||||
|
"sweep_done": "完了 — 資金をあなたのアドレスに集約しました。",
|
||||||
|
"sweep_to": "集約先:",
|
||||||
|
"sweep_toggle": "ウォレットに集約(鍵は保持しない)",
|
||||||
|
"sweep_tx": "取引:",
|
||||||
|
"switch_corrupt_body": "このウォレットは破損しているようです。ノードが開けませんでした。バックアップから復元するか、作り直すか、修復を試してください。",
|
||||||
|
"switch_corrupt_repair": "修復を試す(salvage)",
|
||||||
|
"switch_progress_background": "バックグラウンドで続行",
|
||||||
|
"switch_progress_default_wallet": "既定のウォレット",
|
||||||
|
"switch_progress_elapsed": "経過",
|
||||||
|
"switch_progress_external_wallet": "外部ウォレット",
|
||||||
|
"switch_progress_failed_title": "ウォレットの切り替えに失敗しました",
|
||||||
|
"switch_progress_from_label": "元:",
|
||||||
|
"switch_progress_hint": "正常なシャットダウンには最大1分かかることがあります。",
|
||||||
|
"switch_progress_reconnecting": "再接続しています",
|
||||||
|
"switch_progress_starting": "新しいウォレットでノードを起動しています",
|
||||||
|
"switch_progress_stopping": "現在のノードを停止しています",
|
||||||
|
"switch_progress_title": "ウォレットを切り替え中",
|
||||||
|
"switch_stopnode_body": "ウォレットを切り替えると、選択したウォレットでノードが再起動します。実行中のノードは停止され、新しいウォレットで再起動されます。意図的に起動したままにしていた場合は、自動的に復帰します。",
|
||||||
|
"switch_stopnode_confirm": "ノードを停止して切り替え",
|
||||||
|
"switch_stopnode_title": "実行中のノードを停止しますか?",
|
||||||
|
"switch_stopnode_warn": "このウォレットが起動していないノードがすでに実行中です。",
|
||||||
"syncing": "同期中...",
|
"syncing": "同期中...",
|
||||||
"t_address": "Tアドレス",
|
"t_address": "Tアドレス",
|
||||||
"t_addresses": "Tアドレス",
|
"t_addresses": "Tアドレス",
|
||||||
@@ -1183,8 +1449,10 @@
|
|||||||
"transfer_to": "送金先:",
|
"transfer_to": "送金先:",
|
||||||
"transparent": "透明",
|
"transparent": "透明",
|
||||||
"transparent_address": "トランスペアレントアドレス",
|
"transparent_address": "トランスペアレントアドレス",
|
||||||
|
"try_again": "再試行",
|
||||||
"tt_addr_url": "ブロックエクスプローラーでアドレスを表示するためのベース URL",
|
"tt_addr_url": "ブロックエクスプローラーでアドレスを表示するためのベース URL",
|
||||||
"tt_address_book": "クイック送信用の保存済みアドレスを管理",
|
"tt_address_book": "クイック送信用の保存済みアドレスを管理",
|
||||||
|
"tt_animate_avatars": "アニメーション連絡先アバター(GIF / WebP)を再生。オフでは最初のフレームのみ表示",
|
||||||
"tt_auto_lock": "この無操作時間後にウォレットをロック",
|
"tt_auto_lock": "この無操作時間後にウォレットをロック",
|
||||||
"tt_auto_shield": "プライバシーのため透明残高を自動的にシールドアドレスに移動",
|
"tt_auto_shield": "プライバシーのため透明残高を自動的にシールドアドレスに移動",
|
||||||
"tt_backup": "wallet.dat のバックアップを作成",
|
"tt_backup": "wallet.dat のバックアップを作成",
|
||||||
@@ -1192,10 +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_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": "デバッグログオプションを展開",
|
||||||
@@ -1209,10 +1487,34 @@
|
|||||||
"tt_font_scale": "すべてのテキストと UI をスケーリング(1.0x = デフォルト、最大 1.5x)。",
|
"tt_font_scale": "すべてのテキストと UI をスケーリング(1.0x = デフォルト、最大 1.5x)。",
|
||||||
"tt_idle_delay": "マイニング開始前の待機時間",
|
"tt_idle_delay": "マイニング開始前の待機時間",
|
||||||
"tt_import_key": "このウォレットに秘密鍵(zkey または tkey)をインポート",
|
"tt_import_key": "このウォレットに秘密鍵(zkey または tkey)をインポート",
|
||||||
|
"tt_import_viewkey": "シールド閲覧鍵をインポートしてアドレスを閲覧(読み取り専用)",
|
||||||
"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 を一つのアドレスに統合",
|
||||||
@@ -1233,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": "透明マイニング報酬をシールドアドレスに移動",
|
||||||
@@ -1257,6 +1564,7 @@
|
|||||||
"tt_website": "DragonX ウェブサイトを開く",
|
"tt_website": "DragonX ウェブサイトを開く",
|
||||||
"tt_window_opacity": "背景の不透明度(低い = デスクトップがウィンドウ越しに見える)",
|
"tt_window_opacity": "背景の不透明度(低い = デスクトップがウィンドウ越しに見える)",
|
||||||
"tt_wizard": "初期セットアップウィザードを再実行\\nデーモンは再起動されます",
|
"tt_wizard": "初期セットアップウィザードを再実行\\nデーモンは再起動されます",
|
||||||
|
"tx_chat_badge": "メッセージ",
|
||||||
"tx_confirmations": "%d 確認",
|
"tx_confirmations": "%d 確認",
|
||||||
"tx_details_title": "取引の詳細",
|
"tx_details_title": "取引の詳細",
|
||||||
"tx_from_address": "送信元アドレス:",
|
"tx_from_address": "送信元アドレス:",
|
||||||
@@ -1325,6 +1633,8 @@
|
|||||||
"wallets_add_folder_toggle": "+ 別のフォルダーをウォレット検索…",
|
"wallets_add_folder_toggle": "+ 別のフォルダーをウォレット検索…",
|
||||||
"wallets_badge_encrypted": "暗号化済み(パスフレーズ保護)",
|
"wallets_badge_encrypted": "暗号化済み(パスフレーズ保護)",
|
||||||
"wallets_badge_encrypted_short": "暗号化",
|
"wallets_badge_encrypted_short": "暗号化",
|
||||||
|
"wallets_badge_hd": "HDウォレット — 開かないとシードフレーズを確認できません",
|
||||||
|
"wallets_badge_hd_short": "HDウォレット",
|
||||||
"wallets_badge_legacy": "レガシーウォレット(シードフレーズなし)",
|
"wallets_badge_legacy": "レガシーウォレット(シードフレーズなし)",
|
||||||
"wallets_badge_legacy_short": "レガシー",
|
"wallets_badge_legacy_short": "レガシー",
|
||||||
"wallets_badge_seed": "シードフレーズウォレット (HD)",
|
"wallets_badge_seed": "シードフレーズウォレット (HD)",
|
||||||
@@ -1343,6 +1653,7 @@
|
|||||||
"wallets_created": "作成",
|
"wallets_created": "作成",
|
||||||
"wallets_creating": "ウォレットを作成中 — ノードが再起動します…",
|
"wallets_creating": "ウォレットを作成中 — ノードが再起動します…",
|
||||||
"wallets_current": "現在",
|
"wallets_current": "現在",
|
||||||
|
"wallets_empty_hint": "フォルダーをスキャンして他のウォレットを探す",
|
||||||
"wallets_exists": "その名前のウォレットは既に存在します。",
|
"wallets_exists": "その名前のウォレットは既に存在します。",
|
||||||
"wallets_external_tt": "データディレクトリの外 —「開く」はその場でリンクします(コピーなし)。",
|
"wallets_external_tt": "データディレクトリの外 —「開く」はその場でリンクします(コピーなし)。",
|
||||||
"wallets_folder_hint": "wallet .dat ファイルのあるフォルダのパス",
|
"wallets_folder_hint": "wallet .dat ファイルのあるフォルダのパス",
|
||||||
@@ -1356,7 +1667,9 @@
|
|||||||
"wallets_open_failed": "このウォレットをその場で開けませんでした。データディレクトリとは別のドライブにある可能性があります — 同じドライブに移動してください(Windows では開発者モードを有効にするとドライブ間のリンクも可能になります)。",
|
"wallets_open_failed": "このウォレットをその場で開けませんでした。データディレクトリとは別のドライブにある可能性があります — 同じドライブに移動してください(Windows では開発者モードを有効にするとドライブ間のリンクも可能になります)。",
|
||||||
"wallets_open_folder": "フォルダーの場所を開く",
|
"wallets_open_folder": "フォルダーの場所を開く",
|
||||||
"wallets_open_inplace_tt": "このウォレットをその場で開きます — データディレクトリにリンク(コピーなし)",
|
"wallets_open_inplace_tt": "このウォレットをその場で開きます — データディレクトリにリンク(コピーなし)",
|
||||||
|
"wallets_remove_folder": "このフォルダーのスキャンを停止",
|
||||||
"wallets_reveal": "フォルダを開く",
|
"wallets_reveal": "フォルダを開く",
|
||||||
|
"wallets_scanned_folders": "スキャン対象フォルダー:",
|
||||||
"wallets_sort_addresses": "アドレス",
|
"wallets_sort_addresses": "アドレス",
|
||||||
"wallets_sort_asc": "昇順(古い/少ない/小さい順)",
|
"wallets_sort_asc": "昇順(古い/少ない/小さい順)",
|
||||||
"wallets_sort_by": "並べ替え:",
|
"wallets_sort_by": "並べ替え:",
|
||||||
|
|||||||
325
res/lang/ko.json
325
res/lang/ko.json
@@ -30,6 +30,7 @@
|
|||||||
"address_book_added": "주소록에 주소를 추가했습니다",
|
"address_book_added": "주소록에 주소를 추가했습니다",
|
||||||
"address_book_confirm_delete": "삭제하시겠습니까?",
|
"address_book_confirm_delete": "삭제하시겠습니까?",
|
||||||
"address_book_count": "저장된 주소 %zu개",
|
"address_book_count": "저장된 주소 %zu개",
|
||||||
|
"address_book_count_one": "주소 %zu개 저장됨",
|
||||||
"address_book_deleted": "항목이 삭제되었습니다",
|
"address_book_deleted": "항목이 삭제되었습니다",
|
||||||
"address_book_edit": "주소 편집",
|
"address_book_edit": "주소 편집",
|
||||||
"address_book_empty": "저장된 주소가 없습니다. '새로 추가'를 클릭하여 추가하세요.",
|
"address_book_empty": "저장된 주소가 없습니다. '새로 추가'를 클릭하여 추가하세요.",
|
||||||
@@ -53,8 +54,20 @@
|
|||||||
"amount_details": "금액 상세",
|
"amount_details": "금액 상세",
|
||||||
"amount_exceeds_balance": "금액이 잔액을 초과합니다",
|
"amount_exceeds_balance": "금액이 잔액을 초과합니다",
|
||||||
"amount_label": "금액:",
|
"amount_label": "금액:",
|
||||||
|
"animate_avatars": "아바타 애니메이션",
|
||||||
"appearance": "외관",
|
"appearance": "외관",
|
||||||
"auto_shield": "채굴 자동 차폐",
|
"auto_shield": "채굴 자동 차폐",
|
||||||
|
"av_intro": "채굴 소프트웨어는 종종 잠재적으로 원치 않는 항목으로 표시됩니다. 풀 채굴을 활성화하려면 다음 단계를 따르세요:",
|
||||||
|
"av_open_security": "Windows 보안 열기",
|
||||||
|
"av_step1": "1단계: 제외 추가",
|
||||||
|
"av_step1_b1": "Windows 보안 > 바이러스 및 위협 방지 열기",
|
||||||
|
"av_step1_b2": "설정 관리 > 제외 > 추가 또는 제거 클릭",
|
||||||
|
"av_step1_b3": "폴더 추가: %APPDATA%\\ObsidianDragon\\",
|
||||||
|
"av_step2": "2단계: 격리에서 복원(필요한 경우)",
|
||||||
|
"av_step2_b1": "Windows 보안 > 보호 기록",
|
||||||
|
"av_step2_b2": "xmrig.exe를 찾아 복원 클릭",
|
||||||
|
"av_step3": "3단계: 지갑을 다시 시작하고 다시 시도",
|
||||||
|
"av_title": "Windows Defender가 채굴기를 차단했습니다",
|
||||||
"available": "사용 가능",
|
"available": "사용 가능",
|
||||||
"backup_backing_up": "백업 중...",
|
"backup_backing_up": "백업 중...",
|
||||||
"backup_create": "백업 생성",
|
"backup_create": "백업 생성",
|
||||||
@@ -62,6 +75,7 @@
|
|||||||
"backup_data": "백업 및 데이터",
|
"backup_data": "백업 및 데이터",
|
||||||
"backup_description": "wallet.dat 파일의 백업을 생성합니다. 이 파일에는 모든 개인 키와 거래 내역이 포함되어 있습니다. 백업을 안전한 곳에 보관하세요.",
|
"backup_description": "wallet.dat 파일의 백업을 생성합니다. 이 파일에는 모든 개인 키와 거래 내역이 포함되어 있습니다. 백업을 안전한 곳에 보관하세요.",
|
||||||
"backup_destination": "백업 위치:",
|
"backup_destination": "백업 위치:",
|
||||||
|
"backup_overwrite_confirm": "그곳에 이미 파일이 있습니다. 다시 저장하면 덮어씁니다.",
|
||||||
"backup_source": "소스: %s",
|
"backup_source": "소스: %s",
|
||||||
"backup_tip_external": "외장 드라이브 또는 클라우드 스토리지에 백업 저장",
|
"backup_tip_external": "외장 드라이브 또는 클라우드 스토리지에 백업 저장",
|
||||||
"backup_tip_multiple": "서로 다른 위치에 여러 백업 생성",
|
"backup_tip_multiple": "서로 다른 위치에 여러 백업 생성",
|
||||||
@@ -70,6 +84,7 @@
|
|||||||
"backup_title": "지갑 백업",
|
"backup_title": "지갑 백업",
|
||||||
"backup_wallet": "지갑 백업...",
|
"backup_wallet": "지갑 백업...",
|
||||||
"backup_wallet_not_found": "경고: 예상 위치에서 wallet.dat를 찾을 수 없습니다",
|
"backup_wallet_not_found": "경고: 예상 위치에서 wallet.dat를 찾을 수 없습니다",
|
||||||
|
"backup_warn": "이 파일에는 모든 개인 키가 들어 있습니다. 안전한 곳에 보관하세요.",
|
||||||
"balance": "잔액",
|
"balance": "잔액",
|
||||||
"balance_history_collecting": "잔액 내역 — 데이터 수집 중...",
|
"balance_history_collecting": "잔액 내역 — 데이터 수집 중...",
|
||||||
"balance_layout": "잔액 레이아웃",
|
"balance_layout": "잔액 레이아웃",
|
||||||
@@ -114,28 +129,105 @@
|
|||||||
"bootstrap_wallet_protected": "(wallet.dat 보호됨)",
|
"bootstrap_wallet_protected": "(wallet.dat 보호됨)",
|
||||||
"bootstrap_warning": "기존 블록 데이터(blocks, chainstate, notarizations)가 삭제되고 교체됩니다. wallet.dat는 수정되거나 삭제되지 않습니다.",
|
"bootstrap_warning": "기존 블록 데이터(blocks, chainstate, notarizations)가 삭제되고 교체됩니다. wallet.dat는 수정되거나 삭제되지 않습니다.",
|
||||||
"cancel": "취소",
|
"cancel": "취소",
|
||||||
|
"change_pass_confirm": "새 암호 확인:",
|
||||||
|
"change_pass_current": "현재 암호:",
|
||||||
|
"change_pass_new": "새 암호:",
|
||||||
|
"change_pass_title": "암호 변경",
|
||||||
"characters": "문자",
|
"characters": "문자",
|
||||||
"chat": "채팅",
|
"chat": "채팅",
|
||||||
|
"chat_accent_amber": "황색",
|
||||||
|
"chat_accent_blue": "파랑",
|
||||||
|
"chat_accent_green": "초록",
|
||||||
|
"chat_accent_pink": "분홍",
|
||||||
|
"chat_accent_purple": "보라",
|
||||||
|
"chat_accent_theme": "테마",
|
||||||
|
"chat_add_contact": "연락처 추가",
|
||||||
|
"chat_awaiting_key": "답장 대기 중",
|
||||||
|
"chat_bubble_minimal": "미니멀",
|
||||||
|
"chat_bubble_rounded": "둥근",
|
||||||
|
"chat_bubble_square": "각진",
|
||||||
|
"chat_buffer_loading": "채팅 버퍼: …",
|
||||||
|
"chat_buffer_preparing": "채팅 버퍼: %d/%d 준비 중…",
|
||||||
|
"chat_buffer_ready": "채팅 버퍼: %d/%d 준비됨",
|
||||||
|
"chat_buffer_sending": "채팅: 메시지 %d개 보내는 중…",
|
||||||
|
"chat_buffer_sending_one": "채팅: 메시지 %d개 보내는 중…",
|
||||||
"chat_cancel": "취소",
|
"chat_cancel": "취소",
|
||||||
|
"chat_contact_added": "연락처 추가됨 — 연락처에서 이름을 변경하세요",
|
||||||
"chat_contact_request": "연락 요청",
|
"chat_contact_request": "연락 요청",
|
||||||
|
"chat_copy_address_tip": "클릭하여 주소 복사",
|
||||||
|
"chat_density_comfortable": "편안하게",
|
||||||
|
"chat_density_compact": "촘촘하게",
|
||||||
|
"chat_emoji_color": "컬러",
|
||||||
|
"chat_emoji_mono": "단색",
|
||||||
|
"chat_emoji_search": "이모지 검색",
|
||||||
"chat_empty_hint": "아직 대화가 없습니다. 받은 메시지가 여기에 표시됩니다.",
|
"chat_empty_hint": "아직 대화가 없습니다. 받은 메시지가 여기에 표시됩니다.",
|
||||||
|
"chat_empty_start": "\"새 대화\"로 시작하세요.",
|
||||||
|
"chat_empty_title": "아직 대화가 없습니다",
|
||||||
|
"chat_export": "채팅 내보내기…",
|
||||||
|
"chat_export_done": "대화를 내보냈습니다",
|
||||||
|
"chat_export_failed": "내보내기 파일을 쓸 수 없습니다.",
|
||||||
|
"chat_export_warn": "복호화된 메시지를 일반 텍스트로 저장합니다. 파일을 안전하게 보관하세요.",
|
||||||
|
"chat_filter": "채팅",
|
||||||
|
"chat_hidden_toast": "대화를 숨겼습니다 — 새 메시지가 오면 다시 표시됩니다",
|
||||||
|
"chat_hide": "숨기기",
|
||||||
|
"chat_hide_hidden": "숨김 접기",
|
||||||
|
"chat_jump_latest": "최신",
|
||||||
|
"chat_len_over": "메시지가 너무 깁니다",
|
||||||
"chat_locked_hint": "채팅을 불러오려면 지갑 잠금을 해제하세요.",
|
"chat_locked_hint": "채팅을 불러오려면 지갑 잠금을 해제하세요.",
|
||||||
"chat_new_button": "새 대화",
|
"chat_mute": "음소거",
|
||||||
|
"chat_new_button": "새 채팅",
|
||||||
"chat_new_message": "메시지",
|
"chat_new_message": "메시지",
|
||||||
|
"chat_new_message_toast": "새 암호화 채팅 메시지",
|
||||||
"chat_new_send": "요청 보내기",
|
"chat_new_send": "요청 보내기",
|
||||||
"chat_new_title": "새 대화",
|
"chat_new_title": "새 채팅",
|
||||||
"chat_new_zaddr": "받는 사람 z-주소",
|
"chat_new_zaddr": "받는 사람 z-주소",
|
||||||
|
"chat_no_matches": "검색과 일치하는 대화가 없습니다.",
|
||||||
|
"chat_no_z_contacts": "보호 주소 연락처가 아직 없습니다",
|
||||||
|
"chat_opt_bubble_accent": "말풍선 색상",
|
||||||
|
"chat_opt_bubble_style": "말풍선 스타일",
|
||||||
|
"chat_opt_density": "메시지 밀도",
|
||||||
|
"chat_opt_emoji": "이모지 스타일",
|
||||||
|
"chat_opt_enter_sends": "Enter로 전송",
|
||||||
|
"chat_opt_font_size": "글자 크기",
|
||||||
|
"chat_opt_global_clock": "전역 시간 형식",
|
||||||
|
"chat_opt_poll": "폴링 주기",
|
||||||
|
"chat_opt_timestamp": "타임스탬프",
|
||||||
|
"chat_pick_contact": "연락처에서 선택…",
|
||||||
|
"chat_rename": "연락처 이름 변경",
|
||||||
|
"chat_rename_hint": "연락처 이름",
|
||||||
|
"chat_renamed": "연락처 이름이 변경되었습니다",
|
||||||
|
"chat_retry": "다시 시도",
|
||||||
|
"chat_search": "대화 검색",
|
||||||
|
"chat_sec_appearance": "모양",
|
||||||
|
"chat_sec_messaging": "메시지",
|
||||||
"chat_select_hint": "볼 대화를 선택하세요.",
|
"chat_select_hint": "볼 대화를 선택하세요.",
|
||||||
"chat_send": "전송",
|
"chat_send": "전송",
|
||||||
"chat_send_failed": "전송 안 됨",
|
"chat_send_failed": "전송 안 됨",
|
||||||
|
"chat_sending": "전송 중…",
|
||||||
|
"chat_settings_done": "완료",
|
||||||
|
"chat_settings_section": "채팅 및 연락처",
|
||||||
|
"chat_settings_tip": "채팅 사용자 지정",
|
||||||
|
"chat_settings_title": "채팅 설정",
|
||||||
|
"chat_show_hidden": "숨김 보기",
|
||||||
|
"chat_time_now": "방금",
|
||||||
"chat_toast_compose_failed": "메시지를 작성할 수 없습니다 (너무 긴가요?).",
|
"chat_toast_compose_failed": "메시지를 작성할 수 없습니다 (너무 긴가요?).",
|
||||||
"chat_toast_lite_busy": "이미 전송이 진행 중이거나 열린 지갑이 없습니다.",
|
"chat_toast_lite_busy": "이미 전송이 진행 중이거나 열린 지갑이 없습니다.",
|
||||||
|
"chat_toast_need_funds": "채팅을 보내려면 수수료를 낼 소액의 보호 잔액이 필요합니다.",
|
||||||
"chat_toast_no_zaddr": "채팅을 보낼 z-주소가 없습니다.",
|
"chat_toast_no_zaddr": "채팅을 보낼 z-주소가 없습니다.",
|
||||||
"chat_toast_not_connected": "연결되지 않음 — 채팅 메시지가 전송되지 않았습니다.",
|
"chat_toast_not_connected": "연결되지 않음 — 채팅 메시지가 전송되지 않았습니다.",
|
||||||
"chat_toast_request_compose_failed": "연락 요청을 작성할 수 없습니다 (잘못된 주소 / 텍스트?).",
|
"chat_toast_request_compose_failed": "연락 요청을 작성할 수 없습니다 (잘못된 주소 / 텍스트?).",
|
||||||
"chat_toast_request_queued": "연락 요청이 대기열에 추가되었습니다.",
|
"chat_toast_request_queued": "연락 요청이 대기열에 추가되었습니다.",
|
||||||
"chat_toast_waiting_reply": "상대방이 답장해야 메시지를 보낼 수 있습니다.",
|
"chat_toast_waiting_reply": "상대방이 답장해야 메시지를 보낼 수 있습니다.",
|
||||||
|
"chat_today": "오늘",
|
||||||
|
"chat_ts_12h": "12시간",
|
||||||
|
"chat_ts_24h": "24시간",
|
||||||
|
"chat_ts_global": "전역 설정 따르기",
|
||||||
|
"chat_ts_global_short": "전역",
|
||||||
|
"chat_unhide": "다시 표시",
|
||||||
|
"chat_unmute": "음소거 해제",
|
||||||
|
"chat_verify_key": "신원 키 — 비교하여 확인",
|
||||||
"chat_waiting_reply": "상대방의 답장을 기다리는 중입니다 — 답장하면 메시지를 보낼 수 있습니다.",
|
"chat_waiting_reply": "상대방의 답장을 기다리는 중입니다 — 답장하면 메시지를 보낼 수 있습니다.",
|
||||||
|
"chat_yesterday": "어제",
|
||||||
"chat_you": "나",
|
"chat_you": "나",
|
||||||
"choose_icon": "아이콘 선택",
|
"choose_icon": "아이콘 선택",
|
||||||
"clear": "지우기",
|
"clear": "지우기",
|
||||||
@@ -147,6 +239,7 @@
|
|||||||
"click_copy_address": "클릭하여 주소 복사",
|
"click_copy_address": "클릭하여 주소 복사",
|
||||||
"click_copy_uri": "클릭하여 URI 복사",
|
"click_copy_uri": "클릭하여 URI 복사",
|
||||||
"click_to_copy": "복사하려면 클릭",
|
"click_to_copy": "복사하려면 클릭",
|
||||||
|
"clock_format": "시간 형식",
|
||||||
"close": "닫기",
|
"close": "닫기",
|
||||||
"conf_count": "%d 확인",
|
"conf_count": "%d 확인",
|
||||||
"confirm_and_send": "확인 후 전송",
|
"confirm_and_send": "확인 후 전송",
|
||||||
@@ -180,10 +273,24 @@
|
|||||||
"connected_peers": "연결된 피어",
|
"connected_peers": "연결된 피어",
|
||||||
"connecting": "연결 중...",
|
"connecting": "연결 중...",
|
||||||
"console": "콘솔",
|
"console": "콘솔",
|
||||||
|
"console_accents": "색상 강조",
|
||||||
"console_app": "앱",
|
"console_app": "앱",
|
||||||
"console_auto_scroll": "자동 스크롤",
|
"console_auto_scroll": "자동 스크롤",
|
||||||
"console_available_commands": "사용 가능한 명령어:",
|
"console_available_commands": "사용 가능한 명령어:",
|
||||||
|
"console_backend_reference": "백엔드 명령어 참조",
|
||||||
|
"console_backend_unavailable": "백엔드 없음",
|
||||||
"console_capturing_output": "데몬 출력 캡처 중...",
|
"console_capturing_output": "데몬 출력 캡처 중...",
|
||||||
|
"console_cat_advanced": "고급",
|
||||||
|
"console_cat_blockchain": "블록체인",
|
||||||
|
"console_cat_control": "제어",
|
||||||
|
"console_cat_keys": "키 및 보안",
|
||||||
|
"console_cat_mining": "채굴",
|
||||||
|
"console_cat_network": "네트워크",
|
||||||
|
"console_cat_raw_transactions": "원시 트랜잭션",
|
||||||
|
"console_cat_send": "보내기",
|
||||||
|
"console_cat_sync": "동기화",
|
||||||
|
"console_cat_utility": "유틸리티",
|
||||||
|
"console_cat_wallet": "지갑",
|
||||||
"console_clear": "지우기",
|
"console_clear": "지우기",
|
||||||
"console_clear_console": "콘솔 지우기",
|
"console_clear_console": "콘솔 지우기",
|
||||||
"console_cleared": "콘솔이 지워졌습니다",
|
"console_cleared": "콘솔이 지워졌습니다",
|
||||||
@@ -215,18 +322,37 @@
|
|||||||
"console_help_help": " help - 도움말 표시",
|
"console_help_help": " help - 도움말 표시",
|
||||||
"console_help_setgenerate": " setgenerate - 채굴 제어",
|
"console_help_setgenerate": " setgenerate - 채굴 제어",
|
||||||
"console_help_stop": " stop - 데몬 중지",
|
"console_help_stop": " stop - 데몬 중지",
|
||||||
|
"console_last_error": "마지막 오류:",
|
||||||
"console_line_count": "%zu줄",
|
"console_line_count": "%zu줄",
|
||||||
"console_matches": "일치",
|
"console_matches": "일치",
|
||||||
"console_new_lines": "%d 새 줄",
|
"console_new_lines": "%d 새 줄",
|
||||||
"console_no_daemon": "데몬 없음",
|
"console_no_daemon": "데몬 없음",
|
||||||
|
"console_no_output": "(출력 없음)",
|
||||||
"console_not_connected": "오류: 데몬에 연결되지 않았습니다",
|
"console_not_connected": "오류: 데몬에 연결되지 않았습니다",
|
||||||
|
"console_not_connected_lite": "오류: 열린 지갑 없음",
|
||||||
"console_quit_note": "여기서는 'quit'/'exit'가 필요 없습니다 — 그냥 창을 닫으세요.",
|
"console_quit_note": "여기서는 'quit'/'exit'가 필요 없습니다 — 그냥 창을 닫으세요.",
|
||||||
|
"console_ref_builds": "생성",
|
||||||
|
"console_ref_cancel": "취소",
|
||||||
|
"console_ref_destructive": "주의",
|
||||||
|
"console_ref_example": "예시",
|
||||||
|
"console_ref_insert": "콘솔에 삽입",
|
||||||
|
"console_ref_insert_run": "삽입 후 실행",
|
||||||
|
"console_ref_no_match": "일치하는 명령이 없습니다.",
|
||||||
|
"console_ref_no_params": "매개변수가 없습니다.",
|
||||||
|
"console_ref_optional": "선택",
|
||||||
|
"console_ref_parameters": "매개변수",
|
||||||
|
"console_ref_run": "실행",
|
||||||
|
"console_ref_run_confirm": "%s 을(를) 지금 실행할까요? 영향이 큰 명령입니다.",
|
||||||
|
"console_ref_search_hint": "이름 또는 용도로 검색…",
|
||||||
|
"console_ref_select_hint": "명령을 선택하면 설명이 표시됩니다.",
|
||||||
|
"console_ref_value": "값",
|
||||||
"console_rpc_reference": "RPC 명령어 참조",
|
"console_rpc_reference": "RPC 명령어 참조",
|
||||||
"console_rpc_trace": "RPC",
|
"console_rpc_trace": "RPC",
|
||||||
"console_scanline": "콘솔 스캔라인",
|
"console_scanline": "콘솔 스캔라인",
|
||||||
"console_search_commands": "명령어 검색...",
|
"console_search_commands": "명령어 검색...",
|
||||||
"console_select_all": "모두 선택",
|
"console_select_all": "모두 선택",
|
||||||
"console_show_app_output": "[app] 지갑 로그 줄 표시",
|
"console_show_app_output": "[app] 지갑 로그 줄 표시",
|
||||||
|
"console_show_backend_ref": "백엔드 명령어 참조 표시",
|
||||||
"console_show_daemon_output": "데몬 출력 표시",
|
"console_show_daemon_output": "데몬 출력 표시",
|
||||||
"console_show_errors_only": "오류만 표시",
|
"console_show_errors_only": "오류만 표시",
|
||||||
"console_show_rpc_ref": "RPC 명령어 참조 표시",
|
"console_show_rpc_ref": "RPC 명령어 참조 표시",
|
||||||
@@ -239,17 +365,43 @@
|
|||||||
"console_status_stopped": "중지됨",
|
"console_status_stopped": "중지됨",
|
||||||
"console_status_stopping": "중지 중",
|
"console_status_stopping": "중지 중",
|
||||||
"console_status_unknown": "알 수 없음",
|
"console_status_unknown": "알 수 없음",
|
||||||
|
"console_stop_confirm_node": "'stop'은 노드를 종료하고 지갑 연결을 끊습니다. 확인하려면 'stop'을 다시 입력하세요.",
|
||||||
"console_tab_completion": "Tab으로 자동 완성",
|
"console_tab_completion": "Tab으로 자동 완성",
|
||||||
|
"console_text_colors": "텍스트 색상",
|
||||||
|
"console_toggle_accents": "줄 색상 강조 전환",
|
||||||
|
"console_toggle_text_color": "줄 텍스트 색상 전환",
|
||||||
"console_type_help": "'help'를 입력하여 사용 가능한 명령어 보기",
|
"console_type_help": "'help'를 입력하여 사용 가능한 명령어 보기",
|
||||||
"console_welcome": "ObsidianDragon 콘솔에 오신 것을 환영합니다",
|
"console_welcome": "ObsidianDragon 콘솔에 오신 것을 환영합니다",
|
||||||
"console_zoom_in": "확대",
|
"console_zoom_in": "확대",
|
||||||
"console_zoom_out": "축소",
|
"console_zoom_out": "축소",
|
||||||
|
"contact_avatar": "아바타",
|
||||||
|
"contact_avatar_bad_image": "그 이미지를 불러올 수 없습니다.",
|
||||||
|
"contact_avatar_badge": "배지",
|
||||||
|
"contact_avatar_badge_hint": "배지는 주소 유형에 따라 자동으로 선택됩니다.",
|
||||||
|
"contact_avatar_choose": "이미지 선택…",
|
||||||
|
"contact_avatar_copy_failed": "그 이미지를 복사할 수 없습니다.",
|
||||||
|
"contact_avatar_icon": "아이콘",
|
||||||
|
"contact_avatar_image": "이미지",
|
||||||
|
"contact_avatar_image_hint": "이미지는 앱에 복사되어 원본이 이동해도 계속 사용할 수 있습니다.",
|
||||||
|
"contact_avatar_remove": "제거",
|
||||||
|
"contact_avatar_shielded": "보호",
|
||||||
|
"contact_avatar_transparent": "투명",
|
||||||
"contact_global": "모든 지갑에 표시(전역 연락처)",
|
"contact_global": "모든 지갑에 표시(전역 연락처)",
|
||||||
"contact_global_badge_tt": "전역 연락처 — 모든 지갑에서 표시됨",
|
"contact_global_badge_tt": "전역 연락처 — 모든 지갑에서 표시됨",
|
||||||
"contact_global_tt": "켜짐: 어떤 지갑을 불러오든 이 연락처가 계속 표시됩니다. 꺼짐: 현재 지갑에만 속합니다.",
|
"contact_global_tt": "켜짐: 어떤 지갑을 불러오든 이 연락처가 계속 표시됩니다. 꺼짐: 현재 지갑에만 속합니다.",
|
||||||
|
"contact_preview_addr": "여기에 주소가 표시됩니다",
|
||||||
|
"contact_preview_name": "연락처 이름",
|
||||||
|
"contact_wallet_loading": "지갑을 아직 불러오는 중입니다 — “모든 지갑에 표시”를 선택하거나 잠시 후 다시 시도하세요.",
|
||||||
"contacts": "연락처",
|
"contacts": "연락처",
|
||||||
|
"contacts_avatar_shape": "아바타 모양",
|
||||||
|
"contacts_list_scale": "목록 배율",
|
||||||
"contacts_search_no_match": "일치하는 연락처 없음",
|
"contacts_search_no_match": "일치하는 연락처 없음",
|
||||||
"contacts_search_placeholder": "연락처 검색...",
|
"contacts_search_placeholder": "연락처 검색...",
|
||||||
|
"contacts_settings_tip": "연락처 사용자 지정",
|
||||||
|
"contacts_settings_title": "연락처 설정",
|
||||||
|
"contacts_shape_circle": "원",
|
||||||
|
"contacts_shape_square": "사각형",
|
||||||
|
"contacts_shape_tab": "왼쪽 탭",
|
||||||
"copied": "복사됨!",
|
"copied": "복사됨!",
|
||||||
"copy": "복사",
|
"copy": "복사",
|
||||||
"copy_address": "전체 주소 복사",
|
"copy_address": "전체 주소 복사",
|
||||||
@@ -302,6 +454,19 @@
|
|||||||
"date": "날짜",
|
"date": "날짜",
|
||||||
"date_label": "날짜:",
|
"date_label": "날짜:",
|
||||||
"debug_logging": "디버그 로깅",
|
"debug_logging": "디버그 로깅",
|
||||||
|
"decrypt_desc": "지갑을 내보내고 데몬을 새 암호화되지 않은 지갑으로 다시 시작한 후 모든 키를 다시 가져옵니다. 지갑 크기에 따라 몇 분 정도 걸릴 수 있습니다.",
|
||||||
|
"decrypt_error_title": "복호화 실패",
|
||||||
|
"decrypt_step_backup": "암호화된 지갑 백업 중",
|
||||||
|
"decrypt_step_export": "지갑 키 내보내는 중",
|
||||||
|
"decrypt_step_restart": "데몬 다시 시작 중",
|
||||||
|
"decrypt_step_stop": "데몬 중지 중",
|
||||||
|
"decrypt_step_unlock": "지갑 잠금 해제 중",
|
||||||
|
"decrypt_success_desc": "이제 지갑이 암호화되지 않았습니다. 암호화된 지갑의 백업이 데이터 디렉터리에 wallet.dat.encrypted.bak로 저장되었습니다.",
|
||||||
|
"decrypt_success_title": "지갑 복호화에 성공했습니다!",
|
||||||
|
"decrypt_title": "지갑 암호화 제거",
|
||||||
|
"decrypt_wait_general": "잠시 기다려 주세요. 데몬이 키를 내보내고 다시 시작한 후 다시 가져오고 있습니다. 몇 분 정도 걸릴 수 있습니다.",
|
||||||
|
"decrypt_wait_restart": "데몬이 완전히 시작될 때까지 기다리는 중...",
|
||||||
|
"decrypt_warning": "지갑의 암호화가 제거됩니다. 개인 키가 보호되지 않은 상태로 디스크에 저장됩니다.",
|
||||||
"delete": "삭제",
|
"delete": "삭제",
|
||||||
"delete_blockchain": "블록체인 삭제",
|
"delete_blockchain": "블록체인 삭제",
|
||||||
"delete_blockchain_confirm": "삭제 후 재동기화",
|
"delete_blockchain_confirm": "삭제 후 재동기화",
|
||||||
@@ -314,6 +479,15 @@
|
|||||||
"download_bootstrap": "부트스트랩 다운로드",
|
"download_bootstrap": "부트스트랩 다운로드",
|
||||||
"dragonx_green": "DragonX(그린)",
|
"dragonx_green": "DragonX(그린)",
|
||||||
"edit": "편집",
|
"edit": "편집",
|
||||||
|
"enc_confirm": "확인:",
|
||||||
|
"enc_desc": "지갑을 암호화하면 암호로 개인 키를 보호합니다. 암호화 후 데몬이 다시 시작됩니다.",
|
||||||
|
"enc_encrypting": "지갑을 암호화하는 중...",
|
||||||
|
"enc_pin_desc": "4~8자리 PIN으로 매번 전체 암호를 입력하지 않고도 지갑 잠금을 해제할 수 있습니다.",
|
||||||
|
"enc_pin_set_ok": "PIN이 설정되었습니다",
|
||||||
|
"enc_pin_skipped": "PIN을 건너뛰었습니다. 나중에 설정에서 만들 수 있습니다.",
|
||||||
|
"enc_pin_vault_fail": "PIN 보관소를 만들지 못했습니다",
|
||||||
|
"enc_success": "지갑이 성공적으로 암호화되었습니다!",
|
||||||
|
"enc_wait": "잠시 기다려 주세요. 애플리케이션을 닫지 마세요.",
|
||||||
"error": "오류",
|
"error": "오류",
|
||||||
"error_format": "오류: %s",
|
"error_format": "오류: %s",
|
||||||
"est_time_to_block": "예상 블록 시간",
|
"est_time_to_block": "예상 블록 시간",
|
||||||
@@ -333,23 +507,34 @@
|
|||||||
"explorer_mempool": "멤풀",
|
"explorer_mempool": "멤풀",
|
||||||
"explorer_mempool_size": "크기",
|
"explorer_mempool_size": "크기",
|
||||||
"explorer_mempool_txs": "트랜잭션",
|
"explorer_mempool_txs": "트랜잭션",
|
||||||
|
"explorer_next_block": "다음 블록",
|
||||||
"explorer_no_results": "일치하는 캐시된 블록이 없습니다",
|
"explorer_no_results": "일치하는 캐시된 블록이 없습니다",
|
||||||
"explorer_not_connected": "데몬에 연결되지 않음 — 블록 또는 트랜잭션 해시를 조회할 수 없습니다",
|
"explorer_not_connected": "데몬에 연결되지 않음 — 블록 또는 트랜잭션 해시를 조회할 수 없습니다",
|
||||||
|
"explorer_prev_block": "이전 블록",
|
||||||
"explorer_recent_blocks": "최근 블록",
|
"explorer_recent_blocks": "최근 블록",
|
||||||
"explorer_search": "검색",
|
"explorer_search": "검색",
|
||||||
"explorer_section": "탐색기",
|
"explorer_section": "탐색기",
|
||||||
"explorer_tx_outputs": "출력",
|
"explorer_tx_outputs": "출력",
|
||||||
"explorer_tx_size": "크기",
|
"explorer_tx_size": "크기",
|
||||||
|
"explorer_txid": "TxID:",
|
||||||
"export": "내보내기",
|
"export": "내보내기",
|
||||||
"export_csv": "CSV 내보내기",
|
"export_csv": "CSV 내보내기",
|
||||||
"export_keys_btn": "키 내보내기",
|
"export_keys_btn": "키 내보내기",
|
||||||
"export_keys_danger": "위험: 지갑의 모든 개인 키가 내보내집니다! 이 파일에 접근할 수 있는 사람은 누구나 자금을 훔칠 수 있습니다. 안전하게 보관하고 사용 후 삭제하세요.",
|
"export_keys_danger": "위험: 지갑의 모든 개인 키가 내보내집니다! 이 파일에 접근할 수 있는 사람은 누구나 자금을 훔칠 수 있습니다. 안전하게 보관하고 사용 후 삭제하세요.",
|
||||||
"export_keys_include_t": "T 주소 포함 (투명)",
|
"export_keys_include_t": "T 주소 포함 (투명)",
|
||||||
"export_keys_include_z": "Z 주소 포함 (차폐)",
|
"export_keys_include_z": "Z 주소 포함 (차폐)",
|
||||||
|
"export_keys_none_addrs": "내보낼 주소가 없습니다",
|
||||||
|
"export_keys_none_result": "키를 내보내지 못했습니다(%d개 중 0개). 지갑 잠금을 해제하고 다시 시도하세요.",
|
||||||
|
"export_keys_none_toast": "키를 내보낼 수 없습니다. 지갑이 잠금 해제되어 있나요?",
|
||||||
|
"export_keys_not_connected": "데몬에 연결되어 있지 않습니다",
|
||||||
"export_keys_options": "내보내기 옵션:",
|
"export_keys_options": "내보내기 옵션:",
|
||||||
|
"export_keys_partial": "%d개 중 %d개 키 내보냄 — 불완전(일부는 지출 키가 없거나 지갑이 잠겨 있음).",
|
||||||
|
"export_keys_partial_toast": "부분 내보내기: %d개 중 %d개 키",
|
||||||
"export_keys_progress": "내보내는 중 %d/%d...",
|
"export_keys_progress": "내보내는 중 %d/%d...",
|
||||||
|
"export_keys_select_type": "주소 유형을 하나 이상 선택하세요",
|
||||||
"export_keys_success": "키 내보내기 성공",
|
"export_keys_success": "키 내보내기 성공",
|
||||||
"export_keys_title": "모든 개인 키 내보내기",
|
"export_keys_title": "모든 개인 키 내보내기",
|
||||||
|
"export_keys_write_fail": "키 파일을 쓰지 못했습니다.",
|
||||||
"export_private_key": "개인 키 내보내기",
|
"export_private_key": "개인 키 내보내기",
|
||||||
"export_tx_count": "%zu건의 거래를 CSV 파일로 내보냈습니다.",
|
"export_tx_count": "%zu건의 거래를 CSV 파일로 내보냈습니다.",
|
||||||
"export_tx_file_fail": "CSV 파일 생성 실패",
|
"export_tx_file_fail": "CSV 파일 생성 실패",
|
||||||
@@ -389,23 +574,50 @@
|
|||||||
"hide_qr": "QR 숨기기",
|
"hide_qr": "QR 숨기기",
|
||||||
"hide_zero_balances": "잔액 0 숨기기",
|
"hide_zero_balances": "잔액 0 숨기기",
|
||||||
"history": "내역",
|
"history": "내역",
|
||||||
|
"img_picker_count": "이 폴더에 이미지 %d개",
|
||||||
|
"img_picker_empty": "이 폴더에는 하위 폴더나 이미지가 없습니다.",
|
||||||
|
"img_picker_none": "이 폴더에 이미지가 없습니다",
|
||||||
|
"img_picker_pictures": "사진 폴더",
|
||||||
|
"img_picker_title": "이미지 선택",
|
||||||
|
"img_picker_use": "이미지 사용",
|
||||||
"immature_type": "미성숙",
|
"immature_type": "미성숙",
|
||||||
"import": "가져오기",
|
"import": "가져오기",
|
||||||
|
"import_key_address": "주소:",
|
||||||
"import_key_btn": "키 가져오기",
|
"import_key_btn": "키 가져오기",
|
||||||
|
"import_key_done": "가져왔습니다. 지갑을 재검색 중입니다.",
|
||||||
|
"import_key_field": "키",
|
||||||
"import_key_formats": "지원되는 키 형식:",
|
"import_key_formats": "지원되는 키 형식:",
|
||||||
"import_key_full_rescan": "(0 = 전체 재스캔)",
|
"import_key_full_rescan": "(0 = 전체 재스캔)",
|
||||||
|
"import_key_import": "가져오기",
|
||||||
"import_key_label": "개인 키:",
|
"import_key_label": "개인 키:",
|
||||||
|
"import_key_need_node": "키를 가져오려면 실행 중인 노드에 연결하세요.",
|
||||||
"import_key_no_valid": "입력에서 유효한 키를 찾을 수 없습니다",
|
"import_key_no_valid": "입력에서 유효한 키를 찾을 수 없습니다",
|
||||||
"import_key_progress": "가져오는 중 %d/%d...",
|
"import_key_progress": "가져오는 중 %d/%d...",
|
||||||
"import_key_rescan": "가져오기 후 블록체인 재스캔",
|
"import_key_rescan": "가져오기 후 블록체인 재스캔",
|
||||||
|
"import_key_rescanning": "가져오기 및 재검색 중 — 몇 분 걸릴 수 있습니다",
|
||||||
|
"import_key_reveal_tip": "키 표시/숨기기",
|
||||||
"import_key_start_height": "시작 높이:",
|
"import_key_start_height": "시작 높이:",
|
||||||
"import_key_success": "키 가져오기 성공",
|
"import_key_success": "키 가져오기 성공",
|
||||||
"import_key_t_format": "T 주소 WIF 개인 키",
|
"import_key_t_format": "T 주소 WIF 개인 키",
|
||||||
"import_key_title": "개인 키 가져오기",
|
"import_key_title": "개인 키 가져오기",
|
||||||
"import_key_tooltip": "한 줄에 하나의 개인 키를 입력하세요.\nz 주소와 t 주소 키 모두 지원됩니다.\n#으로 시작하는 줄은 주석으로 처리됩니다.",
|
"import_key_tooltip": "한 줄에 하나의 개인 키를 입력하세요.\nz 주소와 t 주소 키 모두 지원됩니다.\n#으로 시작하는 줄은 주석으로 처리됩니다.",
|
||||||
|
"import_key_type_tkey": "투명 개인 키",
|
||||||
|
"import_key_type_unknown": "알 수 없는 키 형식",
|
||||||
|
"import_key_type_zspend": "차폐 지출 키",
|
||||||
|
"import_key_type_zview": "차폐 조회 키 (읽기 전용)",
|
||||||
|
"import_key_warn": "본인이 소유한 키만 가져오세요 — 해당 자금에 접근할 수 있습니다.",
|
||||||
"import_key_warning": "경고: 개인 키를 절대 공유하지 마세요! 신뢰할 수 없는 소스의 키를 가져오면 지갑이 위험해질 수 있습니다.",
|
"import_key_warning": "경고: 개인 키를 절대 공유하지 마세요! 신뢰할 수 없는 소스의 키를 가져오면 지갑이 위험해질 수 있습니다.",
|
||||||
|
"import_key_wrong_type": "조회 키인 것 같습니다. 대신 \"조회 키 가져오기\"를 사용하세요.",
|
||||||
"import_key_z_format": "Z 주소 지출 키 (secret-extended-key-...)",
|
"import_key_z_format": "Z 주소 지출 키 (secret-extended-key-...)",
|
||||||
"import_private_key": "개인 키 가져오기...",
|
"import_private_key": "개인 키 가져오기...",
|
||||||
|
"import_scan_hint": "0 = 처음부터 다시 스캔",
|
||||||
|
"import_scan_label": "스캔 시작 블록 높이(선택 사항)",
|
||||||
|
"import_scan_tip": "현재 높이",
|
||||||
|
"import_scan_transparent": "투명 키는 항상 전체를 다시 스캔합니다",
|
||||||
|
"import_viewkey_field": "조회 키",
|
||||||
|
"import_viewkey_note": "읽기 전용: 조회 키는 주소의 잔액과 거래를 보여주지만 자금을 사용할 수는 없습니다.",
|
||||||
|
"import_viewkey_title": "조회 키 가져오기",
|
||||||
|
"import_viewkey_wrong_type": "지출 키인 것 같습니다. 대신 \"개인 키 가져오기\"를 사용하세요.",
|
||||||
"incorrect_passphrase": "잘못된 암호",
|
"incorrect_passphrase": "잘못된 암호",
|
||||||
"incorrect_pin": "잘못된 PIN",
|
"incorrect_pin": "잘못된 PIN",
|
||||||
"insufficient_funds": "이 금액과 수수료를 위한 잔액이 부족합니다.",
|
"insufficient_funds": "이 금액과 수수료를 위한 잔액이 부족합니다.",
|
||||||
@@ -414,6 +626,7 @@
|
|||||||
"keep": "유지",
|
"keep": "유지",
|
||||||
"keep_daemon": "데몬 계속 실행",
|
"keep_daemon": "데몬 계속 실행",
|
||||||
"key_export_click_retrieve": "지갑에서 키를 가져오려면 클릭",
|
"key_export_click_retrieve": "지갑에서 키를 가져오려면 클릭",
|
||||||
|
"key_export_failed": "키를 내보내지 못했습니다. 지갑 잠금을 해제하고(암호화된 경우) 다시 시도하세요.",
|
||||||
"key_export_fetching": "지갑에서 키를 가져오는 중...",
|
"key_export_fetching": "지갑에서 키를 가져오는 중...",
|
||||||
"key_export_private_key": "개인 키:",
|
"key_export_private_key": "개인 키:",
|
||||||
"key_export_private_warning": "이 키를 비밀로 유지하세요! 이 키를 가진 사람은 누구나 자금을 사용할 수 있습니다. 온라인이나 신뢰할 수 없는 사람과 공유하지 마세요.",
|
"key_export_private_warning": "이 키를 비밀로 유지하세요! 이 키를 가진 사람은 누구나 자금을 사용할 수 있습니다. 온라인이나 신뢰할 수 없는 사람과 공유하지 마세요.",
|
||||||
@@ -431,6 +644,7 @@
|
|||||||
"lite_birthday_backup": "생성 블록: %llu (이 값도 백업하세요)",
|
"lite_birthday_backup": "생성 블록: %llu (이 값도 백업하세요)",
|
||||||
"lite_birthday_hint": "스캔을 시작할 블록 높이입니다. 모르면 0으로 두세요(전체 스캔이 느려짐).",
|
"lite_birthday_hint": "스캔을 시작할 블록 높이입니다. 모르면 0으로 두세요(전체 스캔이 느려짐).",
|
||||||
"lite_birthday_label": "생일 블록",
|
"lite_birthday_label": "생일 블록",
|
||||||
|
"lite_console_backend_commands": "백엔드 명령:",
|
||||||
"lite_console_help_passthrough": "그 외 입력은 라이트 지갑 콘솔 명령으로 실행됩니다.",
|
"lite_console_help_passthrough": "그 외 입력은 라이트 지갑 콘솔 명령으로 실행됩니다.",
|
||||||
"lite_copy": "복사",
|
"lite_copy": "복사",
|
||||||
"lite_could_not_write": "쓸 수 없습니다: ",
|
"lite_could_not_write": "쓸 수 없습니다: ",
|
||||||
@@ -448,6 +662,7 @@
|
|||||||
"lite_net_add_url_hint": "https://your-lite-server",
|
"lite_net_add_url_hint": "https://your-lite-server",
|
||||||
"lite_net_checking": "확인 중…",
|
"lite_net_checking": "확인 중…",
|
||||||
"lite_net_connected": "연결됨",
|
"lite_net_connected": "연결됨",
|
||||||
|
"lite_net_connecting": "연결 중…",
|
||||||
"lite_net_custom": "사용자 지정",
|
"lite_net_custom": "사용자 지정",
|
||||||
"lite_net_disconnected": "연결되지 않음",
|
"lite_net_disconnected": "연결되지 않음",
|
||||||
"lite_net_hidden_section": "숨겨진 서버",
|
"lite_net_hidden_section": "숨겨진 서버",
|
||||||
@@ -534,6 +749,10 @@
|
|||||||
"market_btc_price": "BTC 가격",
|
"market_btc_price": "BTC 가격",
|
||||||
"market_cap": "시가총액",
|
"market_cap": "시가총액",
|
||||||
"market_cap_short": "시총",
|
"market_cap_short": "시총",
|
||||||
|
"market_chart_loading": "가격 기록 불러오는 중",
|
||||||
|
"market_col_name": "이름",
|
||||||
|
"market_col_trend": "추세",
|
||||||
|
"market_col_value": "가치",
|
||||||
"market_iv_1d": "1일",
|
"market_iv_1d": "1일",
|
||||||
"market_iv_1h": "1시간",
|
"market_iv_1h": "1시간",
|
||||||
"market_iv_1m": "1개월",
|
"market_iv_1m": "1개월",
|
||||||
@@ -542,11 +761,18 @@
|
|||||||
"market_no_history": "가격 내역 없음",
|
"market_no_history": "가격 내역 없음",
|
||||||
"market_no_price": "가격 데이터 없음",
|
"market_no_price": "가격 데이터 없음",
|
||||||
"market_now": "현재",
|
"market_now": "현재",
|
||||||
|
"market_opt_chart_style": "차트 스타일",
|
||||||
"market_pct_shielded": "%.0f%% 차폐됨",
|
"market_pct_shielded": "%.0f%% 차폐됨",
|
||||||
"market_portfolio": "포트폴리오",
|
"market_portfolio": "포트폴리오",
|
||||||
"market_price_loading": "가격 데이터를 불러오는 중...",
|
"market_price_loading": "가격 데이터를 불러오는 중...",
|
||||||
"market_price_unavailable": "가격 데이터를 사용할 수 없습니다",
|
"market_price_unavailable": "가격 데이터를 사용할 수 없습니다",
|
||||||
"market_refresh_price": "가격 데이터 새로고침",
|
"market_refresh_price": "가격 데이터 새로고침",
|
||||||
|
"market_settings_tip": "마켓 옵션",
|
||||||
|
"market_settings_title": "마켓 설정",
|
||||||
|
"market_style_candle": "캔들차트로 전환",
|
||||||
|
"market_style_candle_label": "캔들",
|
||||||
|
"market_style_line": "선형 차트로 전환",
|
||||||
|
"market_style_line_label": "라인",
|
||||||
"market_trade_on": "%s에서 거래",
|
"market_trade_on": "%s에서 거래",
|
||||||
"market_updated": "\\xc2\\xb7 업데이트됨 %s",
|
"market_updated": "\\xc2\\xb7 업데이트됨 %s",
|
||||||
"market_vol_short": "거래량",
|
"market_vol_short": "거래량",
|
||||||
@@ -736,6 +962,7 @@
|
|||||||
"output_filename": "출력 파일명:",
|
"output_filename": "출력 파일명:",
|
||||||
"overview": "개요",
|
"overview": "개요",
|
||||||
"paste": "붙여넣기",
|
"paste": "붙여넣기",
|
||||||
|
"paste_clip_empty": "클립보드가 비어 있습니다",
|
||||||
"paste_from_clipboard": "클립보드에서 붙여넣기",
|
"paste_from_clipboard": "클립보드에서 붙여넣기",
|
||||||
"pay_from": "보낼 곳",
|
"pay_from": "보낼 곳",
|
||||||
"payment_request": "결제 요청",
|
"payment_request": "결제 요청",
|
||||||
@@ -787,7 +1014,14 @@
|
|||||||
"peers_upper": "피어",
|
"peers_upper": "피어",
|
||||||
"peers_version": "버전",
|
"peers_version": "버전",
|
||||||
"pending": "대기 중",
|
"pending": "대기 중",
|
||||||
|
"pin_change_desc": "잠금 해제 PIN을 변경합니다. 현재 PIN과 새 PIN이 필요합니다.",
|
||||||
|
"pin_confirm_new_label": "새 PIN 확인:",
|
||||||
|
"pin_current_label": "현재 PIN:",
|
||||||
|
"pin_new_label": "새 PIN(4~8자리):",
|
||||||
"pin_not_set": "PIN이 설정되지 않았습니다. 암호를 사용하여 잠금 해제하세요.",
|
"pin_not_set": "PIN이 설정되지 않았습니다. 암호를 사용하여 잠금 해제하세요.",
|
||||||
|
"pin_remove_desc": "삭제를 확인하려면 현재 PIN을 입력하세요. 잠금 해제에는 전체 암호가 필요합니다.",
|
||||||
|
"pin_setup_desc": "지갑을 빠르게 잠금 해제할 4~8자리 PIN을 설정하세요. 지갑 암호가 이 PIN으로 암호화되어 로컬에 저장됩니다.",
|
||||||
|
"pin_wallet_passphrase": "지갑 암호:",
|
||||||
"ping": "Ping",
|
"ping": "Ping",
|
||||||
"portfolio_add_entry": "항목 추가",
|
"portfolio_add_entry": "항목 추가",
|
||||||
"portfolio_add_to": "포트폴리오에 추가",
|
"portfolio_add_to": "포트폴리오에 추가",
|
||||||
@@ -826,6 +1060,9 @@
|
|||||||
"portfolio_remove_from": "포트폴리오에서 제거",
|
"portfolio_remove_from": "포트폴리오에서 제거",
|
||||||
"portfolio_revert": "되돌리기",
|
"portfolio_revert": "되돌리기",
|
||||||
"portfolio_save": "저장",
|
"portfolio_save": "저장",
|
||||||
|
"portfolio_save_need_address": "저장하려면 주소를 하나 이상 추가하세요.",
|
||||||
|
"portfolio_save_need_name": "이 그룹을 저장하려면 이름을 입력하세요.",
|
||||||
|
"portfolio_save_need_price": "저장하려면 0보다 큰 수동 가격을 입력하세요.",
|
||||||
"portfolio_search": "주소 검색…",
|
"portfolio_search": "주소 검색…",
|
||||||
"portfolio_search_icons": "아이콘 검색\\xE2\\x80\\xA6",
|
"portfolio_search_icons": "아이콘 검색\\xE2\\x80\\xA6",
|
||||||
"portfolio_select_all": "전체",
|
"portfolio_select_all": "전체",
|
||||||
@@ -839,11 +1076,12 @@
|
|||||||
"portfolio_spark_min": "분",
|
"portfolio_spark_min": "분",
|
||||||
"portfolio_spark_month": "월",
|
"portfolio_spark_month": "월",
|
||||||
"portfolio_spark_week": "주",
|
"portfolio_spark_week": "주",
|
||||||
"portfolio_style_compact": "간결한 행",
|
"portfolio_style_compact": "테이블",
|
||||||
"portfolio_style_detailed": "상세 행",
|
"portfolio_style_detailed": "카드",
|
||||||
"portfolio_style_featured": "강조 행",
|
"portfolio_style_featured": "스포트라이트",
|
||||||
"portfolio_style_label": "포트폴리오 스타일",
|
"portfolio_style_label": "포트폴리오 스타일",
|
||||||
"portfolio_untitled": "제목 없음",
|
"portfolio_untitled": "제목 없음",
|
||||||
|
"portfolio_wallet_loading": "지갑 로딩이 끝난 후 그룹을 추가하세요.",
|
||||||
"price_chart": "가격 차트",
|
"price_chart": "가격 차트",
|
||||||
"privacy_great": "프라이버시가 우수합니다!",
|
"privacy_great": "프라이버시가 우수합니다!",
|
||||||
"privacy_low": "낮은 프라이버시 — 자금을 차폐하세요",
|
"privacy_low": "낮은 프라이버시 — 자금을 차폐하세요",
|
||||||
@@ -973,6 +1211,7 @@
|
|||||||
"send_amount": "금액",
|
"send_amount": "금액",
|
||||||
"send_amount_details": "금액 상세",
|
"send_amount_details": "금액 상세",
|
||||||
"send_amount_upper": "금액",
|
"send_amount_upper": "금액",
|
||||||
|
"send_cannot_send_now": "지금 보낼 수 없습니다 — 연결, 동기화, 사용 가능한 잔액을 확인하세요.",
|
||||||
"send_clear_fields": "모든 양식 필드를 지우시겠습니까?",
|
"send_clear_fields": "모든 양식 필드를 지우시겠습니까?",
|
||||||
"send_contacts_button": "연락처에서 선택",
|
"send_contacts_button": "연락처에서 선택",
|
||||||
"send_copy_error": "오류 복사",
|
"send_copy_error": "오류 복사",
|
||||||
@@ -1061,7 +1300,8 @@
|
|||||||
"settings_gradient_bg": "그라데이션 배경",
|
"settings_gradient_bg": "그라데이션 배경",
|
||||||
"settings_gradient_desc": "텍스처 배경을 부드러운 그라데이션으로 교체",
|
"settings_gradient_desc": "텍스처 배경을 부드러운 그라데이션으로 교체",
|
||||||
"settings_idle_after": "후",
|
"settings_idle_after": "후",
|
||||||
"settings_import_key": "키 가져오기...",
|
"settings_import_key": "개인 키 가져오기...",
|
||||||
|
"settings_import_viewkey": "조회 키 가져오기...",
|
||||||
"settings_language_note": "참고: 일부 텍스트는 업데이트하려면 다시 시작해야 합니다",
|
"settings_language_note": "참고: 일부 텍스트는 업데이트하려면 다시 시작해야 합니다",
|
||||||
"settings_lock_now": "지금 잠금",
|
"settings_lock_now": "지금 잠금",
|
||||||
"settings_locked": "잠김",
|
"settings_locked": "잠김",
|
||||||
@@ -1110,6 +1350,7 @@
|
|||||||
"shield_in_progress": "작업 진행 중...",
|
"shield_in_progress": "작업 진행 중...",
|
||||||
"shield_max_utxos": "작업당 최대 UTXO 수",
|
"shield_max_utxos": "작업당 최대 UTXO 수",
|
||||||
"shield_merge_done": "차폐/통합이 완료되었습니다!",
|
"shield_merge_done": "차폐/통합이 완료되었습니다!",
|
||||||
|
"shield_no_zaddr_hint": "아직 보호된(z) 주소가 없습니다 — 먼저 받기 탭에서 만드세요.",
|
||||||
"shield_op_failed": "작업 실패: ",
|
"shield_op_failed": "작업 실패: ",
|
||||||
"shield_op_submitted": "작업이 제출되었습니다: ",
|
"shield_op_submitted": "작업이 제출되었습니다: ",
|
||||||
"shield_operation_id": "작업 ID: %s",
|
"shield_operation_id": "작업 ID: %s",
|
||||||
@@ -1148,6 +1389,31 @@
|
|||||||
"submitting_transaction": "거래 제출 중...",
|
"submitting_transaction": "거래 제출 중...",
|
||||||
"success": "성공",
|
"success": "성공",
|
||||||
"summary": "요약",
|
"summary": "요약",
|
||||||
|
"sweep_button": "쓸어담기",
|
||||||
|
"sweep_caveat": "키를 가져와 모든 자금을 내 주소로 옮기는 거래에 서명합니다. 키는 잔액이 0인 상태로 지갑에 남습니다.",
|
||||||
|
"sweep_dest_label": "쓸어담은 자금을 보낼 주소",
|
||||||
|
"sweep_dest_new": "새 차폐 주소 (권장)",
|
||||||
|
"sweep_done": "완료 — 자금을 내 주소로 쓸어담았습니다.",
|
||||||
|
"sweep_to": "쓸어담은 주소:",
|
||||||
|
"sweep_toggle": "내 지갑으로 쓸어담기 (키 보관 안 함)",
|
||||||
|
"sweep_tx": "거래:",
|
||||||
|
"switch_corrupt_body": "이 지갑이 손상된 것 같습니다. 노드가 열 수 없습니다. 백업에서 복원하거나 다시 만들거나 복구를 시도하세요.",
|
||||||
|
"switch_corrupt_repair": "복구 시도(salvage)",
|
||||||
|
"switch_progress_background": "백그라운드에서 계속",
|
||||||
|
"switch_progress_default_wallet": "기본 지갑",
|
||||||
|
"switch_progress_elapsed": "경과",
|
||||||
|
"switch_progress_external_wallet": "외부 지갑",
|
||||||
|
"switch_progress_failed_title": "지갑 전환 실패",
|
||||||
|
"switch_progress_from_label": "이전:",
|
||||||
|
"switch_progress_hint": "정상 종료에는 최대 1분이 걸릴 수 있습니다.",
|
||||||
|
"switch_progress_reconnecting": "다시 연결하는 중",
|
||||||
|
"switch_progress_starting": "새 지갑으로 노드를 시작하는 중",
|
||||||
|
"switch_progress_stopping": "현재 노드를 중지하는 중",
|
||||||
|
"switch_progress_title": "지갑 전환 중",
|
||||||
|
"switch_stopnode_body": "지갑을 전환하면 선택한 지갑으로 노드가 다시 시작됩니다. 실행 중인 노드가 중지되고 새 지갑으로 다시 시작됩니다. 일부러 계속 실행해 두었다면 자동으로 다시 켜집니다.",
|
||||||
|
"switch_stopnode_confirm": "노드 중지 후 전환",
|
||||||
|
"switch_stopnode_title": "실행 중인 노드를 중지할까요?",
|
||||||
|
"switch_stopnode_warn": "이 지갑이 시작하지 않은 노드가 이미 실행 중입니다.",
|
||||||
"syncing": "동기화 중...",
|
"syncing": "동기화 중...",
|
||||||
"t_address": "T 주소",
|
"t_address": "T 주소",
|
||||||
"t_addresses": "T 주소",
|
"t_addresses": "T 주소",
|
||||||
@@ -1183,8 +1449,10 @@
|
|||||||
"transfer_to": "이체 대상:",
|
"transfer_to": "이체 대상:",
|
||||||
"transparent": "투명",
|
"transparent": "투명",
|
||||||
"transparent_address": "투명 주소",
|
"transparent_address": "투명 주소",
|
||||||
|
"try_again": "다시 시도",
|
||||||
"tt_addr_url": "블록 탐색기에서 주소를 보기 위한 기본 URL",
|
"tt_addr_url": "블록 탐색기에서 주소를 보기 위한 기본 URL",
|
||||||
"tt_address_book": "빠른 전송을 위해 저장된 주소 관리",
|
"tt_address_book": "빠른 전송을 위해 저장된 주소 관리",
|
||||||
|
"tt_animate_avatars": "애니메이션 연락처 아바타(GIF / WebP) 재생, 끄면 첫 프레임만 표시",
|
||||||
"tt_auto_lock": "이 비활성 시간 후 지갑 잠금",
|
"tt_auto_lock": "이 비활성 시간 후 지갑 잠금",
|
||||||
"tt_auto_shield": "개인 정보 보호를 위해 투명 잔액을 자동으로 차폐 주소로 이동",
|
"tt_auto_shield": "개인 정보 보호를 위해 투명 잔액을 자동으로 차폐 주소로 이동",
|
||||||
"tt_backup": "wallet.dat 백업 만들기",
|
"tt_backup": "wallet.dat 백업 만들기",
|
||||||
@@ -1192,10 +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_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": "디버그 로깅 옵션 펼치기",
|
||||||
@@ -1209,10 +1487,34 @@
|
|||||||
"tt_font_scale": "모든 텍스트 및 UI 크기 조정 (1.0x = 기본, 최대 1.5x).",
|
"tt_font_scale": "모든 텍스트 및 UI 크기 조정 (1.0x = 기본, 최대 1.5x).",
|
||||||
"tt_idle_delay": "채굴 시작 전 대기 시간",
|
"tt_idle_delay": "채굴 시작 전 대기 시간",
|
||||||
"tt_import_key": "이 지갑에 개인키 (zkey 또는 tkey) 가져오기",
|
"tt_import_key": "이 지갑에 개인키 (zkey 또는 tkey) 가져오기",
|
||||||
|
"tt_import_viewkey": "차폐 조회 키를 가져와 주소를 조회(읽기 전용)",
|
||||||
"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를 하나의 주소로 통합",
|
||||||
@@ -1233,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": "투명 채굴 보상을 차폐 주소로 이동",
|
||||||
@@ -1257,6 +1564,7 @@
|
|||||||
"tt_website": "DragonX 웹사이트 열기",
|
"tt_website": "DragonX 웹사이트 열기",
|
||||||
"tt_window_opacity": "배경 불투명도 (낮을수록 = 창을 통해 바탕 화면이 보임)",
|
"tt_window_opacity": "배경 불투명도 (낮을수록 = 창을 통해 바탕 화면이 보임)",
|
||||||
"tt_wizard": "초기 설정 마법사 다시 실행\\n데몬이 재시작됩니다",
|
"tt_wizard": "초기 설정 마법사 다시 실행\\n데몬이 재시작됩니다",
|
||||||
|
"tx_chat_badge": "메시지",
|
||||||
"tx_confirmations": "%d 확인",
|
"tx_confirmations": "%d 확인",
|
||||||
"tx_details_title": "거래 상세",
|
"tx_details_title": "거래 상세",
|
||||||
"tx_from_address": "보낸 주소:",
|
"tx_from_address": "보낸 주소:",
|
||||||
@@ -1325,6 +1633,8 @@
|
|||||||
"wallets_add_folder_toggle": "+ 다른 폴더에서 지갑 검색…",
|
"wallets_add_folder_toggle": "+ 다른 폴더에서 지갑 검색…",
|
||||||
"wallets_badge_encrypted": "암호화됨 (암호로 보호됨)",
|
"wallets_badge_encrypted": "암호화됨 (암호로 보호됨)",
|
||||||
"wallets_badge_encrypted_short": "암호화됨",
|
"wallets_badge_encrypted_short": "암호화됨",
|
||||||
|
"wallets_badge_hd": "HD 지갑 — 열지 않으면 시드 문구를 확인할 수 없습니다",
|
||||||
|
"wallets_badge_hd_short": "HD 지갑",
|
||||||
"wallets_badge_legacy": "레거시 지갑 (시드 문구 없음)",
|
"wallets_badge_legacy": "레거시 지갑 (시드 문구 없음)",
|
||||||
"wallets_badge_legacy_short": "레거시",
|
"wallets_badge_legacy_short": "레거시",
|
||||||
"wallets_badge_seed": "시드 문구 지갑 (HD)",
|
"wallets_badge_seed": "시드 문구 지갑 (HD)",
|
||||||
@@ -1343,6 +1653,7 @@
|
|||||||
"wallets_created": "생성",
|
"wallets_created": "생성",
|
||||||
"wallets_creating": "지갑을 만드는 중 — 노드가 재시작됩니다…",
|
"wallets_creating": "지갑을 만드는 중 — 노드가 재시작됩니다…",
|
||||||
"wallets_current": "현재",
|
"wallets_current": "현재",
|
||||||
|
"wallets_empty_hint": "폴더를 스캔하여 다른 지갑 찾기",
|
||||||
"wallets_exists": "같은 이름의 지갑이 이미 존재합니다.",
|
"wallets_exists": "같은 이름의 지갑이 이미 존재합니다.",
|
||||||
"wallets_external_tt": "데이터 디렉터리 밖 — '열기'는 제자리에 링크합니다 (복사 없음).",
|
"wallets_external_tt": "데이터 디렉터리 밖 — '열기'는 제자리에 링크합니다 (복사 없음).",
|
||||||
"wallets_folder_hint": "wallet-*.dat 파일이 있는 폴더 경로",
|
"wallets_folder_hint": "wallet-*.dat 파일이 있는 폴더 경로",
|
||||||
@@ -1356,7 +1667,9 @@
|
|||||||
"wallets_open_failed": "이 지갑을 제자리에서 열 수 없습니다. 데이터 디렉터리와 다른 드라이브에 있을 가능성이 높습니다 — 같은 드라이브로 옮기세요 (Windows에서는 개발자 모드를 켜면 드라이브 간 링크도 가능합니다).",
|
"wallets_open_failed": "이 지갑을 제자리에서 열 수 없습니다. 데이터 디렉터리와 다른 드라이브에 있을 가능성이 높습니다 — 같은 드라이브로 옮기세요 (Windows에서는 개발자 모드를 켜면 드라이브 간 링크도 가능합니다).",
|
||||||
"wallets_open_folder": "폴더 위치 열기",
|
"wallets_open_folder": "폴더 위치 열기",
|
||||||
"wallets_open_inplace_tt": "이 지갑을 있는 자리에서 엽니다 — 데이터 디렉터리에 링크 (복사 없음)",
|
"wallets_open_inplace_tt": "이 지갑을 있는 자리에서 엽니다 — 데이터 디렉터리에 링크 (복사 없음)",
|
||||||
|
"wallets_remove_folder": "이 폴더 스캔 중지",
|
||||||
"wallets_reveal": "폴더 열기",
|
"wallets_reveal": "폴더 열기",
|
||||||
|
"wallets_scanned_folders": "스캔한 폴더:",
|
||||||
"wallets_sort_addresses": "주소",
|
"wallets_sort_addresses": "주소",
|
||||||
"wallets_sort_asc": "오름차순 (오래된/적은/작은 순)",
|
"wallets_sort_asc": "오름차순 (오래된/적은/작은 순)",
|
||||||
"wallets_sort_by": "정렬:",
|
"wallets_sort_by": "정렬:",
|
||||||
|
|||||||
325
res/lang/pt.json
325
res/lang/pt.json
@@ -30,6 +30,7 @@
|
|||||||
"address_book_added": "Endereço adicionado ao livro",
|
"address_book_added": "Endereço adicionado ao livro",
|
||||||
"address_book_confirm_delete": "Confirmar exclusão?",
|
"address_book_confirm_delete": "Confirmar exclusão?",
|
||||||
"address_book_count": "%zu endereços salvos",
|
"address_book_count": "%zu endereços salvos",
|
||||||
|
"address_book_count_one": "%zu endereço salvo",
|
||||||
"address_book_deleted": "Entrada excluída",
|
"address_book_deleted": "Entrada excluída",
|
||||||
"address_book_edit": "Editar Endereço",
|
"address_book_edit": "Editar Endereço",
|
||||||
"address_book_empty": "Nenhum endereço salvo. Clique em 'Adicionar Novo' para criar um.",
|
"address_book_empty": "Nenhum endereço salvo. Clique em 'Adicionar Novo' para criar um.",
|
||||||
@@ -53,8 +54,20 @@
|
|||||||
"amount_details": "DETALHES DO VALOR",
|
"amount_details": "DETALHES DO VALOR",
|
||||||
"amount_exceeds_balance": "Valor excede o saldo",
|
"amount_exceeds_balance": "Valor excede o saldo",
|
||||||
"amount_label": "Valor:",
|
"amount_label": "Valor:",
|
||||||
|
"animate_avatars": "Animar avatares",
|
||||||
"appearance": "APARÊNCIA",
|
"appearance": "APARÊNCIA",
|
||||||
"auto_shield": "Auto-blindar mineração",
|
"auto_shield": "Auto-blindar mineração",
|
||||||
|
"av_intro": "Softwares de mineração costumam ser sinalizados como potencialmente indesejados. Siga estes passos para habilitar a mineração em pool:",
|
||||||
|
"av_open_security": "Abrir Segurança do Windows",
|
||||||
|
"av_step1": "Passo 1: Adicionar exclusão",
|
||||||
|
"av_step1_b1": "Abra Segurança do Windows > Proteção contra vírus e ameaças",
|
||||||
|
"av_step1_b2": "Clique em Gerenciar configurações > Exclusões > Adicionar ou remover",
|
||||||
|
"av_step1_b3": "Adicione a pasta: %APPDATA%\\ObsidianDragon\\",
|
||||||
|
"av_step2": "Passo 2: Restaurar da quarentena (se necessário)",
|
||||||
|
"av_step2_b1": "Segurança do Windows > Histórico de proteção",
|
||||||
|
"av_step2_b2": "Encontre xmrig.exe e clique em Restaurar",
|
||||||
|
"av_step3": "Passo 3: Reinicie a carteira e tente novamente",
|
||||||
|
"av_title": "Windows Defender bloqueou o minerador",
|
||||||
"available": "Disponível",
|
"available": "Disponível",
|
||||||
"backup_backing_up": "Fazendo backup...",
|
"backup_backing_up": "Fazendo backup...",
|
||||||
"backup_create": "Criar Backup",
|
"backup_create": "Criar Backup",
|
||||||
@@ -62,6 +75,7 @@
|
|||||||
"backup_data": "BACKUP & DADOS",
|
"backup_data": "BACKUP & DADOS",
|
||||||
"backup_description": "Crie um backup do seu arquivo wallet.dat. Este arquivo contém todas as suas chaves privadas e histórico de transações. Guarde o backup em um local seguro.",
|
"backup_description": "Crie um backup do seu arquivo wallet.dat. Este arquivo contém todas as suas chaves privadas e histórico de transações. Guarde o backup em um local seguro.",
|
||||||
"backup_destination": "Destino do backup:",
|
"backup_destination": "Destino do backup:",
|
||||||
|
"backup_overwrite_confirm": "Já existe um arquivo ali — salve novamente para sobrescrevê-lo.",
|
||||||
"backup_source": "Origem: %s",
|
"backup_source": "Origem: %s",
|
||||||
"backup_tip_external": "Armazene backups em unidades externas ou armazenamento em nuvem",
|
"backup_tip_external": "Armazene backups em unidades externas ou armazenamento em nuvem",
|
||||||
"backup_tip_multiple": "Crie múltiplos backups em diferentes locais",
|
"backup_tip_multiple": "Crie múltiplos backups em diferentes locais",
|
||||||
@@ -70,6 +84,7 @@
|
|||||||
"backup_title": "Backup da Carteira",
|
"backup_title": "Backup da Carteira",
|
||||||
"backup_wallet": "Fazer Backup da Carteira...",
|
"backup_wallet": "Fazer Backup da Carteira...",
|
||||||
"backup_wallet_not_found": "Aviso: wallet.dat não encontrado no local esperado",
|
"backup_wallet_not_found": "Aviso: wallet.dat não encontrado no local esperado",
|
||||||
|
"backup_warn": "Este arquivo contém todas as suas chaves privadas — guarde-o em local seguro.",
|
||||||
"balance": "Saldo",
|
"balance": "Saldo",
|
||||||
"balance_history_collecting": "Histórico de saldo — coletando dados...",
|
"balance_history_collecting": "Histórico de saldo — coletando dados...",
|
||||||
"balance_layout": "Layout do Saldo",
|
"balance_layout": "Layout do Saldo",
|
||||||
@@ -114,28 +129,105 @@
|
|||||||
"bootstrap_wallet_protected": "(wallet.dat está protegido)",
|
"bootstrap_wallet_protected": "(wallet.dat está protegido)",
|
||||||
"bootstrap_warning": "Os dados de blocos existentes (blocks, chainstate, notarizations) serão excluídos e substituídos. Seu wallet.dat NÃO será modificado ou excluído.",
|
"bootstrap_warning": "Os dados de blocos existentes (blocks, chainstate, notarizations) serão excluídos e substituídos. Seu wallet.dat NÃO será modificado ou excluído.",
|
||||||
"cancel": "Cancelar",
|
"cancel": "Cancelar",
|
||||||
|
"change_pass_confirm": "Confirmar nova:",
|
||||||
|
"change_pass_current": "Senha atual:",
|
||||||
|
"change_pass_new": "Nova senha:",
|
||||||
|
"change_pass_title": "Alterar senha",
|
||||||
"characters": "caracteres",
|
"characters": "caracteres",
|
||||||
"chat": "Chat",
|
"chat": "Chat",
|
||||||
|
"chat_accent_amber": "Âmbar",
|
||||||
|
"chat_accent_blue": "Azul",
|
||||||
|
"chat_accent_green": "Verde",
|
||||||
|
"chat_accent_pink": "Rosa",
|
||||||
|
"chat_accent_purple": "Roxo",
|
||||||
|
"chat_accent_theme": "Tema",
|
||||||
|
"chat_add_contact": "Adicionar contato",
|
||||||
|
"chat_awaiting_key": "Aguardando resposta",
|
||||||
|
"chat_bubble_minimal": "Mínimo",
|
||||||
|
"chat_bubble_rounded": "Arredondado",
|
||||||
|
"chat_bubble_square": "Quadrado",
|
||||||
|
"chat_buffer_loading": "Buffer de chat: …",
|
||||||
|
"chat_buffer_preparing": "Buffer de chat: preparando %d/%d…",
|
||||||
|
"chat_buffer_ready": "Buffer de chat: %d/%d prontos",
|
||||||
|
"chat_buffer_sending": "Chat: enviando %d mensagens…",
|
||||||
|
"chat_buffer_sending_one": "Chat: enviando %d mensagem…",
|
||||||
"chat_cancel": "Cancelar",
|
"chat_cancel": "Cancelar",
|
||||||
|
"chat_contact_added": "Contato adicionado — renomeie em Contatos",
|
||||||
"chat_contact_request": "solicitação de contato",
|
"chat_contact_request": "solicitação de contato",
|
||||||
|
"chat_copy_address_tip": "Clique para copiar o endereço",
|
||||||
|
"chat_density_comfortable": "Confortável",
|
||||||
|
"chat_density_compact": "Compacta",
|
||||||
|
"chat_emoji_color": "Colorido",
|
||||||
|
"chat_emoji_mono": "Monocromático",
|
||||||
|
"chat_emoji_search": "Pesquisar emoji",
|
||||||
"chat_empty_hint": "Nenhuma conversa ainda. As mensagens que você receber aparecerão aqui.",
|
"chat_empty_hint": "Nenhuma conversa ainda. As mensagens que você receber aparecerão aqui.",
|
||||||
|
"chat_empty_start": "Inicie uma com \"Nova conversa\".",
|
||||||
|
"chat_empty_title": "Ainda não há conversas",
|
||||||
|
"chat_export": "Exportar conversa…",
|
||||||
|
"chat_export_done": "Conversa exportada",
|
||||||
|
"chat_export_failed": "Não foi possível gravar o arquivo de exportação.",
|
||||||
|
"chat_export_warn": "Salva as mensagens descriptografadas como texto simples. Guarde o arquivo com segurança.",
|
||||||
|
"chat_filter": "Chat",
|
||||||
|
"chat_hidden_toast": "Conversa ocultada — uma nova mensagem a traz de volta",
|
||||||
|
"chat_hide": "Ocultar",
|
||||||
|
"chat_hide_hidden": "Ocultar ocultas",
|
||||||
|
"chat_jump_latest": "Recentes",
|
||||||
|
"chat_len_over": "Mensagem muito longa",
|
||||||
"chat_locked_hint": "Desbloqueie sua carteira para carregar suas conversas.",
|
"chat_locked_hint": "Desbloqueie sua carteira para carregar suas conversas.",
|
||||||
"chat_new_button": "Nova conversa",
|
"chat_mute": "Silenciar",
|
||||||
|
"chat_new_button": "Novo chat",
|
||||||
"chat_new_message": "Mensagem",
|
"chat_new_message": "Mensagem",
|
||||||
|
"chat_new_message_toast": "Nova mensagem de chat criptografada",
|
||||||
"chat_new_send": "Enviar solicitação",
|
"chat_new_send": "Enviar solicitação",
|
||||||
"chat_new_title": "Nova conversa",
|
"chat_new_title": "Novo chat",
|
||||||
"chat_new_zaddr": "Endereço-z do destinatário",
|
"chat_new_zaddr": "Endereço-z do destinatário",
|
||||||
|
"chat_no_matches": "Nenhuma conversa corresponde à sua pesquisa.",
|
||||||
|
"chat_no_z_contacts": "Ainda não há contatos com endereço blindado",
|
||||||
|
"chat_opt_bubble_accent": "Cor do balão",
|
||||||
|
"chat_opt_bubble_style": "Estilo do balão",
|
||||||
|
"chat_opt_density": "Densidade das mensagens",
|
||||||
|
"chat_opt_emoji": "Estilo de emoji",
|
||||||
|
"chat_opt_enter_sends": "Enter envia a mensagem",
|
||||||
|
"chat_opt_font_size": "Tamanho do texto",
|
||||||
|
"chat_opt_global_clock": "Formato de relógio global",
|
||||||
|
"chat_opt_poll": "Taxa de atualização",
|
||||||
|
"chat_opt_timestamp": "Carimbos de data/hora",
|
||||||
|
"chat_pick_contact": "Escolher dos contatos…",
|
||||||
|
"chat_rename": "Renomear contato",
|
||||||
|
"chat_rename_hint": "Nome do contato",
|
||||||
|
"chat_renamed": "Contato renomeado",
|
||||||
|
"chat_retry": "Tentar novamente",
|
||||||
|
"chat_search": "Pesquisar conversas",
|
||||||
|
"chat_sec_appearance": "APARÊNCIA",
|
||||||
|
"chat_sec_messaging": "MENSAGENS",
|
||||||
"chat_select_hint": "Selecione uma conversa para visualizá-la.",
|
"chat_select_hint": "Selecione uma conversa para visualizá-la.",
|
||||||
"chat_send": "Enviar",
|
"chat_send": "Enviar",
|
||||||
"chat_send_failed": "não enviada",
|
"chat_send_failed": "não enviada",
|
||||||
|
"chat_sending": "enviando…",
|
||||||
|
"chat_settings_done": "Concluído",
|
||||||
|
"chat_settings_section": "CHAT E CONTATOS",
|
||||||
|
"chat_settings_tip": "Personalizar chat",
|
||||||
|
"chat_settings_title": "Configurações de chat",
|
||||||
|
"chat_show_hidden": "Ver ocultas",
|
||||||
|
"chat_time_now": "agora",
|
||||||
"chat_toast_compose_failed": "Não foi possível compor a mensagem (muito longa?).",
|
"chat_toast_compose_failed": "Não foi possível compor a mensagem (muito longa?).",
|
||||||
"chat_toast_lite_busy": "Já há um envio em andamento, ou nenhuma carteira está aberta.",
|
"chat_toast_lite_busy": "Já há um envio em andamento, ou nenhuma carteira está aberta.",
|
||||||
|
"chat_toast_need_funds": "É necessário um pequeno saldo blindado para enviar chats (para cobrir a taxa).",
|
||||||
"chat_toast_no_zaddr": "Nenhum endereço-z disponível para enviar o chat.",
|
"chat_toast_no_zaddr": "Nenhum endereço-z disponível para enviar o chat.",
|
||||||
"chat_toast_not_connected": "Não conectado — mensagem de chat não enviada.",
|
"chat_toast_not_connected": "Não conectado — mensagem de chat não enviada.",
|
||||||
"chat_toast_request_compose_failed": "Não foi possível compor a solicitação de contato (endereço / texto inválido?).",
|
"chat_toast_request_compose_failed": "Não foi possível compor a solicitação de contato (endereço / texto inválido?).",
|
||||||
"chat_toast_request_queued": "Solicitação de contato na fila.",
|
"chat_toast_request_queued": "Solicitação de contato na fila.",
|
||||||
"chat_toast_waiting_reply": "Aguardando a resposta do contato antes que você possa enviar mensagens a ele.",
|
"chat_toast_waiting_reply": "Aguardando a resposta do contato antes que você possa enviar mensagens a ele.",
|
||||||
|
"chat_today": "Hoje",
|
||||||
|
"chat_ts_12h": "12 horas",
|
||||||
|
"chat_ts_24h": "24 horas",
|
||||||
|
"chat_ts_global": "Seguir global",
|
||||||
|
"chat_ts_global_short": "Global",
|
||||||
|
"chat_unhide": "Mostrar",
|
||||||
|
"chat_unmute": "Reativar som",
|
||||||
|
"chat_verify_key": "Chave de identidade — compare para verificar",
|
||||||
"chat_waiting_reply": "Aguardando a resposta deste contato — você poderá enviar mensagens assim que ele responder.",
|
"chat_waiting_reply": "Aguardando a resposta deste contato — você poderá enviar mensagens assim que ele responder.",
|
||||||
|
"chat_yesterday": "Ontem",
|
||||||
"chat_you": "Você",
|
"chat_you": "Você",
|
||||||
"choose_icon": "Escolher Ícone",
|
"choose_icon": "Escolher Ícone",
|
||||||
"clear": "Limpar",
|
"clear": "Limpar",
|
||||||
@@ -147,6 +239,7 @@
|
|||||||
"click_copy_address": "Clique para copiar o endereço",
|
"click_copy_address": "Clique para copiar o endereço",
|
||||||
"click_copy_uri": "Clique para copiar a URI",
|
"click_copy_uri": "Clique para copiar a URI",
|
||||||
"click_to_copy": "Clique para copiar",
|
"click_to_copy": "Clique para copiar",
|
||||||
|
"clock_format": "Formato de hora",
|
||||||
"close": "Fechar",
|
"close": "Fechar",
|
||||||
"conf_count": "%d conf.",
|
"conf_count": "%d conf.",
|
||||||
"confirm_and_send": "Confirmar & Enviar",
|
"confirm_and_send": "Confirmar & Enviar",
|
||||||
@@ -180,10 +273,24 @@
|
|||||||
"connected_peers": "Pares Conectados",
|
"connected_peers": "Pares Conectados",
|
||||||
"connecting": "Conectando...",
|
"connecting": "Conectando...",
|
||||||
"console": "Console",
|
"console": "Console",
|
||||||
|
"console_accents": "Destaques de cor",
|
||||||
"console_app": "App",
|
"console_app": "App",
|
||||||
"console_auto_scroll": "Rolagem automática",
|
"console_auto_scroll": "Rolagem automática",
|
||||||
"console_available_commands": "Comandos disponíveis:",
|
"console_available_commands": "Comandos disponíveis:",
|
||||||
|
"console_backend_reference": "Referência de Comandos do Backend",
|
||||||
|
"console_backend_unavailable": "Sem backend",
|
||||||
"console_capturing_output": "Capturando saída do daemon...",
|
"console_capturing_output": "Capturando saída do daemon...",
|
||||||
|
"console_cat_advanced": "Avançado",
|
||||||
|
"console_cat_blockchain": "Blockchain",
|
||||||
|
"console_cat_control": "Controle",
|
||||||
|
"console_cat_keys": "Chaves e segurança",
|
||||||
|
"console_cat_mining": "Mineração",
|
||||||
|
"console_cat_network": "Rede",
|
||||||
|
"console_cat_raw_transactions": "Transações brutas",
|
||||||
|
"console_cat_send": "Enviar",
|
||||||
|
"console_cat_sync": "Sincronização",
|
||||||
|
"console_cat_utility": "Utilitários",
|
||||||
|
"console_cat_wallet": "Carteira",
|
||||||
"console_clear": "Limpar",
|
"console_clear": "Limpar",
|
||||||
"console_clear_console": "Limpar Console",
|
"console_clear_console": "Limpar Console",
|
||||||
"console_cleared": "Console limpo",
|
"console_cleared": "Console limpo",
|
||||||
@@ -215,18 +322,37 @@
|
|||||||
"console_help_help": " help - Mostrar esta mensagem de ajuda",
|
"console_help_help": " help - Mostrar esta mensagem de ajuda",
|
||||||
"console_help_setgenerate": " setgenerate - Controlar mineração",
|
"console_help_setgenerate": " setgenerate - Controlar mineração",
|
||||||
"console_help_stop": " stop - Parar o daemon",
|
"console_help_stop": " stop - Parar o daemon",
|
||||||
|
"console_last_error": "Último erro:",
|
||||||
"console_line_count": "%zu linhas",
|
"console_line_count": "%zu linhas",
|
||||||
"console_matches": "correspondências",
|
"console_matches": "correspondências",
|
||||||
"console_new_lines": "%d novas linhas",
|
"console_new_lines": "%d novas linhas",
|
||||||
"console_no_daemon": "Sem daemon",
|
"console_no_daemon": "Sem daemon",
|
||||||
|
"console_no_output": "(sem saída)",
|
||||||
"console_not_connected": "Erro: Não conectado ao daemon",
|
"console_not_connected": "Erro: Não conectado ao daemon",
|
||||||
|
"console_not_connected_lite": "Erro: Nenhuma carteira aberta",
|
||||||
"console_quit_note": "'quit'/'exit' não são necessários aqui — basta fechar a janela.",
|
"console_quit_note": "'quit'/'exit' não são necessários aqui — basta fechar a janela.",
|
||||||
|
"console_ref_builds": "Gera",
|
||||||
|
"console_ref_cancel": "Cancelar",
|
||||||
|
"console_ref_destructive": "Sensível",
|
||||||
|
"console_ref_example": "Exemplo",
|
||||||
|
"console_ref_insert": "Inserir no console",
|
||||||
|
"console_ref_insert_run": "Inserir e executar",
|
||||||
|
"console_ref_no_match": "Nenhum comando corresponde.",
|
||||||
|
"console_ref_no_params": "Não requer parâmetros.",
|
||||||
|
"console_ref_optional": "opcional",
|
||||||
|
"console_ref_parameters": "Parâmetros",
|
||||||
|
"console_ref_run": "Executar",
|
||||||
|
"console_ref_run_confirm": "Executar %s agora? Este é um comando com consequências.",
|
||||||
|
"console_ref_search_hint": "Pesquisar por nome ou tarefa…",
|
||||||
|
"console_ref_select_hint": "Selecione um comando para ver o que ele faz.",
|
||||||
|
"console_ref_value": "valor",
|
||||||
"console_rpc_reference": "Referência de Comandos RPC",
|
"console_rpc_reference": "Referência de Comandos RPC",
|
||||||
"console_rpc_trace": "RPC",
|
"console_rpc_trace": "RPC",
|
||||||
"console_scanline": "Scanline do console",
|
"console_scanline": "Scanline do console",
|
||||||
"console_search_commands": "Pesquisar comandos...",
|
"console_search_commands": "Pesquisar comandos...",
|
||||||
"console_select_all": "Selecionar Tudo",
|
"console_select_all": "Selecionar Tudo",
|
||||||
"console_show_app_output": "Mostrar linhas do log da carteira [app]",
|
"console_show_app_output": "Mostrar linhas do log da carteira [app]",
|
||||||
|
"console_show_backend_ref": "Mostrar referência de comandos do backend",
|
||||||
"console_show_daemon_output": "Mostrar saída do daemon",
|
"console_show_daemon_output": "Mostrar saída do daemon",
|
||||||
"console_show_errors_only": "Mostrar apenas erros",
|
"console_show_errors_only": "Mostrar apenas erros",
|
||||||
"console_show_rpc_ref": "Mostrar referência de comandos RPC",
|
"console_show_rpc_ref": "Mostrar referência de comandos RPC",
|
||||||
@@ -239,17 +365,43 @@
|
|||||||
"console_status_stopped": "Parado",
|
"console_status_stopped": "Parado",
|
||||||
"console_status_stopping": "Parando",
|
"console_status_stopping": "Parando",
|
||||||
"console_status_unknown": "Desconhecido",
|
"console_status_unknown": "Desconhecido",
|
||||||
|
"console_stop_confirm_node": "'stop' irá desligar o nó e desconectar a carteira. Digite 'stop' novamente para confirmar.",
|
||||||
"console_tab_completion": "Tab para completar",
|
"console_tab_completion": "Tab para completar",
|
||||||
|
"console_text_colors": "Cores do texto",
|
||||||
|
"console_toggle_accents": "Alternar destaques de cor das linhas",
|
||||||
|
"console_toggle_text_color": "Alternar cores do texto das linhas",
|
||||||
"console_type_help": "Digite 'help' para comandos disponíveis",
|
"console_type_help": "Digite 'help' para comandos disponíveis",
|
||||||
"console_welcome": "Bem-vindo ao Console ObsidianDragon",
|
"console_welcome": "Bem-vindo ao Console ObsidianDragon",
|
||||||
"console_zoom_in": "Aumentar zoom",
|
"console_zoom_in": "Aumentar zoom",
|
||||||
"console_zoom_out": "Diminuir zoom",
|
"console_zoom_out": "Diminuir zoom",
|
||||||
|
"contact_avatar": "AVATAR",
|
||||||
|
"contact_avatar_bad_image": "Não foi possível carregar essa imagem.",
|
||||||
|
"contact_avatar_badge": "Selo",
|
||||||
|
"contact_avatar_badge_hint": "O selo é escolhido automaticamente conforme o tipo de endereço.",
|
||||||
|
"contact_avatar_choose": "Escolher imagem…",
|
||||||
|
"contact_avatar_copy_failed": "Não foi possível copiar essa imagem.",
|
||||||
|
"contact_avatar_icon": "Ícone",
|
||||||
|
"contact_avatar_image": "Imagem",
|
||||||
|
"contact_avatar_image_hint": "A imagem é copiada para o app para continuar disponível se o original for movido.",
|
||||||
|
"contact_avatar_remove": "Remover",
|
||||||
|
"contact_avatar_shielded": "Blindado",
|
||||||
|
"contact_avatar_transparent": "Transparente",
|
||||||
"contact_global": "Mostrar em todas as carteiras (contato global)",
|
"contact_global": "Mostrar em todas as carteiras (contato global)",
|
||||||
"contact_global_badge_tt": "Contato global — visível em todas as carteiras",
|
"contact_global_badge_tt": "Contato global — visível em todas as carteiras",
|
||||||
"contact_global_tt": "Ativado: este contato permanece visível em qualquer carteira que você carregar. Desativado: ele pertence apenas à carteira atual.",
|
"contact_global_tt": "Ativado: este contato permanece visível em qualquer carteira que você carregar. Desativado: ele pertence apenas à carteira atual.",
|
||||||
|
"contact_preview_addr": "O endereço aparecerá aqui",
|
||||||
|
"contact_preview_name": "Nome do contato",
|
||||||
|
"contact_wallet_loading": "A carteira ainda está carregando — marque “Mostrar em todas as carteiras” ou tente novamente em um momento.",
|
||||||
"contacts": "Contatos",
|
"contacts": "Contatos",
|
||||||
|
"contacts_avatar_shape": "Forma do avatar",
|
||||||
|
"contacts_list_scale": "Escala da lista",
|
||||||
"contacts_search_no_match": "Nenhum contato correspondente",
|
"contacts_search_no_match": "Nenhum contato correspondente",
|
||||||
"contacts_search_placeholder": "Pesquisar contatos...",
|
"contacts_search_placeholder": "Pesquisar contatos...",
|
||||||
|
"contacts_settings_tip": "Personalizar contatos",
|
||||||
|
"contacts_settings_title": "Configurações de contatos",
|
||||||
|
"contacts_shape_circle": "Círculo",
|
||||||
|
"contacts_shape_square": "Quadrado",
|
||||||
|
"contacts_shape_tab": "Aba",
|
||||||
"copied": "Copiado!",
|
"copied": "Copiado!",
|
||||||
"copy": "Copiar",
|
"copy": "Copiar",
|
||||||
"copy_address": "Copiar Endereço Completo",
|
"copy_address": "Copiar Endereço Completo",
|
||||||
@@ -302,6 +454,19 @@
|
|||||||
"date": "Data",
|
"date": "Data",
|
||||||
"date_label": "Data:",
|
"date_label": "Data:",
|
||||||
"debug_logging": "REGISTRO DE DEPURAÇÃO",
|
"debug_logging": "REGISTRO DE DEPURAÇÃO",
|
||||||
|
"decrypt_desc": "A carteira será exportada, o daemon reiniciado com uma nova carteira sem criptografia e todas as chaves reimportadas. Isso pode levar alguns minutos, dependendo do tamanho da carteira.",
|
||||||
|
"decrypt_error_title": "Falha na descriptografia",
|
||||||
|
"decrypt_step_backup": "Fazendo backup da carteira criptografada",
|
||||||
|
"decrypt_step_export": "Exportando as chaves da carteira",
|
||||||
|
"decrypt_step_restart": "Reiniciando o daemon",
|
||||||
|
"decrypt_step_stop": "Parando o daemon",
|
||||||
|
"decrypt_step_unlock": "Desbloqueando a carteira",
|
||||||
|
"decrypt_success_desc": "Sua carteira agora está sem criptografia. Um backup da carteira criptografada foi salvo como wallet.dat.encrypted.bak no seu diretório de dados.",
|
||||||
|
"decrypt_success_title": "Carteira descriptografada com sucesso!",
|
||||||
|
"decrypt_title": "Remover criptografia da carteira",
|
||||||
|
"decrypt_wait_general": "Aguarde. O daemon está exportando chaves, reiniciando e reimportando. Isso pode levar alguns minutos.",
|
||||||
|
"decrypt_wait_restart": "Aguardando o daemon terminar de iniciar...",
|
||||||
|
"decrypt_warning": "Isso removerá a criptografia da sua carteira. Suas chaves privadas serão armazenadas sem proteção no disco.",
|
||||||
"delete": "Excluir",
|
"delete": "Excluir",
|
||||||
"delete_blockchain": "Excluir Blockchain",
|
"delete_blockchain": "Excluir Blockchain",
|
||||||
"delete_blockchain_confirm": "Excluir e Ressincronizar",
|
"delete_blockchain_confirm": "Excluir e Ressincronizar",
|
||||||
@@ -314,6 +479,15 @@
|
|||||||
"download_bootstrap": "Baixar Bootstrap",
|
"download_bootstrap": "Baixar Bootstrap",
|
||||||
"dragonx_green": "DragonX (Verde)",
|
"dragonx_green": "DragonX (Verde)",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
|
"enc_confirm": "Confirmar:",
|
||||||
|
"enc_desc": "Criptografar sua carteira protege suas chaves privadas com uma senha. Após a criptografia, o daemon será reiniciado.",
|
||||||
|
"enc_encrypting": "Criptografando a carteira...",
|
||||||
|
"enc_pin_desc": "Um PIN de 4 a 8 dígitos permite desbloquear sua carteira sem digitar a senha completa toda vez.",
|
||||||
|
"enc_pin_set_ok": "PIN definido com sucesso",
|
||||||
|
"enc_pin_skipped": "PIN ignorado. Você pode definir um depois em Configurações.",
|
||||||
|
"enc_pin_vault_fail": "Falha ao criar o cofre do PIN",
|
||||||
|
"enc_success": "Carteira criptografada com sucesso!",
|
||||||
|
"enc_wait": "Aguarde, não feche o aplicativo.",
|
||||||
"error": "Erro",
|
"error": "Erro",
|
||||||
"error_format": "Erro: %s",
|
"error_format": "Erro: %s",
|
||||||
"est_time_to_block": "Tempo Est. por Bloco",
|
"est_time_to_block": "Tempo Est. por Bloco",
|
||||||
@@ -333,23 +507,34 @@
|
|||||||
"explorer_mempool": "Mempool",
|
"explorer_mempool": "Mempool",
|
||||||
"explorer_mempool_size": "Tamanho",
|
"explorer_mempool_size": "Tamanho",
|
||||||
"explorer_mempool_txs": "Transações",
|
"explorer_mempool_txs": "Transações",
|
||||||
|
"explorer_next_block": "Próximo bloco",
|
||||||
"explorer_no_results": "Nenhum bloco em cache correspondente",
|
"explorer_no_results": "Nenhum bloco em cache correspondente",
|
||||||
"explorer_not_connected": "Não conectado ao daemon — não é possível procurar um bloco ou hash de transação",
|
"explorer_not_connected": "Não conectado ao daemon — não é possível procurar um bloco ou hash de transação",
|
||||||
|
"explorer_prev_block": "Bloco anterior",
|
||||||
"explorer_recent_blocks": "Blocos Recentes",
|
"explorer_recent_blocks": "Blocos Recentes",
|
||||||
"explorer_search": "Pesquisar",
|
"explorer_search": "Pesquisar",
|
||||||
"explorer_section": "EXPLORADOR",
|
"explorer_section": "EXPLORADOR",
|
||||||
"explorer_tx_outputs": "Saídas",
|
"explorer_tx_outputs": "Saídas",
|
||||||
"explorer_tx_size": "Tamanho",
|
"explorer_tx_size": "Tamanho",
|
||||||
|
"explorer_txid": "TxID:",
|
||||||
"export": "Exportar",
|
"export": "Exportar",
|
||||||
"export_csv": "Exportar CSV",
|
"export_csv": "Exportar CSV",
|
||||||
"export_keys_btn": "Exportar Chaves",
|
"export_keys_btn": "Exportar Chaves",
|
||||||
"export_keys_danger": "PERIGO: Isto exportará TODAS as chaves privadas da sua carteira! Qualquer pessoa com acesso a este arquivo pode roubar seus fundos. Guarde com segurança e exclua após o uso.",
|
"export_keys_danger": "PERIGO: Isto exportará TODAS as chaves privadas da sua carteira! Qualquer pessoa com acesso a este arquivo pode roubar seus fundos. Guarde com segurança e exclua após o uso.",
|
||||||
"export_keys_include_t": "Incluir endereços T (transparentes)",
|
"export_keys_include_t": "Incluir endereços T (transparentes)",
|
||||||
"export_keys_include_z": "Incluir endereços Z (blindados)",
|
"export_keys_include_z": "Incluir endereços Z (blindados)",
|
||||||
|
"export_keys_none_addrs": "Nenhum endereço para exportar",
|
||||||
|
"export_keys_none_result": "Nenhuma chave exportada (0 de %d) — desbloqueie a carteira e tente novamente.",
|
||||||
|
"export_keys_none_toast": "Não foi possível exportar chaves — a carteira está desbloqueada?",
|
||||||
|
"export_keys_not_connected": "Não conectado ao daemon",
|
||||||
"export_keys_options": "Opções de exportação:",
|
"export_keys_options": "Opções de exportação:",
|
||||||
|
"export_keys_partial": "Exportadas %d de %d chaves — incompleto (algumas sem chave de gasto, ou a carteira está bloqueada).",
|
||||||
|
"export_keys_partial_toast": "Exportação parcial: %d de %d chaves",
|
||||||
"export_keys_progress": "Exportando %d/%d...",
|
"export_keys_progress": "Exportando %d/%d...",
|
||||||
|
"export_keys_select_type": "Selecione ao menos um tipo de endereço",
|
||||||
"export_keys_success": "Chaves exportadas com sucesso",
|
"export_keys_success": "Chaves exportadas com sucesso",
|
||||||
"export_keys_title": "Exportar Todas as Chaves Privadas",
|
"export_keys_title": "Exportar Todas as Chaves Privadas",
|
||||||
|
"export_keys_write_fail": "Falha ao gravar o arquivo de chaves.",
|
||||||
"export_private_key": "Exportar Chave Privada",
|
"export_private_key": "Exportar Chave Privada",
|
||||||
"export_tx_count": "Exportar %zu transações para arquivo CSV.",
|
"export_tx_count": "Exportar %zu transações para arquivo CSV.",
|
||||||
"export_tx_file_fail": "Falha ao criar arquivo CSV",
|
"export_tx_file_fail": "Falha ao criar arquivo CSV",
|
||||||
@@ -389,23 +574,50 @@
|
|||||||
"hide_qr": "Ocultar QR",
|
"hide_qr": "Ocultar QR",
|
||||||
"hide_zero_balances": "Ocultar saldos zero",
|
"hide_zero_balances": "Ocultar saldos zero",
|
||||||
"history": "Histórico",
|
"history": "Histórico",
|
||||||
|
"img_picker_count": "%d imagem(ns) nesta pasta",
|
||||||
|
"img_picker_empty": "Esta pasta não tem subpastas nem imagens.",
|
||||||
|
"img_picker_none": "Nenhuma imagem nesta pasta",
|
||||||
|
"img_picker_pictures": "Pasta de imagens",
|
||||||
|
"img_picker_title": "Escolher uma imagem",
|
||||||
|
"img_picker_use": "Usar imagem",
|
||||||
"immature_type": "Imaturo",
|
"immature_type": "Imaturo",
|
||||||
"import": "Importar",
|
"import": "Importar",
|
||||||
|
"import_key_address": "Endereço:",
|
||||||
"import_key_btn": "Importar Chave(s)",
|
"import_key_btn": "Importar Chave(s)",
|
||||||
|
"import_key_done": "Importada. A carteira está reescaneando.",
|
||||||
|
"import_key_field": "Chave",
|
||||||
"import_key_formats": "Formatos de chave suportados:",
|
"import_key_formats": "Formatos de chave suportados:",
|
||||||
"import_key_full_rescan": "(0 = rescan completo)",
|
"import_key_full_rescan": "(0 = rescan completo)",
|
||||||
|
"import_key_import": "Importar",
|
||||||
"import_key_label": "Chave(s) Privada(s):",
|
"import_key_label": "Chave(s) Privada(s):",
|
||||||
|
"import_key_need_node": "Conecte um nó em execução para importar uma chave.",
|
||||||
"import_key_no_valid": "Nenhuma chave válida encontrada na entrada",
|
"import_key_no_valid": "Nenhuma chave válida encontrada na entrada",
|
||||||
"import_key_progress": "Importando %d/%d...",
|
"import_key_progress": "Importando %d/%d...",
|
||||||
"import_key_rescan": "Reescanear blockchain após importação",
|
"import_key_rescan": "Reescanear blockchain após importação",
|
||||||
|
"import_key_rescanning": "Importando e reescaneando — pode levar vários minutos",
|
||||||
|
"import_key_reveal_tip": "Mostrar/ocultar a chave",
|
||||||
"import_key_start_height": "Altura inicial:",
|
"import_key_start_height": "Altura inicial:",
|
||||||
"import_key_success": "Chaves importadas com sucesso",
|
"import_key_success": "Chaves importadas com sucesso",
|
||||||
"import_key_t_format": "Chaves privadas WIF de endereços T",
|
"import_key_t_format": "Chaves privadas WIF de endereços T",
|
||||||
"import_key_title": "Importar Chave Privada",
|
"import_key_title": "Importar Chave Privada",
|
||||||
"import_key_tooltip": "Digite uma ou mais chaves privadas, uma por linha.\nSuporta chaves de z-endereço e t-endereço.\nLinhas começando com # são tratadas como comentários.",
|
"import_key_tooltip": "Digite uma ou mais chaves privadas, uma por linha.\nSuporta chaves de z-endereço e t-endereço.\nLinhas começando com # são tratadas como comentários.",
|
||||||
|
"import_key_type_tkey": "Chave privada transparente",
|
||||||
|
"import_key_type_unknown": "Formato de chave não reconhecido",
|
||||||
|
"import_key_type_zspend": "Chave de gasto blindada",
|
||||||
|
"import_key_type_zview": "Chave de visualização blindada (somente leitura)",
|
||||||
|
"import_key_warn": "Importe apenas uma chave que você possui — ela concede acesso aos seus fundos.",
|
||||||
"import_key_warning": "Aviso: Nunca compartilhe suas chaves privadas! Importar chaves de fontes não confiáveis pode comprometer sua carteira.",
|
"import_key_warning": "Aviso: Nunca compartilhe suas chaves privadas! Importar chaves de fontes não confiáveis pode comprometer sua carteira.",
|
||||||
|
"import_key_wrong_type": "Isto parece uma chave de visualização. Use \"Importar chave de visualização\".",
|
||||||
"import_key_z_format": "Chaves de gasto de z-endereço (secret-extended-key-...)",
|
"import_key_z_format": "Chaves de gasto de z-endereço (secret-extended-key-...)",
|
||||||
"import_private_key": "Importar Chave Privada...",
|
"import_private_key": "Importar Chave Privada...",
|
||||||
|
"import_scan_hint": "0 = reescanear desde o início",
|
||||||
|
"import_scan_label": "Escanear a partir da altura do bloco (opcional)",
|
||||||
|
"import_scan_tip": "altura atual",
|
||||||
|
"import_scan_transparent": "Chaves transparentes sempre reescaneiam totalmente",
|
||||||
|
"import_viewkey_field": "Chave de visualização",
|
||||||
|
"import_viewkey_note": "Somente leitura: uma chave de visualização revela o saldo e as transações de um endereço, mas não pode gastar seus fundos.",
|
||||||
|
"import_viewkey_title": "Importar chave de visualização",
|
||||||
|
"import_viewkey_wrong_type": "Isto parece uma chave de gasto. Use \"Importar Chave Privada\".",
|
||||||
"incorrect_passphrase": "Senha incorreta",
|
"incorrect_passphrase": "Senha incorreta",
|
||||||
"incorrect_pin": "PIN incorreto",
|
"incorrect_pin": "PIN incorreto",
|
||||||
"insufficient_funds": "Fundos insuficientes para este valor mais taxa.",
|
"insufficient_funds": "Fundos insuficientes para este valor mais taxa.",
|
||||||
@@ -414,6 +626,7 @@
|
|||||||
"keep": "Manter",
|
"keep": "Manter",
|
||||||
"keep_daemon": "Manter daemon em execução",
|
"keep_daemon": "Manter daemon em execução",
|
||||||
"key_export_click_retrieve": "Clique para recuperar a chave da sua carteira",
|
"key_export_click_retrieve": "Clique para recuperar a chave da sua carteira",
|
||||||
|
"key_export_failed": "Não foi possível exportar a chave — desbloqueie a carteira (se estiver criptografada) e tente novamente.",
|
||||||
"key_export_fetching": "Buscando chave da carteira...",
|
"key_export_fetching": "Buscando chave da carteira...",
|
||||||
"key_export_private_key": "Chave Privada:",
|
"key_export_private_key": "Chave Privada:",
|
||||||
"key_export_private_warning": "Mantenha esta chave em SEGREDO! Qualquer pessoa com esta chave pode gastar seus fundos. Nunca a compartilhe online ou com terceiros não confiáveis.",
|
"key_export_private_warning": "Mantenha esta chave em SEGREDO! Qualquer pessoa com esta chave pode gastar seus fundos. Nunca a compartilhe online ou com terceiros não confiáveis.",
|
||||||
@@ -431,6 +644,7 @@
|
|||||||
"lite_birthday_backup": "Aniversário: %llu (faça o backup disto também)",
|
"lite_birthday_backup": "Aniversário: %llu (faça o backup disto também)",
|
||||||
"lite_birthday_hint": "Altura do bloco a partir da qual começar a escanear. Deixe 0 se desconhecida (escaneamento completo mais lento).",
|
"lite_birthday_hint": "Altura do bloco a partir da qual começar a escanear. Deixe 0 se desconhecida (escaneamento completo mais lento).",
|
||||||
"lite_birthday_label": "Data de nascimento",
|
"lite_birthday_label": "Data de nascimento",
|
||||||
|
"lite_console_backend_commands": "Comandos do backend:",
|
||||||
"lite_console_help_passthrough": "Qualquer outra entrada é executada como um comando de console da carteira leve.",
|
"lite_console_help_passthrough": "Qualquer outra entrada é executada como um comando de console da carteira leve.",
|
||||||
"lite_copy": "Copiar",
|
"lite_copy": "Copiar",
|
||||||
"lite_could_not_write": "Não foi possível gravar ",
|
"lite_could_not_write": "Não foi possível gravar ",
|
||||||
@@ -448,6 +662,7 @@
|
|||||||
"lite_net_add_url_hint": "https://seu-servidor-lite",
|
"lite_net_add_url_hint": "https://seu-servidor-lite",
|
||||||
"lite_net_checking": "verificando…",
|
"lite_net_checking": "verificando…",
|
||||||
"lite_net_connected": "Conectado",
|
"lite_net_connected": "Conectado",
|
||||||
|
"lite_net_connecting": "Conectando…",
|
||||||
"lite_net_custom": "Personalizado",
|
"lite_net_custom": "Personalizado",
|
||||||
"lite_net_disconnected": "Não conectado",
|
"lite_net_disconnected": "Não conectado",
|
||||||
"lite_net_hidden_section": "Servidores ocultos",
|
"lite_net_hidden_section": "Servidores ocultos",
|
||||||
@@ -534,6 +749,10 @@
|
|||||||
"market_btc_price": "PREÇO BTC",
|
"market_btc_price": "PREÇO BTC",
|
||||||
"market_cap": "Capitalização",
|
"market_cap": "Capitalização",
|
||||||
"market_cap_short": "Cap.",
|
"market_cap_short": "Cap.",
|
||||||
|
"market_chart_loading": "Carregando histórico de preços",
|
||||||
|
"market_col_name": "Nome",
|
||||||
|
"market_col_trend": "Tendência",
|
||||||
|
"market_col_value": "Valor",
|
||||||
"market_iv_1d": "1D",
|
"market_iv_1d": "1D",
|
||||||
"market_iv_1h": "1H",
|
"market_iv_1h": "1H",
|
||||||
"market_iv_1m": "1M",
|
"market_iv_1m": "1M",
|
||||||
@@ -542,11 +761,18 @@
|
|||||||
"market_no_history": "Nenhum histórico de preços disponível",
|
"market_no_history": "Nenhum histórico de preços disponível",
|
||||||
"market_no_price": "Sem dados de preço",
|
"market_no_price": "Sem dados de preço",
|
||||||
"market_now": "Agora",
|
"market_now": "Agora",
|
||||||
|
"market_opt_chart_style": "Estilo do gráfico",
|
||||||
"market_pct_shielded": "%.0f%% Blindado",
|
"market_pct_shielded": "%.0f%% Blindado",
|
||||||
"market_portfolio": "PORTFÓLIO",
|
"market_portfolio": "PORTFÓLIO",
|
||||||
"market_price_loading": "Carregando dados de preço...",
|
"market_price_loading": "Carregando dados de preço...",
|
||||||
"market_price_unavailable": "Dados de preço indisponíveis",
|
"market_price_unavailable": "Dados de preço indisponíveis",
|
||||||
"market_refresh_price": "Atualizar dados de preço",
|
"market_refresh_price": "Atualizar dados de preço",
|
||||||
|
"market_settings_tip": "Opções de mercado",
|
||||||
|
"market_settings_title": "Configurações de mercado",
|
||||||
|
"market_style_candle": "Mudar para velas",
|
||||||
|
"market_style_candle_label": "Velas",
|
||||||
|
"market_style_line": "Mudar para gráfico de linhas",
|
||||||
|
"market_style_line_label": "Linha",
|
||||||
"market_trade_on": "Negociar no %s",
|
"market_trade_on": "Negociar no %s",
|
||||||
"market_updated": "\\xc2\\xb7 Atualizado %s",
|
"market_updated": "\\xc2\\xb7 Atualizado %s",
|
||||||
"market_vol_short": "Vol",
|
"market_vol_short": "Vol",
|
||||||
@@ -736,6 +962,7 @@
|
|||||||
"output_filename": "Nome do arquivo de saída:",
|
"output_filename": "Nome do arquivo de saída:",
|
||||||
"overview": "Resumo",
|
"overview": "Resumo",
|
||||||
"paste": "Colar",
|
"paste": "Colar",
|
||||||
|
"paste_clip_empty": "A área de transferência está vazia",
|
||||||
"paste_from_clipboard": "Colar da Área de Transferência",
|
"paste_from_clipboard": "Colar da Área de Transferência",
|
||||||
"pay_from": "Pagar de",
|
"pay_from": "Pagar de",
|
||||||
"payment_request": "SOLICITAÇÃO DE PAGAMENTO",
|
"payment_request": "SOLICITAÇÃO DE PAGAMENTO",
|
||||||
@@ -787,7 +1014,14 @@
|
|||||||
"peers_upper": "PARES",
|
"peers_upper": "PARES",
|
||||||
"peers_version": "Versão",
|
"peers_version": "Versão",
|
||||||
"pending": "Pendente",
|
"pending": "Pendente",
|
||||||
|
"pin_change_desc": "Altere seu PIN de desbloqueio. Você precisa do seu PIN atual e de um novo PIN.",
|
||||||
|
"pin_confirm_new_label": "Confirmar novo PIN:",
|
||||||
|
"pin_current_label": "PIN atual:",
|
||||||
|
"pin_new_label": "Novo PIN (4 a 8 dígitos):",
|
||||||
"pin_not_set": "PIN não definido. Use a senha para desbloquear.",
|
"pin_not_set": "PIN não definido. Use a senha para desbloquear.",
|
||||||
|
"pin_remove_desc": "Digite seu PIN atual para confirmar a remoção. Você precisará usar sua senha completa para desbloquear.",
|
||||||
|
"pin_setup_desc": "Defina um PIN de 4 a 8 dígitos para desbloquear a carteira rapidamente. Sua senha da carteira será criptografada com este PIN e armazenada localmente.",
|
||||||
|
"pin_wallet_passphrase": "Senha da carteira:",
|
||||||
"ping": "Ping",
|
"ping": "Ping",
|
||||||
"portfolio_add_entry": "Adicionar entrada",
|
"portfolio_add_entry": "Adicionar entrada",
|
||||||
"portfolio_add_to": "Adicionar ao portfólio",
|
"portfolio_add_to": "Adicionar ao portfólio",
|
||||||
@@ -826,6 +1060,9 @@
|
|||||||
"portfolio_remove_from": "Remover do portfólio",
|
"portfolio_remove_from": "Remover do portfólio",
|
||||||
"portfolio_revert": "Reverter",
|
"portfolio_revert": "Reverter",
|
||||||
"portfolio_save": "Salvar",
|
"portfolio_save": "Salvar",
|
||||||
|
"portfolio_save_need_address": "Adicione pelo menos um endereço para salvar.",
|
||||||
|
"portfolio_save_need_name": "Insira um nome para salvar este grupo.",
|
||||||
|
"portfolio_save_need_price": "Insira um preço manual acima de 0 para salvar.",
|
||||||
"portfolio_search": "Pesquisar endereços…",
|
"portfolio_search": "Pesquisar endereços…",
|
||||||
"portfolio_search_icons": "Pesquisar ícones\\xE2\\x80\\xA6",
|
"portfolio_search_icons": "Pesquisar ícones\\xE2\\x80\\xA6",
|
||||||
"portfolio_select_all": "Todos",
|
"portfolio_select_all": "Todos",
|
||||||
@@ -839,11 +1076,12 @@
|
|||||||
"portfolio_spark_min": "Minuto",
|
"portfolio_spark_min": "Minuto",
|
||||||
"portfolio_spark_month": "Mês",
|
"portfolio_spark_month": "Mês",
|
||||||
"portfolio_spark_week": "Semana",
|
"portfolio_spark_week": "Semana",
|
||||||
"portfolio_style_compact": "Linhas compactas",
|
"portfolio_style_compact": "Tabela",
|
||||||
"portfolio_style_detailed": "Linhas detalhadas",
|
"portfolio_style_detailed": "Cartões",
|
||||||
"portfolio_style_featured": "Linhas em destaque",
|
"portfolio_style_featured": "Destaque",
|
||||||
"portfolio_style_label": "Estilo do portfólio",
|
"portfolio_style_label": "Estilo do portfólio",
|
||||||
"portfolio_untitled": "Sem título",
|
"portfolio_untitled": "Sem título",
|
||||||
|
"portfolio_wallet_loading": "Aguarde a carteira terminar de carregar para adicionar um grupo.",
|
||||||
"price_chart": "Gráfico de Preços",
|
"price_chart": "Gráfico de Preços",
|
||||||
"privacy_great": "Ótima privacidade!",
|
"privacy_great": "Ótima privacidade!",
|
||||||
"privacy_low": "Baixa privacidade — blinde os fundos",
|
"privacy_low": "Baixa privacidade — blinde os fundos",
|
||||||
@@ -973,6 +1211,7 @@
|
|||||||
"send_amount": "Valor",
|
"send_amount": "Valor",
|
||||||
"send_amount_details": "DETALHES DO VALOR",
|
"send_amount_details": "DETALHES DO VALOR",
|
||||||
"send_amount_upper": "VALOR",
|
"send_amount_upper": "VALOR",
|
||||||
|
"send_cannot_send_now": "Não é possível enviar agora — verifique a conexão, a sincronização e o saldo disponível.",
|
||||||
"send_clear_fields": "Limpar todos os campos do formulário?",
|
"send_clear_fields": "Limpar todos os campos do formulário?",
|
||||||
"send_contacts_button": "Escolher dos contatos",
|
"send_contacts_button": "Escolher dos contatos",
|
||||||
"send_copy_error": "Copiar Erro",
|
"send_copy_error": "Copiar Erro",
|
||||||
@@ -1061,7 +1300,8 @@
|
|||||||
"settings_gradient_bg": "Fundo gradiente",
|
"settings_gradient_bg": "Fundo gradiente",
|
||||||
"settings_gradient_desc": "Substituir fundos texturizados por gradientes suaves",
|
"settings_gradient_desc": "Substituir fundos texturizados por gradientes suaves",
|
||||||
"settings_idle_after": "após",
|
"settings_idle_after": "após",
|
||||||
"settings_import_key": "Importar chave...",
|
"settings_import_key": "Importar Chave Privada...",
|
||||||
|
"settings_import_viewkey": "Importar chave de visualização...",
|
||||||
"settings_language_note": "Nota: Alguns textos requerem reinício para atualizar",
|
"settings_language_note": "Nota: Alguns textos requerem reinício para atualizar",
|
||||||
"settings_lock_now": "Bloquear agora",
|
"settings_lock_now": "Bloquear agora",
|
||||||
"settings_locked": "Bloqueado",
|
"settings_locked": "Bloqueado",
|
||||||
@@ -1110,6 +1350,7 @@
|
|||||||
"shield_in_progress": "Operação em andamento...",
|
"shield_in_progress": "Operação em andamento...",
|
||||||
"shield_max_utxos": "Máx. UTXOs por operação",
|
"shield_max_utxos": "Máx. UTXOs por operação",
|
||||||
"shield_merge_done": "Blindagem/fusão concluída!",
|
"shield_merge_done": "Blindagem/fusão concluída!",
|
||||||
|
"shield_no_zaddr_hint": "Ainda não há endereço blindado (z) — crie um primeiro na aba Receber.",
|
||||||
"shield_op_failed": "Falha na operação: ",
|
"shield_op_failed": "Falha na operação: ",
|
||||||
"shield_op_submitted": "Operação enviada: ",
|
"shield_op_submitted": "Operação enviada: ",
|
||||||
"shield_operation_id": "ID da operação: %s",
|
"shield_operation_id": "ID da operação: %s",
|
||||||
@@ -1148,6 +1389,31 @@
|
|||||||
"submitting_transaction": "Enviando transação...",
|
"submitting_transaction": "Enviando transação...",
|
||||||
"success": "Sucesso",
|
"success": "Sucesso",
|
||||||
"summary": "Resumo",
|
"summary": "Resumo",
|
||||||
|
"sweep_button": "Varrer",
|
||||||
|
"sweep_caveat": "Importa a chave para assinar uma transação que move todos os seus fundos para o seu endereço. A chave permanece na carteira com saldo zero.",
|
||||||
|
"sweep_dest_label": "Enviar fundos varridos para",
|
||||||
|
"sweep_dest_new": "Novo endereço blindado (recomendado)",
|
||||||
|
"sweep_done": "Concluído — fundos varridos para o seu endereço.",
|
||||||
|
"sweep_to": "Varrido para:",
|
||||||
|
"sweep_toggle": "Varrer para minha carteira (não manter a chave)",
|
||||||
|
"sweep_tx": "Transação:",
|
||||||
|
"switch_corrupt_body": "Esta carteira parece corrompida — o nó não conseguiu abri-la. Restaure de um backup, recrie-a ou tente repará-la.",
|
||||||
|
"switch_corrupt_repair": "Tentar reparar (salvage)",
|
||||||
|
"switch_progress_background": "Continuar em segundo plano",
|
||||||
|
"switch_progress_default_wallet": "Carteira padrão",
|
||||||
|
"switch_progress_elapsed": "Decorrido",
|
||||||
|
"switch_progress_external_wallet": "Carteira externa",
|
||||||
|
"switch_progress_failed_title": "Falha ao trocar de carteira",
|
||||||
|
"switch_progress_from_label": "de",
|
||||||
|
"switch_progress_hint": "Um desligamento normal pode levar até um minuto.",
|
||||||
|
"switch_progress_reconnecting": "Reconectando",
|
||||||
|
"switch_progress_starting": "Iniciando o nó com a nova carteira",
|
||||||
|
"switch_progress_stopping": "Parando o nó atual",
|
||||||
|
"switch_progress_title": "Trocando de carteira",
|
||||||
|
"switch_stopnode_body": "Trocar de carteira reinicia o nó com a carteira selecionada. O nó em execução será parado e reiniciado com a nova carteira — se você o deixou em execução de propósito, ele volta automaticamente.",
|
||||||
|
"switch_stopnode_confirm": "Parar nó e trocar",
|
||||||
|
"switch_stopnode_title": "Parar o nó em execução?",
|
||||||
|
"switch_stopnode_warn": "Já há um nó em execução que esta carteira não iniciou.",
|
||||||
"syncing": "Sincronizando...",
|
"syncing": "Sincronizando...",
|
||||||
"t_address": "Endereço T",
|
"t_address": "Endereço T",
|
||||||
"t_addresses": "Endereços T",
|
"t_addresses": "Endereços T",
|
||||||
@@ -1183,8 +1449,10 @@
|
|||||||
"transfer_to": "Transferir para:",
|
"transfer_to": "Transferir para:",
|
||||||
"transparent": "Transparente",
|
"transparent": "Transparente",
|
||||||
"transparent_address": "Endereço Transparente",
|
"transparent_address": "Endereço Transparente",
|
||||||
|
"try_again": "Tentar novamente",
|
||||||
"tt_addr_url": "URL base para visualizar endereços em um explorador de blocos",
|
"tt_addr_url": "URL base para visualizar endereços em um explorador de blocos",
|
||||||
"tt_address_book": "Gerenciar endereços salvos para envio rápido",
|
"tt_address_book": "Gerenciar endereços salvos para envio rápido",
|
||||||
|
"tt_animate_avatars": "Reproduz avatares de contato animados (GIF / WebP); desativado mostra só o primeiro quadro",
|
||||||
"tt_auto_lock": "Bloquear carteira após este tempo de inatividade",
|
"tt_auto_lock": "Bloquear carteira após este tempo de inatividade",
|
||||||
"tt_auto_shield": "Mover automaticamente o saldo transparente para endereços blindados para privacidade",
|
"tt_auto_shield": "Mover automaticamente o saldo transparente para endereços blindados para privacidade",
|
||||||
"tt_backup": "Criar um backup do seu wallet.dat",
|
"tt_backup": "Criar um backup do seu wallet.dat",
|
||||||
@@ -1192,10 +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_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",
|
||||||
@@ -1209,10 +1487,34 @@
|
|||||||
"tt_font_scale": "Escalar todo o texto e interface (1.0x = padrão, até 1.5x).",
|
"tt_font_scale": "Escalar todo o texto e interface (1.0x = padrão, até 1.5x).",
|
||||||
"tt_idle_delay": "Quanto tempo esperar antes de iniciar a mineração",
|
"tt_idle_delay": "Quanto tempo esperar antes de iniciar a mineração",
|
||||||
"tt_import_key": "Importar uma chave privada (zkey ou tkey) nesta carteira",
|
"tt_import_key": "Importar uma chave privada (zkey ou tkey) nesta carteira",
|
||||||
|
"tt_import_viewkey": "Importar uma chave de visualização blindada para observar um endereço (somente leitura)",
|
||||||
"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",
|
||||||
@@ -1233,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",
|
||||||
@@ -1257,6 +1564,7 @@
|
|||||||
"tt_website": "Abrir o site do DragonX",
|
"tt_website": "Abrir o site do DragonX",
|
||||||
"tt_window_opacity": "Opacidade do fundo (menor = área de trabalho visível através da janela)",
|
"tt_window_opacity": "Opacidade do fundo (menor = área de trabalho visível através da janela)",
|
||||||
"tt_wizard": "Executar novamente o assistente de configuração inicial\\nO daemon será reiniciado",
|
"tt_wizard": "Executar novamente o assistente de configuração inicial\\nO daemon será reiniciado",
|
||||||
|
"tx_chat_badge": "Mensagem",
|
||||||
"tx_confirmations": "%d confirmações",
|
"tx_confirmations": "%d confirmações",
|
||||||
"tx_details_title": "Detalhes da Transação",
|
"tx_details_title": "Detalhes da Transação",
|
||||||
"tx_from_address": "Endereço de Origem:",
|
"tx_from_address": "Endereço de Origem:",
|
||||||
@@ -1325,6 +1633,8 @@
|
|||||||
"wallets_add_folder_toggle": "+ Procurar carteiras noutra pasta…",
|
"wallets_add_folder_toggle": "+ Procurar carteiras noutra pasta…",
|
||||||
"wallets_badge_encrypted": "Encriptada (protegida por senha)",
|
"wallets_badge_encrypted": "Encriptada (protegida por senha)",
|
||||||
"wallets_badge_encrypted_short": "Encriptada",
|
"wallets_badge_encrypted_short": "Encriptada",
|
||||||
|
"wallets_badge_hd": "Carteira HD — não é possível confirmar a frase-semente sem abri-la",
|
||||||
|
"wallets_badge_hd_short": "Carteira HD",
|
||||||
"wallets_badge_legacy": "Carteira legada (sem frase semente)",
|
"wallets_badge_legacy": "Carteira legada (sem frase semente)",
|
||||||
"wallets_badge_legacy_short": "Legada",
|
"wallets_badge_legacy_short": "Legada",
|
||||||
"wallets_badge_seed": "Carteira com frase semente (HD)",
|
"wallets_badge_seed": "Carteira com frase semente (HD)",
|
||||||
@@ -1343,6 +1653,7 @@
|
|||||||
"wallets_created": "criada",
|
"wallets_created": "criada",
|
||||||
"wallets_creating": "Criando carteira — o nó será reiniciado…",
|
"wallets_creating": "Criando carteira — o nó será reiniciado…",
|
||||||
"wallets_current": "atual",
|
"wallets_current": "atual",
|
||||||
|
"wallets_empty_hint": "Verifique uma pasta para encontrar mais carteiras",
|
||||||
"wallets_exists": "Já existe uma carteira com esse nome.",
|
"wallets_exists": "Já existe uma carteira com esse nome.",
|
||||||
"wallets_external_tt": "Fora do seu diretório de dados — Abrir o vincula no lugar (sem cópia).",
|
"wallets_external_tt": "Fora do seu diretório de dados — Abrir o vincula no lugar (sem cópia).",
|
||||||
"wallets_folder_hint": "/caminho/para/pasta com arquivos .dat de carteira",
|
"wallets_folder_hint": "/caminho/para/pasta com arquivos .dat de carteira",
|
||||||
@@ -1356,7 +1667,9 @@
|
|||||||
"wallets_open_failed": "Não foi possível abrir esta carteira no lugar. Provavelmente está em uma unidade diferente do seu diretório de dados — mova-a para a mesma unidade (no Windows, ativar o Modo de Desenvolvedor também permite vincular entre unidades).",
|
"wallets_open_failed": "Não foi possível abrir esta carteira no lugar. Provavelmente está em uma unidade diferente do seu diretório de dados — mova-a para a mesma unidade (no Windows, ativar o Modo de Desenvolvedor também permite vincular entre unidades).",
|
||||||
"wallets_open_folder": "Abrir local da pasta",
|
"wallets_open_folder": "Abrir local da pasta",
|
||||||
"wallets_open_inplace_tt": "Abre esta carteira onde está — vinculada ao diretório de dados (sem cópia)",
|
"wallets_open_inplace_tt": "Abre esta carteira onde está — vinculada ao diretório de dados (sem cópia)",
|
||||||
|
"wallets_remove_folder": "Parar de verificar esta pasta",
|
||||||
"wallets_reveal": "Mostrar pasta",
|
"wallets_reveal": "Mostrar pasta",
|
||||||
|
"wallets_scanned_folders": "Pastas verificadas:",
|
||||||
"wallets_sort_addresses": "Endereços",
|
"wallets_sort_addresses": "Endereços",
|
||||||
"wallets_sort_asc": "Crescente (mais antigo / menos / menor primeiro)",
|
"wallets_sort_asc": "Crescente (mais antigo / menos / menor primeiro)",
|
||||||
"wallets_sort_by": "Ordenar:",
|
"wallets_sort_by": "Ordenar:",
|
||||||
|
|||||||
325
res/lang/ru.json
325
res/lang/ru.json
@@ -30,6 +30,7 @@
|
|||||||
"address_book_added": "Адрес добавлен в книгу",
|
"address_book_added": "Адрес добавлен в книгу",
|
||||||
"address_book_confirm_delete": "Подтвердить удаление?",
|
"address_book_confirm_delete": "Подтвердить удаление?",
|
||||||
"address_book_count": "%zu адресов сохранено",
|
"address_book_count": "%zu адресов сохранено",
|
||||||
|
"address_book_count_one": "Сохранён %zu адрес",
|
||||||
"address_book_deleted": "Запись удалена",
|
"address_book_deleted": "Запись удалена",
|
||||||
"address_book_edit": "Редактировать адрес",
|
"address_book_edit": "Редактировать адрес",
|
||||||
"address_book_empty": "Нет сохранённых адресов. Нажмите 'Добавить новый', чтобы создать.",
|
"address_book_empty": "Нет сохранённых адресов. Нажмите 'Добавить новый', чтобы создать.",
|
||||||
@@ -53,8 +54,20 @@
|
|||||||
"amount_details": "ДЕТАЛИ СУММЫ",
|
"amount_details": "ДЕТАЛИ СУММЫ",
|
||||||
"amount_exceeds_balance": "Сумма превышает баланс",
|
"amount_exceeds_balance": "Сумма превышает баланс",
|
||||||
"amount_label": "Сумма:",
|
"amount_label": "Сумма:",
|
||||||
|
"animate_avatars": "Анимировать аватары",
|
||||||
"appearance": "ВНЕШНИЙ ВИД",
|
"appearance": "ВНЕШНИЙ ВИД",
|
||||||
"auto_shield": "Авто-экранирование майнинга",
|
"auto_shield": "Авто-экранирование майнинга",
|
||||||
|
"av_intro": "Программы для майнинга часто помечаются как потенциально нежелательные. Выполните эти шаги, чтобы включить пул-майнинг:",
|
||||||
|
"av_open_security": "Открыть Безопасность Windows",
|
||||||
|
"av_step1": "Шаг 1: Добавить исключение",
|
||||||
|
"av_step1_b1": "Откройте Безопасность Windows > Защита от вирусов и угроз",
|
||||||
|
"av_step1_b2": "Нажмите Управление настройками > Исключения > Добавить или удалить",
|
||||||
|
"av_step1_b3": "Добавьте папку: %APPDATA%\\ObsidianDragon\\",
|
||||||
|
"av_step2": "Шаг 2: Восстановить из карантина (при необходимости)",
|
||||||
|
"av_step2_b1": "Безопасность Windows > Журнал защиты",
|
||||||
|
"av_step2_b2": "Найдите xmrig.exe и нажмите Восстановить",
|
||||||
|
"av_step3": "Шаг 3: Перезапустите кошелёк и попробуйте снова",
|
||||||
|
"av_title": "Windows Defender заблокировал майнер",
|
||||||
"available": "Доступно",
|
"available": "Доступно",
|
||||||
"backup_backing_up": "Создание резервной копии...",
|
"backup_backing_up": "Создание резервной копии...",
|
||||||
"backup_create": "Создать резервную копию",
|
"backup_create": "Создать резервную копию",
|
||||||
@@ -62,6 +75,7 @@
|
|||||||
"backup_data": "РЕЗЕРВНОЕ КОПИРОВАНИЕ И ДАННЫЕ",
|
"backup_data": "РЕЗЕРВНОЕ КОПИРОВАНИЕ И ДАННЫЕ",
|
||||||
"backup_description": "Создайте резервную копию файла wallet.dat. Этот файл содержит все ваши приватные ключи и историю транзакций. Храните копию в безопасном месте.",
|
"backup_description": "Создайте резервную копию файла wallet.dat. Этот файл содержит все ваши приватные ключи и историю транзакций. Храните копию в безопасном месте.",
|
||||||
"backup_destination": "Место сохранения:",
|
"backup_destination": "Место сохранения:",
|
||||||
|
"backup_overwrite_confirm": "Там уже есть файл — сохраните ещё раз, чтобы перезаписать его.",
|
||||||
"backup_source": "Источник: %s",
|
"backup_source": "Источник: %s",
|
||||||
"backup_tip_external": "Храните резервные копии на внешних дисках или в облаке",
|
"backup_tip_external": "Храните резервные копии на внешних дисках или в облаке",
|
||||||
"backup_tip_multiple": "Создавайте несколько копий в разных местах",
|
"backup_tip_multiple": "Создавайте несколько копий в разных местах",
|
||||||
@@ -70,6 +84,7 @@
|
|||||||
"backup_title": "Резервное копирование кошелька",
|
"backup_title": "Резервное копирование кошелька",
|
||||||
"backup_wallet": "Создать резервную копию...",
|
"backup_wallet": "Создать резервную копию...",
|
||||||
"backup_wallet_not_found": "Предупреждение: wallet.dat не найден в ожидаемом расположении",
|
"backup_wallet_not_found": "Предупреждение: wallet.dat не найден в ожидаемом расположении",
|
||||||
|
"backup_warn": "Этот файл содержит все ваши приватные ключи — храните его в надёжном месте.",
|
||||||
"balance": "Баланс",
|
"balance": "Баланс",
|
||||||
"balance_history_collecting": "История баланса — сбор данных...",
|
"balance_history_collecting": "История баланса — сбор данных...",
|
||||||
"balance_layout": "Макет баланса",
|
"balance_layout": "Макет баланса",
|
||||||
@@ -114,28 +129,105 @@
|
|||||||
"bootstrap_wallet_protected": "(wallet.dat защищён)",
|
"bootstrap_wallet_protected": "(wallet.dat защищён)",
|
||||||
"bootstrap_warning": "Существующие данные блоков (blocks, chainstate, notarizations) будут удалены и заменены. Ваш wallet.dat НЕ будет изменён или удалён.",
|
"bootstrap_warning": "Существующие данные блоков (blocks, chainstate, notarizations) будут удалены и заменены. Ваш wallet.dat НЕ будет изменён или удалён.",
|
||||||
"cancel": "Отмена",
|
"cancel": "Отмена",
|
||||||
|
"change_pass_confirm": "Подтвердите новый:",
|
||||||
|
"change_pass_current": "Текущий пароль:",
|
||||||
|
"change_pass_new": "Новый пароль:",
|
||||||
|
"change_pass_title": "Сменить пароль",
|
||||||
"characters": "символов",
|
"characters": "символов",
|
||||||
"chat": "Чат",
|
"chat": "Чат",
|
||||||
|
"chat_accent_amber": "Янтарный",
|
||||||
|
"chat_accent_blue": "Синий",
|
||||||
|
"chat_accent_green": "Зелёный",
|
||||||
|
"chat_accent_pink": "Розовый",
|
||||||
|
"chat_accent_purple": "Фиолетовый",
|
||||||
|
"chat_accent_theme": "Тема",
|
||||||
|
"chat_add_contact": "Добавить контакт",
|
||||||
|
"chat_awaiting_key": "Ожидание ответа",
|
||||||
|
"chat_bubble_minimal": "Минимальный",
|
||||||
|
"chat_bubble_rounded": "Скруглённый",
|
||||||
|
"chat_bubble_square": "Прямоугольный",
|
||||||
|
"chat_buffer_loading": "Буфер чата: …",
|
||||||
|
"chat_buffer_preparing": "Буфер чата: подготовка %d/%d…",
|
||||||
|
"chat_buffer_ready": "Буфер чата: %d/%d готово",
|
||||||
|
"chat_buffer_sending": "Чат: отправка %d сообщений…",
|
||||||
|
"chat_buffer_sending_one": "Чат: отправка %d сообщения…",
|
||||||
"chat_cancel": "Отмена",
|
"chat_cancel": "Отмена",
|
||||||
|
"chat_contact_added": "Контакт добавлен — переименуйте его в Контактах",
|
||||||
"chat_contact_request": "запрос контакта",
|
"chat_contact_request": "запрос контакта",
|
||||||
|
"chat_copy_address_tip": "Нажмите, чтобы скопировать адрес",
|
||||||
|
"chat_density_comfortable": "Свободная",
|
||||||
|
"chat_density_compact": "Компактная",
|
||||||
|
"chat_emoji_color": "Цветной",
|
||||||
|
"chat_emoji_mono": "Монохромный",
|
||||||
|
"chat_emoji_search": "Поиск эмодзи",
|
||||||
"chat_empty_hint": "Пока нет переписок. Полученные сообщения появятся здесь.",
|
"chat_empty_hint": "Пока нет переписок. Полученные сообщения появятся здесь.",
|
||||||
|
"chat_empty_start": "Начните новый с помощью «Новый разговор».",
|
||||||
|
"chat_empty_title": "Пока нет разговоров",
|
||||||
|
"chat_export": "Экспорт чата…",
|
||||||
|
"chat_export_done": "Разговор экспортирован",
|
||||||
|
"chat_export_failed": "Не удалось записать файл экспорта.",
|
||||||
|
"chat_export_warn": "Сохраняет расшифрованные сообщения в виде обычного текста. Храните файл в надёжном месте.",
|
||||||
|
"chat_filter": "Чат",
|
||||||
|
"chat_hidden_toast": "Разговор скрыт — новое сообщение вернёт его",
|
||||||
|
"chat_hide": "Скрыть",
|
||||||
|
"chat_hide_hidden": "Скрыть скрытые",
|
||||||
|
"chat_jump_latest": "Новые",
|
||||||
|
"chat_len_over": "Сообщение слишком длинное",
|
||||||
"chat_locked_hint": "Разблокируйте кошелёк, чтобы загрузить переписку.",
|
"chat_locked_hint": "Разблокируйте кошелёк, чтобы загрузить переписку.",
|
||||||
"chat_new_button": "Новая переписка",
|
"chat_mute": "Отключить уведомления",
|
||||||
|
"chat_new_button": "Новый чат",
|
||||||
"chat_new_message": "Сообщение",
|
"chat_new_message": "Сообщение",
|
||||||
|
"chat_new_message_toast": "Новое зашифрованное сообщение",
|
||||||
"chat_new_send": "Отправить запрос",
|
"chat_new_send": "Отправить запрос",
|
||||||
"chat_new_title": "Новая переписка",
|
"chat_new_title": "Новый чат",
|
||||||
"chat_new_zaddr": "Z-адрес получателя",
|
"chat_new_zaddr": "Z-адрес получателя",
|
||||||
|
"chat_no_matches": "Нет разговоров, соответствующих запросу.",
|
||||||
|
"chat_no_z_contacts": "Пока нет контактов с защищённым адресом",
|
||||||
|
"chat_opt_bubble_accent": "Цвет пузырька",
|
||||||
|
"chat_opt_bubble_style": "Стиль пузырька",
|
||||||
|
"chat_opt_density": "Плотность сообщений",
|
||||||
|
"chat_opt_emoji": "Стиль эмодзи",
|
||||||
|
"chat_opt_enter_sends": "Enter отправляет сообщение",
|
||||||
|
"chat_opt_font_size": "Размер текста",
|
||||||
|
"chat_opt_global_clock": "Глобальный формат времени",
|
||||||
|
"chat_opt_poll": "Частота опроса",
|
||||||
|
"chat_opt_timestamp": "Метки времени",
|
||||||
|
"chat_pick_contact": "Выбрать из контактов…",
|
||||||
|
"chat_rename": "Переименовать контакт",
|
||||||
|
"chat_rename_hint": "Имя контакта",
|
||||||
|
"chat_renamed": "Контакт переименован",
|
||||||
|
"chat_retry": "Повторить",
|
||||||
|
"chat_search": "Поиск разговоров",
|
||||||
|
"chat_sec_appearance": "ВИД",
|
||||||
|
"chat_sec_messaging": "СООБЩЕНИЯ",
|
||||||
"chat_select_hint": "Выберите переписку для просмотра.",
|
"chat_select_hint": "Выберите переписку для просмотра.",
|
||||||
"chat_send": "Отправить",
|
"chat_send": "Отправить",
|
||||||
"chat_send_failed": "не отправлено",
|
"chat_send_failed": "не отправлено",
|
||||||
|
"chat_sending": "отправка…",
|
||||||
|
"chat_settings_done": "Готово",
|
||||||
|
"chat_settings_section": "ЧАТ И КОНТАКТЫ",
|
||||||
|
"chat_settings_tip": "Настройка чата",
|
||||||
|
"chat_settings_title": "Настройки чата",
|
||||||
|
"chat_show_hidden": "Показать скрытые",
|
||||||
|
"chat_time_now": "сейчас",
|
||||||
"chat_toast_compose_failed": "Не удалось составить сообщение (слишком длинное?).",
|
"chat_toast_compose_failed": "Не удалось составить сообщение (слишком длинное?).",
|
||||||
"chat_toast_lite_busy": "Отправка уже выполняется, или кошелёк не открыт.",
|
"chat_toast_lite_busy": "Отправка уже выполняется, или кошелёк не открыт.",
|
||||||
|
"chat_toast_need_funds": "Для отправки сообщений нужен небольшой экранированный баланс (для оплаты комиссии).",
|
||||||
"chat_toast_no_zaddr": "Нет доступного Z-адреса для отправки сообщений.",
|
"chat_toast_no_zaddr": "Нет доступного Z-адреса для отправки сообщений.",
|
||||||
"chat_toast_not_connected": "Нет подключения — сообщение не отправлено.",
|
"chat_toast_not_connected": "Нет подключения — сообщение не отправлено.",
|
||||||
"chat_toast_request_compose_failed": "Не удалось составить запрос контакта (неверный адрес / текст?).",
|
"chat_toast_request_compose_failed": "Не удалось составить запрос контакта (неверный адрес / текст?).",
|
||||||
"chat_toast_request_queued": "Запрос контакта поставлен в очередь.",
|
"chat_toast_request_queued": "Запрос контакта поставлен в очередь.",
|
||||||
"chat_toast_waiting_reply": "Ожидание ответа от контакта — вы сможете писать ему только после этого.",
|
"chat_toast_waiting_reply": "Ожидание ответа от контакта — вы сможете писать ему только после этого.",
|
||||||
|
"chat_today": "Сегодня",
|
||||||
|
"chat_ts_12h": "12 часов",
|
||||||
|
"chat_ts_24h": "24 часа",
|
||||||
|
"chat_ts_global": "Как глобально",
|
||||||
|
"chat_ts_global_short": "Общий",
|
||||||
|
"chat_unhide": "Показать",
|
||||||
|
"chat_unmute": "Включить уведомления",
|
||||||
|
"chat_verify_key": "Ключ личности — сравните для проверки",
|
||||||
"chat_waiting_reply": "Ожидание ответа от контакта — вы сможете писать ему, как только он ответит.",
|
"chat_waiting_reply": "Ожидание ответа от контакта — вы сможете писать ему, как только он ответит.",
|
||||||
|
"chat_yesterday": "Вчера",
|
||||||
"chat_you": "Вы",
|
"chat_you": "Вы",
|
||||||
"choose_icon": "Выбрать иконку",
|
"choose_icon": "Выбрать иконку",
|
||||||
"clear": "Очистить",
|
"clear": "Очистить",
|
||||||
@@ -147,6 +239,7 @@
|
|||||||
"click_copy_address": "Нажмите, чтобы скопировать адрес",
|
"click_copy_address": "Нажмите, чтобы скопировать адрес",
|
||||||
"click_copy_uri": "Нажмите, чтобы скопировать URI",
|
"click_copy_uri": "Нажмите, чтобы скопировать URI",
|
||||||
"click_to_copy": "Нажмите для копирования",
|
"click_to_copy": "Нажмите для копирования",
|
||||||
|
"clock_format": "Формат времени",
|
||||||
"close": "Закрыть",
|
"close": "Закрыть",
|
||||||
"conf_count": "%d подтв.",
|
"conf_count": "%d подтв.",
|
||||||
"confirm_and_send": "Подтвердить и отправить",
|
"confirm_and_send": "Подтвердить и отправить",
|
||||||
@@ -180,10 +273,24 @@
|
|||||||
"connected_peers": "Подключённые узлы",
|
"connected_peers": "Подключённые узлы",
|
||||||
"connecting": "Подключение...",
|
"connecting": "Подключение...",
|
||||||
"console": "Консоль",
|
"console": "Консоль",
|
||||||
|
"console_accents": "Цветовые акценты",
|
||||||
"console_app": "Прил.",
|
"console_app": "Прил.",
|
||||||
"console_auto_scroll": "Авто-прокрутка",
|
"console_auto_scroll": "Авто-прокрутка",
|
||||||
"console_available_commands": "Доступные команды:",
|
"console_available_commands": "Доступные команды:",
|
||||||
|
"console_backend_reference": "Справочник команд бэкенда",
|
||||||
|
"console_backend_unavailable": "Нет бэкенда",
|
||||||
"console_capturing_output": "Захват вывода daemon...",
|
"console_capturing_output": "Захват вывода daemon...",
|
||||||
|
"console_cat_advanced": "Дополнительно",
|
||||||
|
"console_cat_blockchain": "Блокчейн",
|
||||||
|
"console_cat_control": "Управление",
|
||||||
|
"console_cat_keys": "Ключи и безопасность",
|
||||||
|
"console_cat_mining": "Майнинг",
|
||||||
|
"console_cat_network": "Сеть",
|
||||||
|
"console_cat_raw_transactions": "Сырые транзакции",
|
||||||
|
"console_cat_send": "Отправка",
|
||||||
|
"console_cat_sync": "Синхронизация",
|
||||||
|
"console_cat_utility": "Утилиты",
|
||||||
|
"console_cat_wallet": "Кошелёк",
|
||||||
"console_clear": "Очистить",
|
"console_clear": "Очистить",
|
||||||
"console_clear_console": "Очистить консоль",
|
"console_clear_console": "Очистить консоль",
|
||||||
"console_cleared": "Консоль очищена",
|
"console_cleared": "Консоль очищена",
|
||||||
@@ -215,18 +322,37 @@
|
|||||||
"console_help_help": " help - Показать эту справку",
|
"console_help_help": " help - Показать эту справку",
|
||||||
"console_help_setgenerate": " setgenerate - Управление майнингом",
|
"console_help_setgenerate": " setgenerate - Управление майнингом",
|
||||||
"console_help_stop": " stop - Остановить daemon",
|
"console_help_stop": " stop - Остановить daemon",
|
||||||
|
"console_last_error": "Последняя ошибка:",
|
||||||
"console_line_count": "%zu строк",
|
"console_line_count": "%zu строк",
|
||||||
"console_matches": "совпадений",
|
"console_matches": "совпадений",
|
||||||
"console_new_lines": "%d новых строк",
|
"console_new_lines": "%d новых строк",
|
||||||
"console_no_daemon": "Нет daemon",
|
"console_no_daemon": "Нет daemon",
|
||||||
|
"console_no_output": "(нет вывода)",
|
||||||
"console_not_connected": "Ошибка: Не подключено к daemon",
|
"console_not_connected": "Ошибка: Не подключено к daemon",
|
||||||
|
"console_not_connected_lite": "Ошибка: Нет открытого кошелька",
|
||||||
"console_quit_note": "Здесь не нужны 'quit'/'exit' — просто закройте окно.",
|
"console_quit_note": "Здесь не нужны 'quit'/'exit' — просто закройте окно.",
|
||||||
|
"console_ref_builds": "Формирует",
|
||||||
|
"console_ref_cancel": "Отмена",
|
||||||
|
"console_ref_destructive": "Осторожно",
|
||||||
|
"console_ref_example": "Пример",
|
||||||
|
"console_ref_insert": "Вставить в консоль",
|
||||||
|
"console_ref_insert_run": "Вставить и выполнить",
|
||||||
|
"console_ref_no_match": "Нет подходящих команд.",
|
||||||
|
"console_ref_no_params": "Не требует параметров.",
|
||||||
|
"console_ref_optional": "необязательно",
|
||||||
|
"console_ref_parameters": "Параметры",
|
||||||
|
"console_ref_run": "Выполнить",
|
||||||
|
"console_ref_run_confirm": "Выполнить %s сейчас? Это ответственная команда.",
|
||||||
|
"console_ref_search_hint": "Поиск по названию или задаче…",
|
||||||
|
"console_ref_select_hint": "Выберите команду, чтобы увидеть, что она делает.",
|
||||||
|
"console_ref_value": "значение",
|
||||||
"console_rpc_reference": "Справочник RPC-команд",
|
"console_rpc_reference": "Справочник RPC-команд",
|
||||||
"console_rpc_trace": "RPC",
|
"console_rpc_trace": "RPC",
|
||||||
"console_scanline": "Скан-линия консоли",
|
"console_scanline": "Скан-линия консоли",
|
||||||
"console_search_commands": "Поиск команд...",
|
"console_search_commands": "Поиск команд...",
|
||||||
"console_select_all": "Выбрать всё",
|
"console_select_all": "Выбрать всё",
|
||||||
"console_show_app_output": "Показать строки журнала кошелька [app]",
|
"console_show_app_output": "Показать строки журнала кошелька [app]",
|
||||||
|
"console_show_backend_ref": "Показать справочник команд бэкенда",
|
||||||
"console_show_daemon_output": "Показать вывод daemon",
|
"console_show_daemon_output": "Показать вывод daemon",
|
||||||
"console_show_errors_only": "Показать только ошибки",
|
"console_show_errors_only": "Показать только ошибки",
|
||||||
"console_show_rpc_ref": "Показать справочник RPC-команд",
|
"console_show_rpc_ref": "Показать справочник RPC-команд",
|
||||||
@@ -239,17 +365,43 @@
|
|||||||
"console_status_stopped": "Остановлен",
|
"console_status_stopped": "Остановлен",
|
||||||
"console_status_stopping": "Остановка",
|
"console_status_stopping": "Остановка",
|
||||||
"console_status_unknown": "Неизвестно",
|
"console_status_unknown": "Неизвестно",
|
||||||
|
"console_stop_confirm_node": "'stop' остановит узел и отключит кошелёк. Введите 'stop' ещё раз для подтверждения.",
|
||||||
"console_tab_completion": "Tab для дополнения",
|
"console_tab_completion": "Tab для дополнения",
|
||||||
|
"console_text_colors": "Цвета текста",
|
||||||
|
"console_toggle_accents": "Переключить цветовые акценты строк",
|
||||||
|
"console_toggle_text_color": "Переключить цвета текста строк",
|
||||||
"console_type_help": "Введите 'help' для списка команд",
|
"console_type_help": "Введите 'help' для списка команд",
|
||||||
"console_welcome": "Добро пожаловать в консоль ObsidianDragon",
|
"console_welcome": "Добро пожаловать в консоль ObsidianDragon",
|
||||||
"console_zoom_in": "Увеличить",
|
"console_zoom_in": "Увеличить",
|
||||||
"console_zoom_out": "Уменьшить",
|
"console_zoom_out": "Уменьшить",
|
||||||
|
"contact_avatar": "АВАТАР",
|
||||||
|
"contact_avatar_bad_image": "Не удалось загрузить это изображение.",
|
||||||
|
"contact_avatar_badge": "Значок",
|
||||||
|
"contact_avatar_badge_hint": "Значок выбирается автоматически по типу адреса.",
|
||||||
|
"contact_avatar_choose": "Выбрать изображение…",
|
||||||
|
"contact_avatar_copy_failed": "Не удалось скопировать это изображение.",
|
||||||
|
"contact_avatar_icon": "Иконка",
|
||||||
|
"contact_avatar_image": "Изображение",
|
||||||
|
"contact_avatar_image_hint": "Изображение копируется в приложение, чтобы оставаться доступным, если оригинал переместят.",
|
||||||
|
"contact_avatar_remove": "Удалить",
|
||||||
|
"contact_avatar_shielded": "Защищённый",
|
||||||
|
"contact_avatar_transparent": "Прозрачный",
|
||||||
"contact_global": "Показывать во всех кошельках (глобальный контакт)",
|
"contact_global": "Показывать во всех кошельках (глобальный контакт)",
|
||||||
"contact_global_badge_tt": "Глобальный контакт — виден во всех кошельках",
|
"contact_global_badge_tt": "Глобальный контакт — виден во всех кошельках",
|
||||||
"contact_global_tt": "Вкл.: этот контакт остаётся видимым, какой бы кошелёк вы ни загрузили. Выкл.: он принадлежит только текущему кошельку.",
|
"contact_global_tt": "Вкл.: этот контакт остаётся видимым, какой бы кошелёк вы ни загрузили. Выкл.: он принадлежит только текущему кошельку.",
|
||||||
|
"contact_preview_addr": "Здесь появится адрес",
|
||||||
|
"contact_preview_name": "Имя контакта",
|
||||||
|
"contact_wallet_loading": "Кошелёк ещё загружается — отметьте «Показывать во всех кошельках» или повторите чуть позже.",
|
||||||
"contacts": "Контакты",
|
"contacts": "Контакты",
|
||||||
|
"contacts_avatar_shape": "Форма аватара",
|
||||||
|
"contacts_list_scale": "Масштаб списка",
|
||||||
"contacts_search_no_match": "Совпадающих контактов нет",
|
"contacts_search_no_match": "Совпадающих контактов нет",
|
||||||
"contacts_search_placeholder": "Поиск контактов...",
|
"contacts_search_placeholder": "Поиск контактов...",
|
||||||
|
"contacts_settings_tip": "Настройка контактов",
|
||||||
|
"contacts_settings_title": "Настройки контактов",
|
||||||
|
"contacts_shape_circle": "Круг",
|
||||||
|
"contacts_shape_square": "Квадрат",
|
||||||
|
"contacts_shape_tab": "Вкладка",
|
||||||
"copied": "Скопировано!",
|
"copied": "Скопировано!",
|
||||||
"copy": "Копировать",
|
"copy": "Копировать",
|
||||||
"copy_address": "Копировать полный адрес",
|
"copy_address": "Копировать полный адрес",
|
||||||
@@ -302,6 +454,19 @@
|
|||||||
"date": "Дата",
|
"date": "Дата",
|
||||||
"date_label": "Дата:",
|
"date_label": "Дата:",
|
||||||
"debug_logging": "ЖУРНАЛ ОТЛАДКИ",
|
"debug_logging": "ЖУРНАЛ ОТЛАДКИ",
|
||||||
|
"decrypt_desc": "Кошелёк будет экспортирован, демон перезапущен с новым незашифрованным кошельком, и все ключи будут повторно импортированы. Это может занять несколько минут в зависимости от размера кошелька.",
|
||||||
|
"decrypt_error_title": "Не удалось расшифровать",
|
||||||
|
"decrypt_step_backup": "Резервное копирование зашифрованного кошелька",
|
||||||
|
"decrypt_step_export": "Экспорт ключей кошелька",
|
||||||
|
"decrypt_step_restart": "Перезапуск демона",
|
||||||
|
"decrypt_step_stop": "Остановка демона",
|
||||||
|
"decrypt_step_unlock": "Разблокировка кошелька",
|
||||||
|
"decrypt_success_desc": "Ваш кошелёк теперь не зашифрован. Резервная копия зашифрованного кошелька сохранена как wallet.dat.encrypted.bak в вашем каталоге данных.",
|
||||||
|
"decrypt_success_title": "Кошелёк успешно расшифрован!",
|
||||||
|
"decrypt_title": "Удалить шифрование кошелька",
|
||||||
|
"decrypt_wait_general": "Пожалуйста, подождите. Демон экспортирует ключи, перезапускается и повторно импортирует. Это может занять несколько минут.",
|
||||||
|
"decrypt_wait_restart": "Ожидание полного запуска демона...",
|
||||||
|
"decrypt_warning": "Это удалит шифрование вашего кошелька. Ваши приватные ключи будут храниться на диске без защиты.",
|
||||||
"delete": "Удалить",
|
"delete": "Удалить",
|
||||||
"delete_blockchain": "Удалить блокчейн",
|
"delete_blockchain": "Удалить блокчейн",
|
||||||
"delete_blockchain_confirm": "Удалить и пересинхронизировать",
|
"delete_blockchain_confirm": "Удалить и пересинхронизировать",
|
||||||
@@ -314,6 +479,15 @@
|
|||||||
"download_bootstrap": "Скачать бутстрап",
|
"download_bootstrap": "Скачать бутстрап",
|
||||||
"dragonx_green": "DragonX (Зелёная)",
|
"dragonx_green": "DragonX (Зелёная)",
|
||||||
"edit": "Редактировать",
|
"edit": "Редактировать",
|
||||||
|
"enc_confirm": "Подтвердите:",
|
||||||
|
"enc_desc": "Шифрование кошелька защищает ваши приватные ключи паролем. После шифрования демон перезапустится.",
|
||||||
|
"enc_encrypting": "Шифрование кошелька...",
|
||||||
|
"enc_pin_desc": "PIN-код из 4–8 цифр позволяет разблокировать кошелёк, не вводя полный пароль каждый раз.",
|
||||||
|
"enc_pin_set_ok": "PIN-код успешно установлен",
|
||||||
|
"enc_pin_skipped": "PIN-код пропущен. Вы можете задать его позже в настройках.",
|
||||||
|
"enc_pin_vault_fail": "Не удалось создать хранилище PIN-кода",
|
||||||
|
"enc_success": "Кошелёк успешно зашифрован!",
|
||||||
|
"enc_wait": "Пожалуйста, подождите, не закрывайте приложение.",
|
||||||
"error": "Ошибка",
|
"error": "Ошибка",
|
||||||
"error_format": "Ошибка: %s",
|
"error_format": "Ошибка: %s",
|
||||||
"est_time_to_block": "Расч. время до блока",
|
"est_time_to_block": "Расч. время до блока",
|
||||||
@@ -333,23 +507,34 @@
|
|||||||
"explorer_mempool": "Мемпул",
|
"explorer_mempool": "Мемпул",
|
||||||
"explorer_mempool_size": "Размер",
|
"explorer_mempool_size": "Размер",
|
||||||
"explorer_mempool_txs": "Транзакции",
|
"explorer_mempool_txs": "Транзакции",
|
||||||
|
"explorer_next_block": "Следующий блок",
|
||||||
"explorer_no_results": "Нет подходящих кэшированных блоков",
|
"explorer_no_results": "Нет подходящих кэшированных блоков",
|
||||||
"explorer_not_connected": "Нет подключения к демону — невозможно найти хеш блока или транзакции",
|
"explorer_not_connected": "Нет подключения к демону — невозможно найти хеш блока или транзакции",
|
||||||
|
"explorer_prev_block": "Предыдущий блок",
|
||||||
"explorer_recent_blocks": "Последние блоки",
|
"explorer_recent_blocks": "Последние блоки",
|
||||||
"explorer_search": "Поиск",
|
"explorer_search": "Поиск",
|
||||||
"explorer_section": "ОБОЗРЕВАТЕЛЬ",
|
"explorer_section": "ОБОЗРЕВАТЕЛЬ",
|
||||||
"explorer_tx_outputs": "Выходы",
|
"explorer_tx_outputs": "Выходы",
|
||||||
"explorer_tx_size": "Размер",
|
"explorer_tx_size": "Размер",
|
||||||
|
"explorer_txid": "TxID:",
|
||||||
"export": "Экспорт",
|
"export": "Экспорт",
|
||||||
"export_csv": "Экспорт в CSV",
|
"export_csv": "Экспорт в CSV",
|
||||||
"export_keys_btn": "Экспорт ключей",
|
"export_keys_btn": "Экспорт ключей",
|
||||||
"export_keys_danger": "ОПАСНОСТЬ: Будут экспортированы ВСЕ приватные ключи из вашего кошелька! Любой, кто получит доступ к этому файлу, сможет украсть ваши средства. Храните его в безопасности и удалите после использования.",
|
"export_keys_danger": "ОПАСНОСТЬ: Будут экспортированы ВСЕ приватные ключи из вашего кошелька! Любой, кто получит доступ к этому файлу, сможет украсть ваши средства. Храните его в безопасности и удалите после использования.",
|
||||||
"export_keys_include_t": "Включить T-адреса (прозрачные)",
|
"export_keys_include_t": "Включить T-адреса (прозрачные)",
|
||||||
"export_keys_include_z": "Включить Z-адреса (экранированные)",
|
"export_keys_include_z": "Включить Z-адреса (экранированные)",
|
||||||
|
"export_keys_none_addrs": "Нет адресов для экспорта",
|
||||||
|
"export_keys_none_result": "Ключи не экспортированы (0 из %d) — разблокируйте кошелёк и повторите попытку.",
|
||||||
|
"export_keys_none_toast": "Не удалось экспортировать ключи — кошелёк разблокирован?",
|
||||||
|
"export_keys_not_connected": "Нет подключения к демону",
|
||||||
"export_keys_options": "Параметры экспорта:",
|
"export_keys_options": "Параметры экспорта:",
|
||||||
|
"export_keys_partial": "Экспортировано %d из %d ключей — неполно (у некоторых нет ключа расходования или кошелёк заблокирован).",
|
||||||
|
"export_keys_partial_toast": "Частичный экспорт: %d из %d ключей",
|
||||||
"export_keys_progress": "Экспорт %d/%d...",
|
"export_keys_progress": "Экспорт %d/%d...",
|
||||||
|
"export_keys_select_type": "Выберите хотя бы один тип адреса",
|
||||||
"export_keys_success": "Ключи успешно экспортированы",
|
"export_keys_success": "Ключи успешно экспортированы",
|
||||||
"export_keys_title": "Экспорт всех приватных ключей",
|
"export_keys_title": "Экспорт всех приватных ключей",
|
||||||
|
"export_keys_write_fail": "Не удалось записать файл ключей.",
|
||||||
"export_private_key": "Экспорт приватного ключа",
|
"export_private_key": "Экспорт приватного ключа",
|
||||||
"export_tx_count": "Экспортировать %zu транзакций в файл CSV.",
|
"export_tx_count": "Экспортировать %zu транзакций в файл CSV.",
|
||||||
"export_tx_file_fail": "Не удалось создать файл CSV",
|
"export_tx_file_fail": "Не удалось создать файл CSV",
|
||||||
@@ -389,23 +574,50 @@
|
|||||||
"hide_qr": "Скрыть QR",
|
"hide_qr": "Скрыть QR",
|
||||||
"hide_zero_balances": "Скрыть нулевые балансы",
|
"hide_zero_balances": "Скрыть нулевые балансы",
|
||||||
"history": "История",
|
"history": "История",
|
||||||
|
"img_picker_count": "Изображений в этой папке: %d",
|
||||||
|
"img_picker_empty": "В этой папке нет подпапок или изображений.",
|
||||||
|
"img_picker_none": "В этой папке нет изображений",
|
||||||
|
"img_picker_pictures": "Папка изображений",
|
||||||
|
"img_picker_title": "Выбрать изображение",
|
||||||
|
"img_picker_use": "Использовать изображение",
|
||||||
"immature_type": "Незрелая",
|
"immature_type": "Незрелая",
|
||||||
"import": "Импорт",
|
"import": "Импорт",
|
||||||
|
"import_key_address": "Адрес:",
|
||||||
"import_key_btn": "Импорт ключей",
|
"import_key_btn": "Импорт ключей",
|
||||||
|
"import_key_done": "Импортировано. Кошелёк выполняет повторное сканирование.",
|
||||||
|
"import_key_field": "Ключ",
|
||||||
"import_key_formats": "Поддерживаемые форматы ключей:",
|
"import_key_formats": "Поддерживаемые форматы ключей:",
|
||||||
"import_key_full_rescan": "(0 = полное сканирование)",
|
"import_key_full_rescan": "(0 = полное сканирование)",
|
||||||
|
"import_key_import": "Импорт",
|
||||||
"import_key_label": "Приватный ключ(и):",
|
"import_key_label": "Приватный ключ(и):",
|
||||||
|
"import_key_need_node": "Подключите запущенный узел, чтобы импортировать ключ.",
|
||||||
"import_key_no_valid": "В введённых данных не найдено действительных ключей",
|
"import_key_no_valid": "В введённых данных не найдено действительных ключей",
|
||||||
"import_key_progress": "Импорт %d/%d...",
|
"import_key_progress": "Импорт %d/%d...",
|
||||||
"import_key_rescan": "Пересканировать блокчейн после импорта",
|
"import_key_rescan": "Пересканировать блокчейн после импорта",
|
||||||
|
"import_key_rescanning": "Импорт и повторное сканирование — это может занять несколько минут",
|
||||||
|
"import_key_reveal_tip": "Показать/скрыть ключ",
|
||||||
"import_key_start_height": "Начальная высота:",
|
"import_key_start_height": "Начальная высота:",
|
||||||
"import_key_success": "Ключи успешно импортированы",
|
"import_key_success": "Ключи успешно импортированы",
|
||||||
"import_key_t_format": "Приватные ключи WIF для T-адресов",
|
"import_key_t_format": "Приватные ключи WIF для T-адресов",
|
||||||
"import_key_title": "Импорт приватного ключа",
|
"import_key_title": "Импорт приватного ключа",
|
||||||
"import_key_tooltip": "Введите один или несколько приватных ключей, по одному на строку.\nПоддерживаются ключи z-адресов и t-адресов.\nСтроки, начинающиеся с #, считаются комментариями.",
|
"import_key_tooltip": "Введите один или несколько приватных ключей, по одному на строку.\nПоддерживаются ключи z-адресов и t-адресов.\nСтроки, начинающиеся с #, считаются комментариями.",
|
||||||
|
"import_key_type_tkey": "Прозрачный приватный ключ",
|
||||||
|
"import_key_type_unknown": "Нераспознанный формат ключа",
|
||||||
|
"import_key_type_zspend": "Экранированный ключ траты",
|
||||||
|
"import_key_type_zview": "Экранированный ключ просмотра (только чтение)",
|
||||||
|
"import_key_warn": "Импортируйте только свой ключ — он даёт доступ к его средствам.",
|
||||||
"import_key_warning": "Предупреждение: Никогда не делитесь своими приватными ключами! Импорт ключей из ненадёжных источников может скомпрометировать ваш кошелёк.",
|
"import_key_warning": "Предупреждение: Никогда не делитесь своими приватными ключами! Импорт ключей из ненадёжных источников может скомпрометировать ваш кошелёк.",
|
||||||
|
"import_key_wrong_type": "Похоже, это ключ просмотра. Используйте «Импорт ключа просмотра».",
|
||||||
"import_key_z_format": "Ключи расходования z-адресов (secret-extended-key-...)",
|
"import_key_z_format": "Ключи расходования z-адресов (secret-extended-key-...)",
|
||||||
"import_private_key": "Импорт приватного ключа...",
|
"import_private_key": "Импорт приватного ключа...",
|
||||||
|
"import_scan_hint": "0 = повторное сканирование с начала",
|
||||||
|
"import_scan_label": "Сканировать с высоты блока (необязательно)",
|
||||||
|
"import_scan_tip": "текущая высота",
|
||||||
|
"import_scan_transparent": "Прозрачные ключи всегда пересканируются полностью",
|
||||||
|
"import_viewkey_field": "Ключ просмотра",
|
||||||
|
"import_viewkey_note": "Только чтение: ключ просмотра показывает баланс и транзакции адреса, но не может тратить его средства.",
|
||||||
|
"import_viewkey_title": "Импорт ключа просмотра",
|
||||||
|
"import_viewkey_wrong_type": "Похоже, это ключ расходования. Используйте «Импорт приватного ключа».",
|
||||||
"incorrect_passphrase": "Неверный пароль",
|
"incorrect_passphrase": "Неверный пароль",
|
||||||
"incorrect_pin": "Неверный PIN",
|
"incorrect_pin": "Неверный PIN",
|
||||||
"insufficient_funds": "Недостаточно средств для этой суммы плюс комиссия.",
|
"insufficient_funds": "Недостаточно средств для этой суммы плюс комиссия.",
|
||||||
@@ -414,6 +626,7 @@
|
|||||||
"keep": "Сохранить",
|
"keep": "Сохранить",
|
||||||
"keep_daemon": "Оставить daemon работающим",
|
"keep_daemon": "Оставить daemon работающим",
|
||||||
"key_export_click_retrieve": "Нажмите, чтобы получить ключ из вашего кошелька",
|
"key_export_click_retrieve": "Нажмите, чтобы получить ключ из вашего кошелька",
|
||||||
|
"key_export_failed": "Не удалось экспортировать ключ — разблокируйте кошелёк (если зашифрован) и повторите попытку.",
|
||||||
"key_export_fetching": "Получение ключа из кошелька...",
|
"key_export_fetching": "Получение ключа из кошелька...",
|
||||||
"key_export_private_key": "Приватный ключ:",
|
"key_export_private_key": "Приватный ключ:",
|
||||||
"key_export_private_warning": "Держите этот ключ в ТАЙНЕ! Любой, кто владеет этим ключом, может потратить ваши средства. Никогда не делитесь им в интернете или с ненадёжными лицами.",
|
"key_export_private_warning": "Держите этот ключ в ТАЙНЕ! Любой, кто владеет этим ключом, может потратить ваши средства. Никогда не делитесь им в интернете или с ненадёжными лицами.",
|
||||||
@@ -431,6 +644,7 @@
|
|||||||
"lite_birthday_backup": "Дата рождения: %llu (сохраните её тоже)",
|
"lite_birthday_backup": "Дата рождения: %llu (сохраните её тоже)",
|
||||||
"lite_birthday_hint": "Высота блока, с которой начинать сканирование. Оставьте 0, если неизвестно (медленное полное сканирование).",
|
"lite_birthday_hint": "Высота блока, с которой начинать сканирование. Оставьте 0, если неизвестно (медленное полное сканирование).",
|
||||||
"lite_birthday_label": "Дата рождения",
|
"lite_birthday_label": "Дата рождения",
|
||||||
|
"lite_console_backend_commands": "Команды бэкенда:",
|
||||||
"lite_console_help_passthrough": "Любой другой ввод выполняется как команда консоли лайт-кошелька.",
|
"lite_console_help_passthrough": "Любой другой ввод выполняется как команда консоли лайт-кошелька.",
|
||||||
"lite_copy": "Копировать",
|
"lite_copy": "Копировать",
|
||||||
"lite_could_not_write": "Не удалось записать ",
|
"lite_could_not_write": "Не удалось записать ",
|
||||||
@@ -448,6 +662,7 @@
|
|||||||
"lite_net_add_url_hint": "https://your-lite-server",
|
"lite_net_add_url_hint": "https://your-lite-server",
|
||||||
"lite_net_checking": "проверка…",
|
"lite_net_checking": "проверка…",
|
||||||
"lite_net_connected": "Подключено",
|
"lite_net_connected": "Подключено",
|
||||||
|
"lite_net_connecting": "Подключение…",
|
||||||
"lite_net_custom": "Свой",
|
"lite_net_custom": "Свой",
|
||||||
"lite_net_disconnected": "Не подключено",
|
"lite_net_disconnected": "Не подключено",
|
||||||
"lite_net_hidden_section": "Скрытые серверы",
|
"lite_net_hidden_section": "Скрытые серверы",
|
||||||
@@ -534,6 +749,10 @@
|
|||||||
"market_btc_price": "ЦЕНА BTC",
|
"market_btc_price": "ЦЕНА BTC",
|
||||||
"market_cap": "Рыночная капитализация",
|
"market_cap": "Рыночная капитализация",
|
||||||
"market_cap_short": "Кап.",
|
"market_cap_short": "Кап.",
|
||||||
|
"market_chart_loading": "Загрузка истории цен",
|
||||||
|
"market_col_name": "Название",
|
||||||
|
"market_col_trend": "Тренд",
|
||||||
|
"market_col_value": "Стоимость",
|
||||||
"market_iv_1d": "1Д",
|
"market_iv_1d": "1Д",
|
||||||
"market_iv_1h": "1Ч",
|
"market_iv_1h": "1Ч",
|
||||||
"market_iv_1m": "1М",
|
"market_iv_1m": "1М",
|
||||||
@@ -542,11 +761,18 @@
|
|||||||
"market_no_history": "Нет истории цен",
|
"market_no_history": "Нет истории цен",
|
||||||
"market_no_price": "Нет данных о ценах",
|
"market_no_price": "Нет данных о ценах",
|
||||||
"market_now": "Сейчас",
|
"market_now": "Сейчас",
|
||||||
|
"market_opt_chart_style": "Стиль графика",
|
||||||
"market_pct_shielded": "%.0f%% Экранировано",
|
"market_pct_shielded": "%.0f%% Экранировано",
|
||||||
"market_portfolio": "ПОРТФЕЛЬ",
|
"market_portfolio": "ПОРТФЕЛЬ",
|
||||||
"market_price_loading": "Загрузка данных о ценах...",
|
"market_price_loading": "Загрузка данных о ценах...",
|
||||||
"market_price_unavailable": "Данные о ценах недоступны",
|
"market_price_unavailable": "Данные о ценах недоступны",
|
||||||
"market_refresh_price": "Обновить данные о ценах",
|
"market_refresh_price": "Обновить данные о ценах",
|
||||||
|
"market_settings_tip": "Параметры рынка",
|
||||||
|
"market_settings_title": "Настройки рынка",
|
||||||
|
"market_style_candle": "Переключить на свечи",
|
||||||
|
"market_style_candle_label": "Свечи",
|
||||||
|
"market_style_line": "Переключить на линейный график",
|
||||||
|
"market_style_line_label": "Линия",
|
||||||
"market_trade_on": "Торговать на %s",
|
"market_trade_on": "Торговать на %s",
|
||||||
"market_updated": "\\xc2\\xb7 Обновлено %s",
|
"market_updated": "\\xc2\\xb7 Обновлено %s",
|
||||||
"market_vol_short": "Объём",
|
"market_vol_short": "Объём",
|
||||||
@@ -736,6 +962,7 @@
|
|||||||
"output_filename": "Имя выходного файла:",
|
"output_filename": "Имя выходного файла:",
|
||||||
"overview": "Обзор",
|
"overview": "Обзор",
|
||||||
"paste": "Вставить",
|
"paste": "Вставить",
|
||||||
|
"paste_clip_empty": "Буфер обмена пуст",
|
||||||
"paste_from_clipboard": "Вставить из буфера обмена",
|
"paste_from_clipboard": "Вставить из буфера обмена",
|
||||||
"pay_from": "Оплатить с",
|
"pay_from": "Оплатить с",
|
||||||
"payment_request": "ЗАПРОС НА ОПЛАТУ",
|
"payment_request": "ЗАПРОС НА ОПЛАТУ",
|
||||||
@@ -787,7 +1014,14 @@
|
|||||||
"peers_upper": "УЗЛЫ",
|
"peers_upper": "УЗЛЫ",
|
||||||
"peers_version": "Версия",
|
"peers_version": "Версия",
|
||||||
"pending": "Ожидание",
|
"pending": "Ожидание",
|
||||||
|
"pin_change_desc": "Измените PIN-код разблокировки. Вам понадобится текущий PIN-код и новый PIN-код.",
|
||||||
|
"pin_confirm_new_label": "Подтвердите новый PIN-код:",
|
||||||
|
"pin_current_label": "Текущий PIN-код:",
|
||||||
|
"pin_new_label": "Новый PIN-код (4–8 цифр):",
|
||||||
"pin_not_set": "PIN не установлен. Используйте пароль для разблокировки.",
|
"pin_not_set": "PIN не установлен. Используйте пароль для разблокировки.",
|
||||||
|
"pin_remove_desc": "Введите текущий PIN-код для подтверждения удаления. Для разблокировки вам понадобится полный пароль.",
|
||||||
|
"pin_setup_desc": "Установите PIN-код из 4–8 цифр для быстрой разблокировки кошелька. Пароль вашего кошелька будет зашифрован этим PIN-кодом и сохранён локально.",
|
||||||
|
"pin_wallet_passphrase": "Пароль кошелька:",
|
||||||
"ping": "Пинг",
|
"ping": "Пинг",
|
||||||
"portfolio_add_entry": "Добавить запись",
|
"portfolio_add_entry": "Добавить запись",
|
||||||
"portfolio_add_to": "Добавить в портфель",
|
"portfolio_add_to": "Добавить в портфель",
|
||||||
@@ -826,6 +1060,9 @@
|
|||||||
"portfolio_remove_from": "Удалить из портфеля",
|
"portfolio_remove_from": "Удалить из портфеля",
|
||||||
"portfolio_revert": "Отменить изменения",
|
"portfolio_revert": "Отменить изменения",
|
||||||
"portfolio_save": "Сохранить",
|
"portfolio_save": "Сохранить",
|
||||||
|
"portfolio_save_need_address": "Добавьте хотя бы один адрес, чтобы сохранить.",
|
||||||
|
"portfolio_save_need_name": "Введите имя, чтобы сохранить эту группу.",
|
||||||
|
"portfolio_save_need_price": "Введите ручную цену больше 0, чтобы сохранить.",
|
||||||
"portfolio_search": "Поиск адресов…",
|
"portfolio_search": "Поиск адресов…",
|
||||||
"portfolio_search_icons": "Поиск иконок…",
|
"portfolio_search_icons": "Поиск иконок…",
|
||||||
"portfolio_select_all": "Все",
|
"portfolio_select_all": "Все",
|
||||||
@@ -839,11 +1076,12 @@
|
|||||||
"portfolio_spark_min": "Минута",
|
"portfolio_spark_min": "Минута",
|
||||||
"portfolio_spark_month": "Месяц",
|
"portfolio_spark_month": "Месяц",
|
||||||
"portfolio_spark_week": "Неделя",
|
"portfolio_spark_week": "Неделя",
|
||||||
"portfolio_style_compact": "Компактные строки",
|
"portfolio_style_compact": "Таблица",
|
||||||
"portfolio_style_detailed": "Подробные строки",
|
"portfolio_style_detailed": "Карточки",
|
||||||
"portfolio_style_featured": "Избранные строки",
|
"portfolio_style_featured": "Витрина",
|
||||||
"portfolio_style_label": "Стиль портфеля",
|
"portfolio_style_label": "Стиль портфеля",
|
||||||
"portfolio_untitled": "Без названия",
|
"portfolio_untitled": "Без названия",
|
||||||
|
"portfolio_wallet_loading": "Дождитесь загрузки кошелька, чтобы добавить группу.",
|
||||||
"price_chart": "График цен",
|
"price_chart": "График цен",
|
||||||
"privacy_great": "Отличная конфиденциальность!",
|
"privacy_great": "Отличная конфиденциальность!",
|
||||||
"privacy_low": "Низкая конфиденциальность — экранируйте средства",
|
"privacy_low": "Низкая конфиденциальность — экранируйте средства",
|
||||||
@@ -973,6 +1211,7 @@
|
|||||||
"send_amount": "Сумма",
|
"send_amount": "Сумма",
|
||||||
"send_amount_details": "ДЕТАЛИ СУММЫ",
|
"send_amount_details": "ДЕТАЛИ СУММЫ",
|
||||||
"send_amount_upper": "СУММА",
|
"send_amount_upper": "СУММА",
|
||||||
|
"send_cannot_send_now": "Сейчас отправить нельзя — проверьте соединение, синхронизацию и доступный баланс.",
|
||||||
"send_clear_fields": "Очистить все поля формы?",
|
"send_clear_fields": "Очистить все поля формы?",
|
||||||
"send_contacts_button": "Выбрать из контактов",
|
"send_contacts_button": "Выбрать из контактов",
|
||||||
"send_copy_error": "Копировать ошибку",
|
"send_copy_error": "Копировать ошибку",
|
||||||
@@ -1061,7 +1300,8 @@
|
|||||||
"settings_gradient_bg": "Градиент фона",
|
"settings_gradient_bg": "Градиент фона",
|
||||||
"settings_gradient_desc": "Заменить текстурные фоны плавными градиентами",
|
"settings_gradient_desc": "Заменить текстурные фоны плавными градиентами",
|
||||||
"settings_idle_after": "через",
|
"settings_idle_after": "через",
|
||||||
"settings_import_key": "Импортировать ключ...",
|
"settings_import_key": "Импорт приватного ключа...",
|
||||||
|
"settings_import_viewkey": "Импортировать ключ просмотра...",
|
||||||
"settings_language_note": "Примечание: Некоторый текст требует перезапуска для обновления",
|
"settings_language_note": "Примечание: Некоторый текст требует перезапуска для обновления",
|
||||||
"settings_lock_now": "Заблокировать сейчас",
|
"settings_lock_now": "Заблокировать сейчас",
|
||||||
"settings_locked": "Заблокирован",
|
"settings_locked": "Заблокирован",
|
||||||
@@ -1110,6 +1350,7 @@
|
|||||||
"shield_in_progress": "Операция выполняется...",
|
"shield_in_progress": "Операция выполняется...",
|
||||||
"shield_max_utxos": "Макс. UTXO за операцию",
|
"shield_max_utxos": "Макс. UTXO за операцию",
|
||||||
"shield_merge_done": "Экранирование/объединение завершено!",
|
"shield_merge_done": "Экранирование/объединение завершено!",
|
||||||
|
"shield_no_zaddr_hint": "Пока нет защищённого (z-)адреса — сначала создайте его на вкладке «Получить».",
|
||||||
"shield_op_failed": "Ошибка операции: ",
|
"shield_op_failed": "Ошибка операции: ",
|
||||||
"shield_op_submitted": "Операция отправлена: ",
|
"shield_op_submitted": "Операция отправлена: ",
|
||||||
"shield_operation_id": "ID операции: %s",
|
"shield_operation_id": "ID операции: %s",
|
||||||
@@ -1148,6 +1389,31 @@
|
|||||||
"submitting_transaction": "Отправка транзакции...",
|
"submitting_transaction": "Отправка транзакции...",
|
||||||
"success": "Успешно",
|
"success": "Успешно",
|
||||||
"summary": "Итоги",
|
"summary": "Итоги",
|
||||||
|
"sweep_button": "Перевести",
|
||||||
|
"sweep_caveat": "Импортирует ключ, чтобы подписать одну транзакцию, переводящую все его средства на ваш адрес. Ключ остаётся в кошельке с нулевым балансом.",
|
||||||
|
"sweep_dest_label": "Отправить переведённые средства на",
|
||||||
|
"sweep_dest_new": "Новый экранированный адрес (рекомендуется)",
|
||||||
|
"sweep_done": "Готово — средства переведены на ваш адрес.",
|
||||||
|
"sweep_to": "Переведено на:",
|
||||||
|
"sweep_toggle": "Перевести в мой кошелёк (не сохранять ключ)",
|
||||||
|
"sweep_tx": "Транзакция:",
|
||||||
|
"switch_corrupt_body": "Похоже, этот кошелёк повреждён — узел не смог его открыть. Восстановите из резервной копии, создайте заново или попробуйте восстановить.",
|
||||||
|
"switch_corrupt_repair": "Попробовать восстановить (salvage)",
|
||||||
|
"switch_progress_background": "Продолжить в фоне",
|
||||||
|
"switch_progress_default_wallet": "Кошелёк по умолчанию",
|
||||||
|
"switch_progress_elapsed": "Прошло",
|
||||||
|
"switch_progress_external_wallet": "Внешний кошелёк",
|
||||||
|
"switch_progress_failed_title": "Не удалось переключить кошелёк",
|
||||||
|
"switch_progress_from_label": "из",
|
||||||
|
"switch_progress_hint": "Корректное завершение работы может занять до минуты.",
|
||||||
|
"switch_progress_reconnecting": "Переподключение",
|
||||||
|
"switch_progress_starting": "Запуск узла с новым кошельком",
|
||||||
|
"switch_progress_stopping": "Остановка текущего узла",
|
||||||
|
"switch_progress_title": "Переключение кошелька",
|
||||||
|
"switch_stopnode_body": "Смена кошелька перезапускает узел с выбранным кошельком. Запущенный узел будет остановлен и перезапущен с новым кошельком — если вы намеренно оставили его работать, он запустится снова автоматически.",
|
||||||
|
"switch_stopnode_confirm": "Остановить узел и переключить",
|
||||||
|
"switch_stopnode_title": "Остановить запущенный узел?",
|
||||||
|
"switch_stopnode_warn": "Уже запущен узел, который не был запущен этим кошельком.",
|
||||||
"syncing": "Синхронизация...",
|
"syncing": "Синхронизация...",
|
||||||
"t_address": "T-адрес",
|
"t_address": "T-адрес",
|
||||||
"t_addresses": "T-адреса",
|
"t_addresses": "T-адреса",
|
||||||
@@ -1183,8 +1449,10 @@
|
|||||||
"transfer_to": "Перевести на:",
|
"transfer_to": "Перевести на:",
|
||||||
"transparent": "Прозрачный",
|
"transparent": "Прозрачный",
|
||||||
"transparent_address": "Прозрачный адрес",
|
"transparent_address": "Прозрачный адрес",
|
||||||
|
"try_again": "Повторить",
|
||||||
"tt_addr_url": "Базовый URL для просмотра адресов в обозревателе блоков",
|
"tt_addr_url": "Базовый URL для просмотра адресов в обозревателе блоков",
|
||||||
"tt_address_book": "Управление сохранёнными адресами для быстрой отправки",
|
"tt_address_book": "Управление сохранёнными адресами для быстрой отправки",
|
||||||
|
"tt_animate_avatars": "Воспроизводить анимированные аватары контактов (GIF / WebP); при отключении показывается только первый кадр",
|
||||||
"tt_auto_lock": "Заблокировать кошелёк после этого времени бездействия",
|
"tt_auto_lock": "Заблокировать кошелёк после этого времени бездействия",
|
||||||
"tt_auto_shield": "Автоматически перемещать прозрачный баланс на экранированные адреса для конфиденциальности",
|
"tt_auto_shield": "Автоматически перемещать прозрачный баланс на экранированные адреса для конфиденциальности",
|
||||||
"tt_backup": "Создать резервную копию вашего wallet.dat",
|
"tt_backup": "Создать резервную копию вашего wallet.dat",
|
||||||
@@ -1192,10 +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_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": "Развернуть параметры журнала отладки",
|
||||||
@@ -1209,10 +1487,34 @@
|
|||||||
"tt_font_scale": "Масштабировать весь текст и интерфейс (1.0x = по умолчанию, до 1.5x).",
|
"tt_font_scale": "Масштабировать весь текст и интерфейс (1.0x = по умолчанию, до 1.5x).",
|
||||||
"tt_idle_delay": "Сколько ждать перед началом майнинга",
|
"tt_idle_delay": "Сколько ждать перед началом майнинга",
|
||||||
"tt_import_key": "Импортировать приватный ключ (zkey или tkey) в этот кошелёк",
|
"tt_import_key": "Импортировать приватный ключ (zkey или tkey) в этот кошелёк",
|
||||||
|
"tt_import_viewkey": "Импортировать экранированный ключ просмотра для наблюдения за адресом (только чтение)",
|
||||||
"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 в один адрес",
|
||||||
@@ -1233,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": "Перевести прозрачные вознаграждения за майнинг на экранированный адрес",
|
||||||
@@ -1257,6 +1564,7 @@
|
|||||||
"tt_website": "Открыть сайт DragonX",
|
"tt_website": "Открыть сайт DragonX",
|
||||||
"tt_window_opacity": "Непрозрачность фона (ниже = рабочий стол виден сквозь окно)",
|
"tt_window_opacity": "Непрозрачность фона (ниже = рабочий стол виден сквозь окно)",
|
||||||
"tt_wizard": "Повторно запустить мастер начальной настройки\\nДемон будет перезапущен",
|
"tt_wizard": "Повторно запустить мастер начальной настройки\\nДемон будет перезапущен",
|
||||||
|
"tx_chat_badge": "Сообщение",
|
||||||
"tx_confirmations": "%d подтверждений",
|
"tx_confirmations": "%d подтверждений",
|
||||||
"tx_details_title": "Детали транзакции",
|
"tx_details_title": "Детали транзакции",
|
||||||
"tx_from_address": "Адрес отправителя:",
|
"tx_from_address": "Адрес отправителя:",
|
||||||
@@ -1325,6 +1633,8 @@
|
|||||||
"wallets_add_folder_toggle": "+ Искать кошельки в другой папке…",
|
"wallets_add_folder_toggle": "+ Искать кошельки в другой папке…",
|
||||||
"wallets_badge_encrypted": "Зашифрован (защищён паролем)",
|
"wallets_badge_encrypted": "Зашифрован (защищён паролем)",
|
||||||
"wallets_badge_encrypted_short": "Зашифрован",
|
"wallets_badge_encrypted_short": "Зашифрован",
|
||||||
|
"wallets_badge_hd": "HD-кошелёк — невозможно подтвердить seed-фразу без открытия",
|
||||||
|
"wallets_badge_hd_short": "HD-кошелёк",
|
||||||
"wallets_badge_legacy": "Устаревший кошелёк (без seed-фразы)",
|
"wallets_badge_legacy": "Устаревший кошелёк (без seed-фразы)",
|
||||||
"wallets_badge_legacy_short": "Устаревший",
|
"wallets_badge_legacy_short": "Устаревший",
|
||||||
"wallets_badge_seed": "Кошелёк с seed-фразой (HD)",
|
"wallets_badge_seed": "Кошелёк с seed-фразой (HD)",
|
||||||
@@ -1343,6 +1653,7 @@
|
|||||||
"wallets_created": "создан",
|
"wallets_created": "создан",
|
||||||
"wallets_creating": "Создание кошелька — узел будет перезапущен…",
|
"wallets_creating": "Создание кошелька — узел будет перезапущен…",
|
||||||
"wallets_current": "текущий",
|
"wallets_current": "текущий",
|
||||||
|
"wallets_empty_hint": "Просканируйте папку, чтобы найти другие кошельки",
|
||||||
"wallets_exists": "Кошелёк с таким именем уже существует.",
|
"wallets_exists": "Кошелёк с таким именем уже существует.",
|
||||||
"wallets_external_tt": "Вне каталога данных — «Открыть» создаёт ссылку на месте (без копирования).",
|
"wallets_external_tt": "Вне каталога данных — «Открыть» создаёт ссылку на месте (без копирования).",
|
||||||
"wallets_folder_hint": "/путь/к/папке с файлами кошельков .dat",
|
"wallets_folder_hint": "/путь/к/папке с файлами кошельков .dat",
|
||||||
@@ -1356,7 +1667,9 @@
|
|||||||
"wallets_open_failed": "Не удалось открыть этот кошелёк на месте. Вероятно, он на другом диске, чем каталог данных — переместите его на тот же диск (в Windows включённый режим разработчика также позволяет создавать ссылки между дисками).",
|
"wallets_open_failed": "Не удалось открыть этот кошелёк на месте. Вероятно, он на другом диске, чем каталог данных — переместите его на тот же диск (в Windows включённый режим разработчика также позволяет создавать ссылки между дисками).",
|
||||||
"wallets_open_folder": "Открыть расположение папки",
|
"wallets_open_folder": "Открыть расположение папки",
|
||||||
"wallets_open_inplace_tt": "Открывает этот кошелёк на месте — по ссылке в каталоге данных (без копирования)",
|
"wallets_open_inplace_tt": "Открывает этот кошелёк на месте — по ссылке в каталоге данных (без копирования)",
|
||||||
|
"wallets_remove_folder": "Больше не сканировать эту папку",
|
||||||
"wallets_reveal": "Открыть папку",
|
"wallets_reveal": "Открыть папку",
|
||||||
|
"wallets_scanned_folders": "Просканированные папки:",
|
||||||
"wallets_sort_addresses": "Адреса",
|
"wallets_sort_addresses": "Адреса",
|
||||||
"wallets_sort_asc": "По возрастанию (сначала старые / меньше / меньший)",
|
"wallets_sort_asc": "По возрастанию (сначала старые / меньше / меньший)",
|
||||||
"wallets_sort_by": "Сортировка:",
|
"wallets_sort_by": "Сортировка:",
|
||||||
|
|||||||
325
res/lang/zh.json
325
res/lang/zh.json
@@ -30,6 +30,7 @@
|
|||||||
"address_book_added": "地址已添加到通讯录",
|
"address_book_added": "地址已添加到通讯录",
|
||||||
"address_book_confirm_delete": "确认删除?",
|
"address_book_confirm_delete": "确认删除?",
|
||||||
"address_book_count": "已保存 %zu 个地址",
|
"address_book_count": "已保存 %zu 个地址",
|
||||||
|
"address_book_count_one": "已保存 %zu 个地址",
|
||||||
"address_book_deleted": "条目已删除",
|
"address_book_deleted": "条目已删除",
|
||||||
"address_book_edit": "编辑地址",
|
"address_book_edit": "编辑地址",
|
||||||
"address_book_empty": "没有保存的地址。点击'添加新地址'创建一个。",
|
"address_book_empty": "没有保存的地址。点击'添加新地址'创建一个。",
|
||||||
@@ -53,8 +54,20 @@
|
|||||||
"amount_details": "金额详情",
|
"amount_details": "金额详情",
|
||||||
"amount_exceeds_balance": "金额超过余额",
|
"amount_exceeds_balance": "金额超过余额",
|
||||||
"amount_label": "金额:",
|
"amount_label": "金额:",
|
||||||
|
"animate_avatars": "动画头像",
|
||||||
"appearance": "外观",
|
"appearance": "外观",
|
||||||
"auto_shield": "自动屏蔽挖矿",
|
"auto_shield": "自动屏蔽挖矿",
|
||||||
|
"av_intro": "挖矿软件经常被标记为潜在有害程序。请按照以下步骤启用矿池挖矿:",
|
||||||
|
"av_open_security": "打开 Windows 安全中心",
|
||||||
|
"av_step1": "第 1 步:添加排除项",
|
||||||
|
"av_step1_b1": "打开 Windows 安全中心 > 病毒和威胁防护",
|
||||||
|
"av_step1_b2": "单击 管理设置 > 排除项 > 添加或删除",
|
||||||
|
"av_step1_b3": "添加文件夹:%APPDATA%\\ObsidianDragon\\",
|
||||||
|
"av_step2": "第 2 步:从隔离区还原(如果需要)",
|
||||||
|
"av_step2_b1": "Windows 安全中心 > 保护历史记录",
|
||||||
|
"av_step2_b2": "找到 xmrig.exe 并单击“还原”",
|
||||||
|
"av_step3": "第 3 步:重启钱包并重试",
|
||||||
|
"av_title": "Windows Defender 已阻止矿工程序",
|
||||||
"available": "可用",
|
"available": "可用",
|
||||||
"backup_backing_up": "正在备份...",
|
"backup_backing_up": "正在备份...",
|
||||||
"backup_create": "创建备份",
|
"backup_create": "创建备份",
|
||||||
@@ -62,6 +75,7 @@
|
|||||||
"backup_data": "备份与数据",
|
"backup_data": "备份与数据",
|
||||||
"backup_description": "创建 wallet.dat 文件的备份。此文件包含您所有的私钥和交易历史。请将备份存放在安全的地方。",
|
"backup_description": "创建 wallet.dat 文件的备份。此文件包含您所有的私钥和交易历史。请将备份存放在安全的地方。",
|
||||||
"backup_destination": "备份目标:",
|
"backup_destination": "备份目标:",
|
||||||
|
"backup_overwrite_confirm": "该位置已存在文件,再次保存将覆盖它。",
|
||||||
"backup_source": "来源:%s",
|
"backup_source": "来源:%s",
|
||||||
"backup_tip_external": "将备份存储在外部驱动器或云存储中",
|
"backup_tip_external": "将备份存储在外部驱动器或云存储中",
|
||||||
"backup_tip_multiple": "在不同位置创建多个备份",
|
"backup_tip_multiple": "在不同位置创建多个备份",
|
||||||
@@ -70,6 +84,7 @@
|
|||||||
"backup_title": "备份钱包",
|
"backup_title": "备份钱包",
|
||||||
"backup_wallet": "备份钱包...",
|
"backup_wallet": "备份钱包...",
|
||||||
"backup_wallet_not_found": "警告:在预期位置未找到 wallet.dat",
|
"backup_wallet_not_found": "警告:在预期位置未找到 wallet.dat",
|
||||||
|
"backup_warn": "此文件包含您的所有私钥,请妥善保管。",
|
||||||
"balance": "余额",
|
"balance": "余额",
|
||||||
"balance_history_collecting": "余额历史——正在收集数据…",
|
"balance_history_collecting": "余额历史——正在收集数据…",
|
||||||
"balance_layout": "余额布局",
|
"balance_layout": "余额布局",
|
||||||
@@ -114,28 +129,105 @@
|
|||||||
"bootstrap_wallet_protected": "(wallet.dat 已受保护)",
|
"bootstrap_wallet_protected": "(wallet.dat 已受保护)",
|
||||||
"bootstrap_warning": "现有区块数据(blocks、chainstate、notarizations)将被删除并替换。您的 wallet.dat 不会被修改或删除。",
|
"bootstrap_warning": "现有区块数据(blocks、chainstate、notarizations)将被删除并替换。您的 wallet.dat 不会被修改或删除。",
|
||||||
"cancel": "取消",
|
"cancel": "取消",
|
||||||
|
"change_pass_confirm": "确认新密码:",
|
||||||
|
"change_pass_current": "当前密码短语:",
|
||||||
|
"change_pass_new": "新密码短语:",
|
||||||
|
"change_pass_title": "更改密码短语",
|
||||||
"characters": "字符",
|
"characters": "字符",
|
||||||
"chat": "聊天",
|
"chat": "聊天",
|
||||||
|
"chat_accent_amber": "琥珀色",
|
||||||
|
"chat_accent_blue": "蓝色",
|
||||||
|
"chat_accent_green": "绿色",
|
||||||
|
"chat_accent_pink": "粉色",
|
||||||
|
"chat_accent_purple": "紫色",
|
||||||
|
"chat_accent_theme": "主题",
|
||||||
|
"chat_add_contact": "添加联系人",
|
||||||
|
"chat_awaiting_key": "等待回复",
|
||||||
|
"chat_bubble_minimal": "极简",
|
||||||
|
"chat_bubble_rounded": "圆角",
|
||||||
|
"chat_bubble_square": "方形",
|
||||||
|
"chat_buffer_loading": "聊天缓冲:…",
|
||||||
|
"chat_buffer_preparing": "聊天缓冲:正在准备 %d/%d…",
|
||||||
|
"chat_buffer_ready": "聊天缓冲:%d/%d 已就绪",
|
||||||
|
"chat_buffer_sending": "聊天:正在发送 %d 条消息…",
|
||||||
|
"chat_buffer_sending_one": "聊天:正在发送 %d 条消息…",
|
||||||
"chat_cancel": "取消",
|
"chat_cancel": "取消",
|
||||||
|
"chat_contact_added": "已添加联系人——可在联系人中重命名",
|
||||||
"chat_contact_request": "联系人请求",
|
"chat_contact_request": "联系人请求",
|
||||||
|
"chat_copy_address_tip": "点击复制地址",
|
||||||
|
"chat_density_comfortable": "宽松",
|
||||||
|
"chat_density_compact": "紧凑",
|
||||||
|
"chat_emoji_color": "彩色",
|
||||||
|
"chat_emoji_mono": "单色",
|
||||||
|
"chat_emoji_search": "搜索表情",
|
||||||
"chat_empty_hint": "暂无对话。您收到的消息将显示在此处。",
|
"chat_empty_hint": "暂无对话。您收到的消息将显示在此处。",
|
||||||
|
"chat_empty_start": "点击\"新建会话\"开始。",
|
||||||
|
"chat_empty_title": "还没有会话",
|
||||||
|
"chat_export": "导出聊天…",
|
||||||
|
"chat_export_done": "会话已导出",
|
||||||
|
"chat_export_failed": "无法写入导出文件。",
|
||||||
|
"chat_export_warn": "将解密后的消息保存为纯文本。请妥善保管该文件。",
|
||||||
|
"chat_filter": "聊天",
|
||||||
|
"chat_hidden_toast": "会话已隐藏——收到新消息后会重新显示",
|
||||||
|
"chat_hide": "隐藏",
|
||||||
|
"chat_hide_hidden": "收起已隐藏",
|
||||||
|
"chat_jump_latest": "最新",
|
||||||
|
"chat_len_over": "消息过长",
|
||||||
"chat_locked_hint": "解锁钱包以加载您的聊天记录。",
|
"chat_locked_hint": "解锁钱包以加载您的聊天记录。",
|
||||||
"chat_new_button": "新建对话",
|
"chat_mute": "静音",
|
||||||
|
"chat_new_button": "新聊天",
|
||||||
"chat_new_message": "消息",
|
"chat_new_message": "消息",
|
||||||
|
"chat_new_message_toast": "新的加密聊天消息",
|
||||||
"chat_new_send": "发送请求",
|
"chat_new_send": "发送请求",
|
||||||
"chat_new_title": "新建对话",
|
"chat_new_title": "新聊天",
|
||||||
"chat_new_zaddr": "收款方 z 地址",
|
"chat_new_zaddr": "收款方 z 地址",
|
||||||
|
"chat_no_matches": "没有与搜索匹配的会话。",
|
||||||
|
"chat_no_z_contacts": "暂无使用隐私地址的联系人",
|
||||||
|
"chat_opt_bubble_accent": "气泡颜色",
|
||||||
|
"chat_opt_bubble_style": "气泡样式",
|
||||||
|
"chat_opt_density": "消息密度",
|
||||||
|
"chat_opt_emoji": "表情样式",
|
||||||
|
"chat_opt_enter_sends": "回车发送消息",
|
||||||
|
"chat_opt_font_size": "文字大小",
|
||||||
|
"chat_opt_global_clock": "全局时间格式",
|
||||||
|
"chat_opt_poll": "轮询频率",
|
||||||
|
"chat_opt_timestamp": "时间戳",
|
||||||
|
"chat_pick_contact": "从联系人中选择…",
|
||||||
|
"chat_rename": "重命名联系人",
|
||||||
|
"chat_rename_hint": "联系人名称",
|
||||||
|
"chat_renamed": "联系人已重命名",
|
||||||
|
"chat_retry": "重试",
|
||||||
|
"chat_search": "搜索会话",
|
||||||
|
"chat_sec_appearance": "外观",
|
||||||
|
"chat_sec_messaging": "消息",
|
||||||
"chat_select_hint": "选择一个对话以查看。",
|
"chat_select_hint": "选择一个对话以查看。",
|
||||||
"chat_send": "发送",
|
"chat_send": "发送",
|
||||||
"chat_send_failed": "未发送",
|
"chat_send_failed": "未发送",
|
||||||
|
"chat_sending": "发送中…",
|
||||||
|
"chat_settings_done": "完成",
|
||||||
|
"chat_settings_section": "聊天与联系人",
|
||||||
|
"chat_settings_tip": "聊天自定义",
|
||||||
|
"chat_settings_title": "聊天设置",
|
||||||
|
"chat_show_hidden": "显示已隐藏",
|
||||||
|
"chat_time_now": "刚刚",
|
||||||
"chat_toast_compose_failed": "无法编写该消息(内容过长?)。",
|
"chat_toast_compose_failed": "无法编写该消息(内容过长?)。",
|
||||||
"chat_toast_lite_busy": "已有发送正在进行中,或未打开任何钱包。",
|
"chat_toast_lite_busy": "已有发送正在进行中,或未打开任何钱包。",
|
||||||
|
"chat_toast_need_funds": "发送聊天需要少量屏蔽余额(用于支付手续费)。",
|
||||||
"chat_toast_no_zaddr": "没有可用于发送聊天的 z 地址。",
|
"chat_toast_no_zaddr": "没有可用于发送聊天的 z 地址。",
|
||||||
"chat_toast_not_connected": "未连接——聊天消息未发送。",
|
"chat_toast_not_connected": "未连接——聊天消息未发送。",
|
||||||
"chat_toast_request_compose_failed": "无法编写联系人请求(地址或文本无效?)。",
|
"chat_toast_request_compose_failed": "无法编写联系人请求(地址或文本无效?)。",
|
||||||
"chat_toast_request_queued": "联系人请求已排队。",
|
"chat_toast_request_queued": "联系人请求已排队。",
|
||||||
"chat_toast_waiting_reply": "等待该联系人回复——对方回复后您即可向其发送消息。",
|
"chat_toast_waiting_reply": "等待该联系人回复——对方回复后您即可向其发送消息。",
|
||||||
|
"chat_today": "今天",
|
||||||
|
"chat_ts_12h": "12小时",
|
||||||
|
"chat_ts_24h": "24小时",
|
||||||
|
"chat_ts_global": "跟随全局",
|
||||||
|
"chat_ts_global_short": "全局",
|
||||||
|
"chat_unhide": "取消隐藏",
|
||||||
|
"chat_unmute": "取消静音",
|
||||||
|
"chat_verify_key": "身份密钥 — 对比以验证",
|
||||||
"chat_waiting_reply": "等待该联系人回复——对方回复后您即可向其发送消息。",
|
"chat_waiting_reply": "等待该联系人回复——对方回复后您即可向其发送消息。",
|
||||||
|
"chat_yesterday": "昨天",
|
||||||
"chat_you": "我",
|
"chat_you": "我",
|
||||||
"choose_icon": "选择图标",
|
"choose_icon": "选择图标",
|
||||||
"clear": "清除",
|
"clear": "清除",
|
||||||
@@ -147,6 +239,7 @@
|
|||||||
"click_copy_address": "点击复制地址",
|
"click_copy_address": "点击复制地址",
|
||||||
"click_copy_uri": "点击复制 URI",
|
"click_copy_uri": "点击复制 URI",
|
||||||
"click_to_copy": "点击复制",
|
"click_to_copy": "点击复制",
|
||||||
|
"clock_format": "时间格式",
|
||||||
"close": "关闭",
|
"close": "关闭",
|
||||||
"conf_count": "%d 确认",
|
"conf_count": "%d 确认",
|
||||||
"confirm_and_send": "确认并发送",
|
"confirm_and_send": "确认并发送",
|
||||||
@@ -180,10 +273,24 @@
|
|||||||
"connected_peers": "已连接节点",
|
"connected_peers": "已连接节点",
|
||||||
"connecting": "连接中...",
|
"connecting": "连接中...",
|
||||||
"console": "控制台",
|
"console": "控制台",
|
||||||
|
"console_accents": "颜色强调",
|
||||||
"console_app": "应用",
|
"console_app": "应用",
|
||||||
"console_auto_scroll": "自动滚动",
|
"console_auto_scroll": "自动滚动",
|
||||||
"console_available_commands": "可用命令:",
|
"console_available_commands": "可用命令:",
|
||||||
|
"console_backend_reference": "后端命令参考",
|
||||||
|
"console_backend_unavailable": "无后端",
|
||||||
"console_capturing_output": "正在捕获守护进程输出...",
|
"console_capturing_output": "正在捕获守护进程输出...",
|
||||||
|
"console_cat_advanced": "高级",
|
||||||
|
"console_cat_blockchain": "区块链",
|
||||||
|
"console_cat_control": "控制",
|
||||||
|
"console_cat_keys": "密钥与安全",
|
||||||
|
"console_cat_mining": "挖矿",
|
||||||
|
"console_cat_network": "网络",
|
||||||
|
"console_cat_raw_transactions": "原始交易",
|
||||||
|
"console_cat_send": "发送",
|
||||||
|
"console_cat_sync": "同步",
|
||||||
|
"console_cat_utility": "实用工具",
|
||||||
|
"console_cat_wallet": "钱包",
|
||||||
"console_clear": "清除",
|
"console_clear": "清除",
|
||||||
"console_clear_console": "清除控制台",
|
"console_clear_console": "清除控制台",
|
||||||
"console_cleared": "控制台已清除",
|
"console_cleared": "控制台已清除",
|
||||||
@@ -215,18 +322,37 @@
|
|||||||
"console_help_help": " help - 显示此帮助信息",
|
"console_help_help": " help - 显示此帮助信息",
|
||||||
"console_help_setgenerate": " setgenerate - 控制挖矿",
|
"console_help_setgenerate": " setgenerate - 控制挖矿",
|
||||||
"console_help_stop": " stop - 停止守护进程",
|
"console_help_stop": " stop - 停止守护进程",
|
||||||
|
"console_last_error": "上次错误:",
|
||||||
"console_line_count": "%zu 行",
|
"console_line_count": "%zu 行",
|
||||||
"console_matches": "个匹配",
|
"console_matches": "个匹配",
|
||||||
"console_new_lines": "%d 新行",
|
"console_new_lines": "%d 新行",
|
||||||
"console_no_daemon": "无守护进程",
|
"console_no_daemon": "无守护进程",
|
||||||
|
"console_no_output": "(无输出)",
|
||||||
"console_not_connected": "错误:未连接到守护进程",
|
"console_not_connected": "错误:未连接到守护进程",
|
||||||
|
"console_not_connected_lite": "错误:没有打开的钱包",
|
||||||
"console_quit_note": "这里不需要 'quit'/'exit'——直接关闭窗口即可。",
|
"console_quit_note": "这里不需要 'quit'/'exit'——直接关闭窗口即可。",
|
||||||
|
"console_ref_builds": "生成",
|
||||||
|
"console_ref_cancel": "取消",
|
||||||
|
"console_ref_destructive": "谨慎",
|
||||||
|
"console_ref_example": "示例",
|
||||||
|
"console_ref_insert": "插入到控制台",
|
||||||
|
"console_ref_insert_run": "插入并运行",
|
||||||
|
"console_ref_no_match": "没有匹配的命令。",
|
||||||
|
"console_ref_no_params": "无需参数。",
|
||||||
|
"console_ref_optional": "可选",
|
||||||
|
"console_ref_parameters": "参数",
|
||||||
|
"console_ref_run": "运行",
|
||||||
|
"console_ref_run_confirm": "立即运行 %s?这是一个有重大影响的命令。",
|
||||||
|
"console_ref_search_hint": "按名称或用途搜索…",
|
||||||
|
"console_ref_select_hint": "选择一个命令以查看其功能。",
|
||||||
|
"console_ref_value": "值",
|
||||||
"console_rpc_reference": "RPC 命令参考",
|
"console_rpc_reference": "RPC 命令参考",
|
||||||
"console_rpc_trace": "RPC",
|
"console_rpc_trace": "RPC",
|
||||||
"console_scanline": "控制台扫描线",
|
"console_scanline": "控制台扫描线",
|
||||||
"console_search_commands": "搜索命令...",
|
"console_search_commands": "搜索命令...",
|
||||||
"console_select_all": "全选",
|
"console_select_all": "全选",
|
||||||
"console_show_app_output": "显示[应用]钱包日志行",
|
"console_show_app_output": "显示[应用]钱包日志行",
|
||||||
|
"console_show_backend_ref": "显示后端命令参考",
|
||||||
"console_show_daemon_output": "显示守护进程输出",
|
"console_show_daemon_output": "显示守护进程输出",
|
||||||
"console_show_errors_only": "仅显示错误",
|
"console_show_errors_only": "仅显示错误",
|
||||||
"console_show_rpc_ref": "显示 RPC 命令参考",
|
"console_show_rpc_ref": "显示 RPC 命令参考",
|
||||||
@@ -239,17 +365,43 @@
|
|||||||
"console_status_stopped": "已停止",
|
"console_status_stopped": "已停止",
|
||||||
"console_status_stopping": "停止中",
|
"console_status_stopping": "停止中",
|
||||||
"console_status_unknown": "未知",
|
"console_status_unknown": "未知",
|
||||||
|
"console_stop_confirm_node": "'stop' 将关闭节点并断开钱包连接。再次输入 'stop' 以确认。",
|
||||||
"console_tab_completion": "Tab 补全",
|
"console_tab_completion": "Tab 补全",
|
||||||
|
"console_text_colors": "文本颜色",
|
||||||
|
"console_toggle_accents": "切换行颜色强调",
|
||||||
|
"console_toggle_text_color": "切换行文本颜色",
|
||||||
"console_type_help": "输入 'help' 查看可用命令",
|
"console_type_help": "输入 'help' 查看可用命令",
|
||||||
"console_welcome": "欢迎使用 ObsidianDragon 控制台",
|
"console_welcome": "欢迎使用 ObsidianDragon 控制台",
|
||||||
"console_zoom_in": "放大",
|
"console_zoom_in": "放大",
|
||||||
"console_zoom_out": "缩小",
|
"console_zoom_out": "缩小",
|
||||||
|
"contact_avatar": "头像",
|
||||||
|
"contact_avatar_bad_image": "无法加载该图片。",
|
||||||
|
"contact_avatar_badge": "标记",
|
||||||
|
"contact_avatar_badge_hint": "标记会根据地址类型自动选择。",
|
||||||
|
"contact_avatar_choose": "选择图片…",
|
||||||
|
"contact_avatar_copy_failed": "无法复制该图片。",
|
||||||
|
"contact_avatar_icon": "图标",
|
||||||
|
"contact_avatar_image": "图片",
|
||||||
|
"contact_avatar_image_hint": "图片会复制到应用中,即使原文件移动也能保持可用。",
|
||||||
|
"contact_avatar_remove": "移除",
|
||||||
|
"contact_avatar_shielded": "隐蔽",
|
||||||
|
"contact_avatar_transparent": "透明",
|
||||||
"contact_global": "在每个钱包中显示(全局联系人)",
|
"contact_global": "在每个钱包中显示(全局联系人)",
|
||||||
"contact_global_badge_tt": "全局联系人——在每个钱包中可见",
|
"contact_global_badge_tt": "全局联系人——在每个钱包中可见",
|
||||||
"contact_global_tt": "开启:无论您加载哪个钱包,此联系人都保持可见。关闭:它仅属于当前钱包。",
|
"contact_global_tt": "开启:无论您加载哪个钱包,此联系人都保持可见。关闭:它仅属于当前钱包。",
|
||||||
|
"contact_preview_addr": "地址将显示在此处",
|
||||||
|
"contact_preview_name": "联系人名称",
|
||||||
|
"contact_wallet_loading": "钱包仍在加载——请选中“在每个钱包中显示”,或稍后再试。",
|
||||||
"contacts": "联系人",
|
"contacts": "联系人",
|
||||||
|
"contacts_avatar_shape": "头像形状",
|
||||||
|
"contacts_list_scale": "列表缩放",
|
||||||
"contacts_search_no_match": "没有匹配的联系人",
|
"contacts_search_no_match": "没有匹配的联系人",
|
||||||
"contacts_search_placeholder": "搜索联系人...",
|
"contacts_search_placeholder": "搜索联系人...",
|
||||||
|
"contacts_settings_tip": "联系人自定义",
|
||||||
|
"contacts_settings_title": "联系人设置",
|
||||||
|
"contacts_shape_circle": "圆形",
|
||||||
|
"contacts_shape_square": "方形",
|
||||||
|
"contacts_shape_tab": "左标签",
|
||||||
"copied": "已复制!",
|
"copied": "已复制!",
|
||||||
"copy": "复制",
|
"copy": "复制",
|
||||||
"copy_address": "复制完整地址",
|
"copy_address": "复制完整地址",
|
||||||
@@ -302,6 +454,19 @@
|
|||||||
"date": "日期",
|
"date": "日期",
|
||||||
"date_label": "日期:",
|
"date_label": "日期:",
|
||||||
"debug_logging": "调试日志",
|
"debug_logging": "调试日志",
|
||||||
|
"decrypt_desc": "钱包将被导出,守护进程将使用全新的未加密钱包重新启动,并重新导入所有密钥。根据钱包大小,这可能需要几分钟。",
|
||||||
|
"decrypt_error_title": "解密失败",
|
||||||
|
"decrypt_step_backup": "正在备份加密钱包",
|
||||||
|
"decrypt_step_export": "正在导出钱包密钥",
|
||||||
|
"decrypt_step_restart": "正在重启守护进程",
|
||||||
|
"decrypt_step_stop": "正在停止守护进程",
|
||||||
|
"decrypt_step_unlock": "正在解锁钱包",
|
||||||
|
"decrypt_success_desc": "您的钱包现在未加密。加密钱包的备份已保存为 wallet.dat.encrypted.bak,位于您的数据目录中。",
|
||||||
|
"decrypt_success_title": "钱包解密成功!",
|
||||||
|
"decrypt_title": "移除钱包加密",
|
||||||
|
"decrypt_wait_general": "请稍候。守护进程正在导出密钥、重启并重新导入。这可能需要几分钟。",
|
||||||
|
"decrypt_wait_restart": "正在等待守护进程完成启动...",
|
||||||
|
"decrypt_warning": "这将移除您钱包的加密。您的私钥将以未受保护的方式存储在磁盘上。",
|
||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
"delete_blockchain": "删除区块链",
|
"delete_blockchain": "删除区块链",
|
||||||
"delete_blockchain_confirm": "删除并重新同步",
|
"delete_blockchain_confirm": "删除并重新同步",
|
||||||
@@ -314,6 +479,15 @@
|
|||||||
"download_bootstrap": "下载引导程序",
|
"download_bootstrap": "下载引导程序",
|
||||||
"dragonx_green": "DragonX(绿色)",
|
"dragonx_green": "DragonX(绿色)",
|
||||||
"edit": "编辑",
|
"edit": "编辑",
|
||||||
|
"enc_confirm": "确认:",
|
||||||
|
"enc_desc": "加密钱包会用密码短语保护您的私钥。加密后,守护进程将重新启动。",
|
||||||
|
"enc_encrypting": "正在加密钱包...",
|
||||||
|
"enc_pin_desc": "4-8 位 PIN 码可让您无需每次输入完整密码短语即可解锁钱包。",
|
||||||
|
"enc_pin_set_ok": "PIN 码设置成功",
|
||||||
|
"enc_pin_skipped": "已跳过 PIN。您可以稍后在设置中设置。",
|
||||||
|
"enc_pin_vault_fail": "无法创建 PIN 保险库",
|
||||||
|
"enc_success": "钱包加密成功!",
|
||||||
|
"enc_wait": "请稍候,不要关闭应用程序。",
|
||||||
"error": "错误",
|
"error": "错误",
|
||||||
"error_format": "错误:%s",
|
"error_format": "错误:%s",
|
||||||
"est_time_to_block": "预计出块时间",
|
"est_time_to_block": "预计出块时间",
|
||||||
@@ -333,23 +507,34 @@
|
|||||||
"explorer_mempool": "内存池",
|
"explorer_mempool": "内存池",
|
||||||
"explorer_mempool_size": "大小",
|
"explorer_mempool_size": "大小",
|
||||||
"explorer_mempool_txs": "交易",
|
"explorer_mempool_txs": "交易",
|
||||||
|
"explorer_next_block": "下一个区块",
|
||||||
"explorer_no_results": "没有匹配的缓存区块",
|
"explorer_no_results": "没有匹配的缓存区块",
|
||||||
"explorer_not_connected": "未连接到守护进程——无法查询区块或交易哈希",
|
"explorer_not_connected": "未连接到守护进程——无法查询区块或交易哈希",
|
||||||
|
"explorer_prev_block": "上一个区块",
|
||||||
"explorer_recent_blocks": "最近区块",
|
"explorer_recent_blocks": "最近区块",
|
||||||
"explorer_search": "搜索",
|
"explorer_search": "搜索",
|
||||||
"explorer_section": "浏览器",
|
"explorer_section": "浏览器",
|
||||||
"explorer_tx_outputs": "输出",
|
"explorer_tx_outputs": "输出",
|
||||||
"explorer_tx_size": "大小",
|
"explorer_tx_size": "大小",
|
||||||
|
"explorer_txid": "TxID:",
|
||||||
"export": "导出",
|
"export": "导出",
|
||||||
"export_csv": "导出 CSV",
|
"export_csv": "导出 CSV",
|
||||||
"export_keys_btn": "导出密钥",
|
"export_keys_btn": "导出密钥",
|
||||||
"export_keys_danger": "危险:这将导出您钱包中的所有私钥!任何获得此文件的人都可以窃取您的资金。请安全保管并在使用后删除。",
|
"export_keys_danger": "危险:这将导出您钱包中的所有私钥!任何获得此文件的人都可以窃取您的资金。请安全保管并在使用后删除。",
|
||||||
"export_keys_include_t": "包含 T 地址(透明)",
|
"export_keys_include_t": "包含 T 地址(透明)",
|
||||||
"export_keys_include_z": "包含 Z 地址(屏蔽)",
|
"export_keys_include_z": "包含 Z 地址(屏蔽)",
|
||||||
|
"export_keys_none_addrs": "没有可导出的地址",
|
||||||
|
"export_keys_none_result": "未导出任何密钥(%d 个中 0 个)——请解锁钱包后重试。",
|
||||||
|
"export_keys_none_toast": "无法导出密钥——钱包已解锁吗?",
|
||||||
|
"export_keys_not_connected": "未连接到守护进程",
|
||||||
"export_keys_options": "导出选项:",
|
"export_keys_options": "导出选项:",
|
||||||
|
"export_keys_partial": "已导出 %d/%d 个密钥——不完整(部分没有花费密钥,或钱包已锁定)。",
|
||||||
|
"export_keys_partial_toast": "部分导出:%d/%d 个密钥",
|
||||||
"export_keys_progress": "正在导出 %d/%d...",
|
"export_keys_progress": "正在导出 %d/%d...",
|
||||||
|
"export_keys_select_type": "请至少选择一种地址类型",
|
||||||
"export_keys_success": "密钥导出成功",
|
"export_keys_success": "密钥导出成功",
|
||||||
"export_keys_title": "导出所有私钥",
|
"export_keys_title": "导出所有私钥",
|
||||||
|
"export_keys_write_fail": "写入密钥文件失败。",
|
||||||
"export_private_key": "导出私钥",
|
"export_private_key": "导出私钥",
|
||||||
"export_tx_count": "导出 %zu 笔交易到 CSV 文件。",
|
"export_tx_count": "导出 %zu 笔交易到 CSV 文件。",
|
||||||
"export_tx_file_fail": "无法创建 CSV 文件",
|
"export_tx_file_fail": "无法创建 CSV 文件",
|
||||||
@@ -389,23 +574,50 @@
|
|||||||
"hide_qr": "隐藏二维码",
|
"hide_qr": "隐藏二维码",
|
||||||
"hide_zero_balances": "隐藏零余额",
|
"hide_zero_balances": "隐藏零余额",
|
||||||
"history": "历史",
|
"history": "历史",
|
||||||
|
"img_picker_count": "此文件夹中的图片:%d",
|
||||||
|
"img_picker_empty": "此文件夹没有子文件夹或图片。",
|
||||||
|
"img_picker_none": "此文件夹中没有图片",
|
||||||
|
"img_picker_pictures": "图片文件夹",
|
||||||
|
"img_picker_title": "选择图片",
|
||||||
|
"img_picker_use": "使用图片",
|
||||||
"immature_type": "未成熟",
|
"immature_type": "未成熟",
|
||||||
"import": "导入",
|
"import": "导入",
|
||||||
|
"import_key_address": "地址:",
|
||||||
"import_key_btn": "导入密钥",
|
"import_key_btn": "导入密钥",
|
||||||
|
"import_key_done": "已导入。钱包正在重新扫描。",
|
||||||
|
"import_key_field": "密钥",
|
||||||
"import_key_formats": "支持的密钥格式:",
|
"import_key_formats": "支持的密钥格式:",
|
||||||
"import_key_full_rescan": "(0 = 完整重扫)",
|
"import_key_full_rescan": "(0 = 完整重扫)",
|
||||||
|
"import_key_import": "导入",
|
||||||
"import_key_label": "私钥:",
|
"import_key_label": "私钥:",
|
||||||
|
"import_key_need_node": "请连接一个运行中的节点以导入密钥。",
|
||||||
"import_key_no_valid": "输入中未找到有效密钥",
|
"import_key_no_valid": "输入中未找到有效密钥",
|
||||||
"import_key_progress": "正在导入 %d/%d...",
|
"import_key_progress": "正在导入 %d/%d...",
|
||||||
"import_key_rescan": "导入后重新扫描区块链",
|
"import_key_rescan": "导入后重新扫描区块链",
|
||||||
|
"import_key_rescanning": "正在导入并重新扫描——可能需要几分钟",
|
||||||
|
"import_key_reveal_tip": "显示/隐藏密钥",
|
||||||
"import_key_start_height": "起始高度:",
|
"import_key_start_height": "起始高度:",
|
||||||
"import_key_success": "密钥导入成功",
|
"import_key_success": "密钥导入成功",
|
||||||
"import_key_t_format": "T 地址 WIF 私钥",
|
"import_key_t_format": "T 地址 WIF 私钥",
|
||||||
"import_key_title": "导入私钥",
|
"import_key_title": "导入私钥",
|
||||||
"import_key_tooltip": "输入一个或多个私钥,每行一个。\n支持 z 地址和 t 地址密钥。\n以 # 开头的行视为注释。",
|
"import_key_tooltip": "输入一个或多个私钥,每行一个。\n支持 z 地址和 t 地址密钥。\n以 # 开头的行视为注释。",
|
||||||
|
"import_key_type_tkey": "透明私钥",
|
||||||
|
"import_key_type_unknown": "无法识别的密钥格式",
|
||||||
|
"import_key_type_zspend": "屏蔽花费密钥",
|
||||||
|
"import_key_type_zview": "屏蔽查看密钥(仅查看)",
|
||||||
|
"import_key_warn": "只导入你自己拥有的密钥——它可访问该地址的资金。",
|
||||||
"import_key_warning": "警告:切勿分享您的私钥!从不可信来源导入密钥可能会危及您的钱包安全。",
|
"import_key_warning": "警告:切勿分享您的私钥!从不可信来源导入密钥可能会危及您的钱包安全。",
|
||||||
|
"import_key_wrong_type": "这看起来像查看密钥。请改用\"导入查看密钥\"。",
|
||||||
"import_key_z_format": "Z 地址花费密钥 (secret-extended-key-...)",
|
"import_key_z_format": "Z 地址花费密钥 (secret-extended-key-...)",
|
||||||
"import_private_key": "导入私钥...",
|
"import_private_key": "导入私钥...",
|
||||||
|
"import_scan_hint": "0 = 从头重新扫描",
|
||||||
|
"import_scan_label": "从区块高度开始扫描(可选)",
|
||||||
|
"import_scan_tip": "当前高度",
|
||||||
|
"import_scan_transparent": "透明密钥始终完整重新扫描",
|
||||||
|
"import_viewkey_field": "查看密钥",
|
||||||
|
"import_viewkey_note": "仅查看:查看密钥可显示地址的余额和交易,但无法花费其资金。",
|
||||||
|
"import_viewkey_title": "导入查看密钥",
|
||||||
|
"import_viewkey_wrong_type": "这看起来像花费密钥。请改用\"导入私钥\"。",
|
||||||
"incorrect_passphrase": "密码错误",
|
"incorrect_passphrase": "密码错误",
|
||||||
"incorrect_pin": "PIN 错误",
|
"incorrect_pin": "PIN 错误",
|
||||||
"insufficient_funds": "余额不足以支付此金额加手续费。",
|
"insufficient_funds": "余额不足以支付此金额加手续费。",
|
||||||
@@ -414,6 +626,7 @@
|
|||||||
"keep": "保留",
|
"keep": "保留",
|
||||||
"keep_daemon": "保持守护进程运行",
|
"keep_daemon": "保持守护进程运行",
|
||||||
"key_export_click_retrieve": "点击从钱包中获取密钥",
|
"key_export_click_retrieve": "点击从钱包中获取密钥",
|
||||||
|
"key_export_failed": "无法导出密钥——请解锁钱包(如已加密)后重试。",
|
||||||
"key_export_fetching": "正在从钱包获取密钥...",
|
"key_export_fetching": "正在从钱包获取密钥...",
|
||||||
"key_export_private_key": "私钥:",
|
"key_export_private_key": "私钥:",
|
||||||
"key_export_private_warning": "请保密此密钥!任何拥有此密钥的人都可以花费您的资金。切勿在网上或与不可信的人分享。",
|
"key_export_private_warning": "请保密此密钥!任何拥有此密钥的人都可以花费您的资金。切勿在网上或与不可信的人分享。",
|
||||||
@@ -431,6 +644,7 @@
|
|||||||
"lite_birthday_backup": "生日区块:%llu (也请一并备份)",
|
"lite_birthday_backup": "生日区块:%llu (也请一并备份)",
|
||||||
"lite_birthday_hint": "开始扫描的区块高度。如未知请保留 0(完整扫描更慢)。",
|
"lite_birthday_hint": "开始扫描的区块高度。如未知请保留 0(完整扫描更慢)。",
|
||||||
"lite_birthday_label": "诞生区块",
|
"lite_birthday_label": "诞生区块",
|
||||||
|
"lite_console_backend_commands": "后端命令:",
|
||||||
"lite_console_help_passthrough": "其他任何输入都将作为轻钱包控制台命令运行。",
|
"lite_console_help_passthrough": "其他任何输入都将作为轻钱包控制台命令运行。",
|
||||||
"lite_copy": "复制",
|
"lite_copy": "复制",
|
||||||
"lite_could_not_write": "无法写入 ",
|
"lite_could_not_write": "无法写入 ",
|
||||||
@@ -448,6 +662,7 @@
|
|||||||
"lite_net_add_url_hint": "https://your-lite-server",
|
"lite_net_add_url_hint": "https://your-lite-server",
|
||||||
"lite_net_checking": "检查中…",
|
"lite_net_checking": "检查中…",
|
||||||
"lite_net_connected": "已连接",
|
"lite_net_connected": "已连接",
|
||||||
|
"lite_net_connecting": "连接中…",
|
||||||
"lite_net_custom": "自定义",
|
"lite_net_custom": "自定义",
|
||||||
"lite_net_disconnected": "未连接",
|
"lite_net_disconnected": "未连接",
|
||||||
"lite_net_hidden_section": "隐藏的服务器",
|
"lite_net_hidden_section": "隐藏的服务器",
|
||||||
@@ -534,6 +749,10 @@
|
|||||||
"market_btc_price": "BTC 价格",
|
"market_btc_price": "BTC 价格",
|
||||||
"market_cap": "市值",
|
"market_cap": "市值",
|
||||||
"market_cap_short": "市值",
|
"market_cap_short": "市值",
|
||||||
|
"market_chart_loading": "正在加载价格历史",
|
||||||
|
"market_col_name": "名称",
|
||||||
|
"market_col_trend": "趋势",
|
||||||
|
"market_col_value": "价值",
|
||||||
"market_iv_1d": "1天",
|
"market_iv_1d": "1天",
|
||||||
"market_iv_1h": "1时",
|
"market_iv_1h": "1时",
|
||||||
"market_iv_1m": "1M",
|
"market_iv_1m": "1M",
|
||||||
@@ -542,11 +761,18 @@
|
|||||||
"market_no_history": "无价格历史",
|
"market_no_history": "无价格历史",
|
||||||
"market_no_price": "无价格数据",
|
"market_no_price": "无价格数据",
|
||||||
"market_now": "现在",
|
"market_now": "现在",
|
||||||
|
"market_opt_chart_style": "图表样式",
|
||||||
"market_pct_shielded": "%.0f%% 屏蔽",
|
"market_pct_shielded": "%.0f%% 屏蔽",
|
||||||
"market_portfolio": "投资组合",
|
"market_portfolio": "投资组合",
|
||||||
"market_price_loading": "正在加载价格数据...",
|
"market_price_loading": "正在加载价格数据...",
|
||||||
"market_price_unavailable": "价格数据不可用",
|
"market_price_unavailable": "价格数据不可用",
|
||||||
"market_refresh_price": "刷新价格数据",
|
"market_refresh_price": "刷新价格数据",
|
||||||
|
"market_settings_tip": "市场选项",
|
||||||
|
"market_settings_title": "市场设置",
|
||||||
|
"market_style_candle": "切换到蜡烛图",
|
||||||
|
"market_style_candle_label": "K线",
|
||||||
|
"market_style_line": "切换到折线图",
|
||||||
|
"market_style_line_label": "折线",
|
||||||
"market_trade_on": "在 %s 交易",
|
"market_trade_on": "在 %s 交易",
|
||||||
"market_updated": "\\xc2\\xb7 已更新 %s",
|
"market_updated": "\\xc2\\xb7 已更新 %s",
|
||||||
"market_vol_short": "成交量",
|
"market_vol_short": "成交量",
|
||||||
@@ -736,6 +962,7 @@
|
|||||||
"output_filename": "输出文件名:",
|
"output_filename": "输出文件名:",
|
||||||
"overview": "概览",
|
"overview": "概览",
|
||||||
"paste": "粘贴",
|
"paste": "粘贴",
|
||||||
|
"paste_clip_empty": "剪贴板为空",
|
||||||
"paste_from_clipboard": "从剪贴板粘贴",
|
"paste_from_clipboard": "从剪贴板粘贴",
|
||||||
"pay_from": "付款来源",
|
"pay_from": "付款来源",
|
||||||
"payment_request": "付款请求",
|
"payment_request": "付款请求",
|
||||||
@@ -787,7 +1014,14 @@
|
|||||||
"peers_upper": "节点",
|
"peers_upper": "节点",
|
||||||
"peers_version": "版本",
|
"peers_version": "版本",
|
||||||
"pending": "待处理",
|
"pending": "待处理",
|
||||||
|
"pin_change_desc": "更改您的解锁 PIN 码。您需要当前 PIN 码和新 PIN 码。",
|
||||||
|
"pin_confirm_new_label": "确认新 PIN 码:",
|
||||||
|
"pin_current_label": "当前 PIN 码:",
|
||||||
|
"pin_new_label": "新 PIN 码(4-8 位):",
|
||||||
"pin_not_set": "未设置 PIN。使用密码解锁。",
|
"pin_not_set": "未设置 PIN。使用密码解锁。",
|
||||||
|
"pin_remove_desc": "输入当前 PIN 码以确认移除。解锁时您需要使用完整的密码短语。",
|
||||||
|
"pin_setup_desc": "设置一个 4-8 位 PIN 码以快速解锁钱包。您的钱包密码短语将使用此 PIN 码加密并存储在本地。",
|
||||||
|
"pin_wallet_passphrase": "钱包密码短语:",
|
||||||
"ping": "延迟",
|
"ping": "延迟",
|
||||||
"portfolio_add_entry": "添加条目",
|
"portfolio_add_entry": "添加条目",
|
||||||
"portfolio_add_to": "添加到投资组合",
|
"portfolio_add_to": "添加到投资组合",
|
||||||
@@ -826,6 +1060,9 @@
|
|||||||
"portfolio_remove_from": "从投资组合中移除",
|
"portfolio_remove_from": "从投资组合中移除",
|
||||||
"portfolio_revert": "还原",
|
"portfolio_revert": "还原",
|
||||||
"portfolio_save": "保存",
|
"portfolio_save": "保存",
|
||||||
|
"portfolio_save_need_address": "添加至少一个地址以保存。",
|
||||||
|
"portfolio_save_need_name": "输入名称以保存此分组。",
|
||||||
|
"portfolio_save_need_price": "输入大于 0 的手动价格以保存。",
|
||||||
"portfolio_search": "搜索地址…",
|
"portfolio_search": "搜索地址…",
|
||||||
"portfolio_search_icons": "搜索图标…",
|
"portfolio_search_icons": "搜索图标…",
|
||||||
"portfolio_select_all": "全部",
|
"portfolio_select_all": "全部",
|
||||||
@@ -839,11 +1076,12 @@
|
|||||||
"portfolio_spark_min": "分钟",
|
"portfolio_spark_min": "分钟",
|
||||||
"portfolio_spark_month": "月",
|
"portfolio_spark_month": "月",
|
||||||
"portfolio_spark_week": "周",
|
"portfolio_spark_week": "周",
|
||||||
"portfolio_style_compact": "紧凑行",
|
"portfolio_style_compact": "表格",
|
||||||
"portfolio_style_detailed": "详细行",
|
"portfolio_style_detailed": "卡片",
|
||||||
"portfolio_style_featured": "特色行",
|
"portfolio_style_featured": "聚焦",
|
||||||
"portfolio_style_label": "投资组合样式",
|
"portfolio_style_label": "投资组合样式",
|
||||||
"portfolio_untitled": "未命名",
|
"portfolio_untitled": "未命名",
|
||||||
|
"portfolio_wallet_loading": "请等待钱包加载完成后再添加分组。",
|
||||||
"price_chart": "价格图表",
|
"price_chart": "价格图表",
|
||||||
"privacy_great": "隐私性极佳!",
|
"privacy_great": "隐私性极佳!",
|
||||||
"privacy_low": "隐私性低——请屏蔽资金",
|
"privacy_low": "隐私性低——请屏蔽资金",
|
||||||
@@ -973,6 +1211,7 @@
|
|||||||
"send_amount": "金额",
|
"send_amount": "金额",
|
||||||
"send_amount_details": "金额详情",
|
"send_amount_details": "金额详情",
|
||||||
"send_amount_upper": "金额",
|
"send_amount_upper": "金额",
|
||||||
|
"send_cannot_send_now": "现在无法发送 — 请检查连接、同步和可用余额。",
|
||||||
"send_clear_fields": "清除所有表单字段?",
|
"send_clear_fields": "清除所有表单字段?",
|
||||||
"send_contacts_button": "从联系人中选择",
|
"send_contacts_button": "从联系人中选择",
|
||||||
"send_copy_error": "复制错误",
|
"send_copy_error": "复制错误",
|
||||||
@@ -1061,7 +1300,8 @@
|
|||||||
"settings_gradient_bg": "渐变背景",
|
"settings_gradient_bg": "渐变背景",
|
||||||
"settings_gradient_desc": "用平滑渐变替换纹理背景",
|
"settings_gradient_desc": "用平滑渐变替换纹理背景",
|
||||||
"settings_idle_after": "之后",
|
"settings_idle_after": "之后",
|
||||||
"settings_import_key": "导入密钥...",
|
"settings_import_key": "导入私钥...",
|
||||||
|
"settings_import_viewkey": "导入查看密钥...",
|
||||||
"settings_language_note": "注意:部分文本需要重启才能更新",
|
"settings_language_note": "注意:部分文本需要重启才能更新",
|
||||||
"settings_lock_now": "立即锁定",
|
"settings_lock_now": "立即锁定",
|
||||||
"settings_locked": "已锁定",
|
"settings_locked": "已锁定",
|
||||||
@@ -1110,6 +1350,7 @@
|
|||||||
"shield_in_progress": "操作进行中...",
|
"shield_in_progress": "操作进行中...",
|
||||||
"shield_max_utxos": "每次操作最大 UTXO 数",
|
"shield_max_utxos": "每次操作最大 UTXO 数",
|
||||||
"shield_merge_done": "屏蔽/合并完成!",
|
"shield_merge_done": "屏蔽/合并完成!",
|
||||||
|
"shield_no_zaddr_hint": "还没有屏蔽(z)地址 — 请先在“接收”标签页创建一个。",
|
||||||
"shield_op_failed": "操作失败:",
|
"shield_op_failed": "操作失败:",
|
||||||
"shield_op_submitted": "操作已提交:",
|
"shield_op_submitted": "操作已提交:",
|
||||||
"shield_operation_id": "操作 ID:%s",
|
"shield_operation_id": "操作 ID:%s",
|
||||||
@@ -1148,6 +1389,31 @@
|
|||||||
"submitting_transaction": "正在提交交易...",
|
"submitting_transaction": "正在提交交易...",
|
||||||
"success": "成功",
|
"success": "成功",
|
||||||
"summary": "摘要",
|
"summary": "摘要",
|
||||||
|
"sweep_button": "归集",
|
||||||
|
"sweep_caveat": "导入密钥以签署一笔将其所有资金转移到您地址的交易。密钥将保留在钱包中,余额为零。",
|
||||||
|
"sweep_dest_label": "将归集资金发送到",
|
||||||
|
"sweep_dest_new": "新的屏蔽地址(推荐)",
|
||||||
|
"sweep_done": "完成 — 资金已归集到您的地址。",
|
||||||
|
"sweep_to": "归集到:",
|
||||||
|
"sweep_toggle": "归集到我的钱包(不保留密钥)",
|
||||||
|
"sweep_tx": "交易:",
|
||||||
|
"switch_corrupt_body": "此钱包似乎已损坏——节点无法打开它。请从备份恢复、重新创建,或尝试修复。",
|
||||||
|
"switch_corrupt_repair": "尝试修复(salvage)",
|
||||||
|
"switch_progress_background": "在后台继续",
|
||||||
|
"switch_progress_default_wallet": "默认钱包",
|
||||||
|
"switch_progress_elapsed": "已用时",
|
||||||
|
"switch_progress_external_wallet": "外部钱包",
|
||||||
|
"switch_progress_failed_title": "切换钱包失败",
|
||||||
|
"switch_progress_from_label": "来自",
|
||||||
|
"switch_progress_hint": "正常关闭最多可能需要一分钟。",
|
||||||
|
"switch_progress_reconnecting": "正在重新连接",
|
||||||
|
"switch_progress_starting": "正在以新钱包启动节点",
|
||||||
|
"switch_progress_stopping": "正在停止当前节点",
|
||||||
|
"switch_progress_title": "正在切换钱包",
|
||||||
|
"switch_stopnode_body": "切换钱包会以所选钱包重启节点。正在运行的节点将被停止并以新钱包重新启动——如果你是特意让它保持运行的,它会自动重新启动。",
|
||||||
|
"switch_stopnode_confirm": "停止节点并切换",
|
||||||
|
"switch_stopnode_title": "停止正在运行的节点?",
|
||||||
|
"switch_stopnode_warn": "已有一个并非由此钱包启动的节点正在运行。",
|
||||||
"syncing": "同步中...",
|
"syncing": "同步中...",
|
||||||
"t_address": "T 地址",
|
"t_address": "T 地址",
|
||||||
"t_addresses": "T 地址",
|
"t_addresses": "T 地址",
|
||||||
@@ -1183,8 +1449,10 @@
|
|||||||
"transfer_to": "转账至:",
|
"transfer_to": "转账至:",
|
||||||
"transparent": "透明",
|
"transparent": "透明",
|
||||||
"transparent_address": "透明地址",
|
"transparent_address": "透明地址",
|
||||||
|
"try_again": "重试",
|
||||||
"tt_addr_url": "在区块浏览器中查看地址的基础 URL",
|
"tt_addr_url": "在区块浏览器中查看地址的基础 URL",
|
||||||
"tt_address_book": "管理已保存的地址以快速发送",
|
"tt_address_book": "管理已保存的地址以快速发送",
|
||||||
|
"tt_animate_avatars": "播放动画联系人头像(GIF / WebP);关闭时仅显示第一帧",
|
||||||
"tt_auto_lock": "在此不活动时间后锁定钱包",
|
"tt_auto_lock": "在此不活动时间后锁定钱包",
|
||||||
"tt_auto_shield": "自动将透明余额转移到屏蔽地址以增强隐私",
|
"tt_auto_shield": "自动将透明余额转移到屏蔽地址以增强隐私",
|
||||||
"tt_backup": "创建 wallet.dat 的备份",
|
"tt_backup": "创建 wallet.dat 的备份",
|
||||||
@@ -1192,10 +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_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": "展开调试日志选项",
|
||||||
@@ -1209,10 +1487,34 @@
|
|||||||
"tt_font_scale": "缩放所有文本和界面(1.0x = 默认,最大 1.5x)。",
|
"tt_font_scale": "缩放所有文本和界面(1.0x = 默认,最大 1.5x)。",
|
||||||
"tt_idle_delay": "开始挖矿前等待多长时间",
|
"tt_idle_delay": "开始挖矿前等待多长时间",
|
||||||
"tt_import_key": "将私钥(zkey 或 tkey)导入此钱包",
|
"tt_import_key": "将私钥(zkey 或 tkey)导入此钱包",
|
||||||
|
"tt_import_viewkey": "导入屏蔽查看密钥以查看某个地址(只读)",
|
||||||
"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 合并到一个地址",
|
||||||
@@ -1233,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": "将透明挖矿奖励转移到屏蔽地址",
|
||||||
@@ -1257,6 +1564,7 @@
|
|||||||
"tt_website": "打开 DragonX 网站",
|
"tt_website": "打开 DragonX 网站",
|
||||||
"tt_window_opacity": "背景不透明度(越低 = 桌面透过窗口可见)",
|
"tt_window_opacity": "背景不透明度(越低 = 桌面透过窗口可见)",
|
||||||
"tt_wizard": "重新运行初始设置向导\\n守护进程将被重启",
|
"tt_wizard": "重新运行初始设置向导\\n守护进程将被重启",
|
||||||
|
"tx_chat_badge": "消息",
|
||||||
"tx_confirmations": "%d 次确认",
|
"tx_confirmations": "%d 次确认",
|
||||||
"tx_details_title": "交易详情",
|
"tx_details_title": "交易详情",
|
||||||
"tx_from_address": "发送地址:",
|
"tx_from_address": "发送地址:",
|
||||||
@@ -1325,6 +1633,8 @@
|
|||||||
"wallets_add_folder_toggle": "+ 扫描其他文件夹中的钱包…",
|
"wallets_add_folder_toggle": "+ 扫描其他文件夹中的钱包…",
|
||||||
"wallets_badge_encrypted": "已加密(密码保护)",
|
"wallets_badge_encrypted": "已加密(密码保护)",
|
||||||
"wallets_badge_encrypted_short": "已加密",
|
"wallets_badge_encrypted_short": "已加密",
|
||||||
|
"wallets_badge_hd": "HD 钱包 — 打开钱包才能确认助记词",
|
||||||
|
"wallets_badge_hd_short": "HD 钱包",
|
||||||
"wallets_badge_legacy": "旧版钱包(无助记词)",
|
"wallets_badge_legacy": "旧版钱包(无助记词)",
|
||||||
"wallets_badge_legacy_short": "旧版",
|
"wallets_badge_legacy_short": "旧版",
|
||||||
"wallets_badge_seed": "助记词钱包 (HD)",
|
"wallets_badge_seed": "助记词钱包 (HD)",
|
||||||
@@ -1343,6 +1653,7 @@
|
|||||||
"wallets_created": "创建于",
|
"wallets_created": "创建于",
|
||||||
"wallets_creating": "正在创建钱包——节点将重启…",
|
"wallets_creating": "正在创建钱包——节点将重启…",
|
||||||
"wallets_current": "当前",
|
"wallets_current": "当前",
|
||||||
|
"wallets_empty_hint": "扫描文件夹以查找更多钱包",
|
||||||
"wallets_exists": "已存在同名钱包。",
|
"wallets_exists": "已存在同名钱包。",
|
||||||
"wallets_external_tt": "在数据目录之外 —「打开」会就地链接(不复制)。",
|
"wallets_external_tt": "在数据目录之外 —「打开」会就地链接(不复制)。",
|
||||||
"wallets_folder_hint": "/含 .dat 钱包文件的文件夹路径",
|
"wallets_folder_hint": "/含 .dat 钱包文件的文件夹路径",
|
||||||
@@ -1356,7 +1667,9 @@
|
|||||||
"wallets_open_failed": "无法在原位置打开此钱包。它可能与数据目录位于不同的驱动器上 — 请将其移动到同一驱动器(在 Windows 上,启用开发者模式也可跨驱动器链接)。",
|
"wallets_open_failed": "无法在原位置打开此钱包。它可能与数据目录位于不同的驱动器上 — 请将其移动到同一驱动器(在 Windows 上,启用开发者模式也可跨驱动器链接)。",
|
||||||
"wallets_open_folder": "打开文件夹位置",
|
"wallets_open_folder": "打开文件夹位置",
|
||||||
"wallets_open_inplace_tt": "在原位置打开此钱包 — 链接到数据目录(不复制)",
|
"wallets_open_inplace_tt": "在原位置打开此钱包 — 链接到数据目录(不复制)",
|
||||||
|
"wallets_remove_folder": "停止扫描此文件夹",
|
||||||
"wallets_reveal": "打开文件夹",
|
"wallets_reveal": "打开文件夹",
|
||||||
|
"wallets_scanned_folders": "已扫描的文件夹:",
|
||||||
"wallets_sort_addresses": "地址",
|
"wallets_sort_addresses": "地址",
|
||||||
"wallets_sort_asc": "升序(最早/最少/最小优先)",
|
"wallets_sort_asc": "升序(最早/最少/最小优先)",
|
||||||
"wallets_sort_by": "排序:",
|
"wallets_sort_by": "排序:",
|
||||||
|
|||||||
190
res/themes/jade.toml
Normal file
190
res/themes/jade.toml
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
[theme]
|
||||||
|
name = "Jade"
|
||||||
|
author = "The Hush Developers"
|
||||||
|
dark = true
|
||||||
|
elevation = { --elevation-0 = "#071210", --elevation-1 = "#0C1A16", --elevation-2 = "#16261F", --elevation-3 = "#1D3128", --elevation-4 = "#243B30" }
|
||||||
|
images = { background_image = "backgrounds/texture/jade_bg.png", logo = "logos/logo_ObsidianDragon_dark.png" }
|
||||||
|
|
||||||
|
[theme.palette]
|
||||||
|
--primary = "#2FA07A"
|
||||||
|
--primary-variant = "#1E7357"
|
||||||
|
--primary-light = "#7FD1B5"
|
||||||
|
--secondary = "#C9A24E"
|
||||||
|
--secondary-variant = "#A8842F"
|
||||||
|
--secondary-light = "#E0C583"
|
||||||
|
--background = "#071210"
|
||||||
|
--surface = "#0C1A16"
|
||||||
|
--surface-variant = "#16261F"
|
||||||
|
--on-primary = "#FFFFFF"
|
||||||
|
--on-secondary = "#000000"
|
||||||
|
--on-background = "#DCEDE4"
|
||||||
|
--on-surface = "#DCEDE4"
|
||||||
|
--on-surface-medium = "rgba(220,237,228,0.85)"
|
||||||
|
--on-surface-disabled = "rgba(220,237,228,0.58)"
|
||||||
|
--error = "#CF6679"
|
||||||
|
--on-error = "#000000"
|
||||||
|
--success = "#81C784"
|
||||||
|
--on-success = "#000000"
|
||||||
|
--warning = "#FFB74D"
|
||||||
|
--on-warning = "#000000"
|
||||||
|
--divider = "rgba(130,205,170,0.14)"
|
||||||
|
--outline = "rgba(130,205,170,0.16)"
|
||||||
|
--scrim = "rgba(0,0,0,0.6)"
|
||||||
|
--surface-hover = "rgba(130,205,170,0.07)"
|
||||||
|
--surface-alt = "rgba(130,205,170,0.05)"
|
||||||
|
--surface-active = "rgba(130,205,170,0.10)"
|
||||||
|
--glass-button = "rgba(130,205,170,0.06)"
|
||||||
|
--glass-button-hover = "rgba(130,205,170,0.12)"
|
||||||
|
--card-border = "rgba(130,205,170,0.26)"
|
||||||
|
--text-shadow = "rgba(0,0,0,0.50)"
|
||||||
|
--input-overlay-text = "rgba(220,237,228,0.30)"
|
||||||
|
--slider-text = "rgba(220,237,228,0.85)"
|
||||||
|
--thumb-fill = "rgba(130,205,170,0.15)"
|
||||||
|
--thumb-border = "rgba(130,205,170,0.50)"
|
||||||
|
--disabled-label = "rgba(130,205,170,0.18)"
|
||||||
|
--chart-grid = "rgba(130,205,170,0.05)"
|
||||||
|
--chart-crosshair = "rgba(130,205,170,0.15)"
|
||||||
|
--chart-hover-ring = "rgba(130,205,170,0.30)"
|
||||||
|
--tooltip-bg = "rgba(9,20,16,0.92)"
|
||||||
|
--tooltip-border = "rgba(130,205,170,0.12)"
|
||||||
|
--glass-fill = "rgba(130,205,170,0.08)"
|
||||||
|
--glass-border = "rgba(47,160,122,0.30)"
|
||||||
|
--glass-noise-tint = "rgba(130,205,170,0.03)"
|
||||||
|
--tactile-top = "rgba(130,205,170,0.06)"
|
||||||
|
--tactile-bottom = "rgba(130,205,170,0.0)"
|
||||||
|
--hover-overlay = "rgba(130,205,170,0.05)"
|
||||||
|
--active-overlay = "rgba(130,205,170,0.10)"
|
||||||
|
--rim-light = "rgba(130,205,170,0.14)"
|
||||||
|
--status-divider = "rgba(130,205,170,0.08)"
|
||||||
|
--sidebar-hover = "rgba(130,205,170,0.10)"
|
||||||
|
--sidebar-icon = "rgba(130,205,170,0.42)"
|
||||||
|
--sidebar-badge = "rgba(220,237,228,1.0)"
|
||||||
|
--sidebar-divider = "rgba(130,205,170,0.06)"
|
||||||
|
--chart-line = "rgba(130,205,170,0.10)"
|
||||||
|
--window-control = "rgba(220,237,228,0.78)"
|
||||||
|
--window-control-hover = "rgba(130,205,170,0.12)"
|
||||||
|
--window-close-hover = "rgba(232,17,35,0.78)"
|
||||||
|
--spinner-track = "rgba(130,205,170,0.10)"
|
||||||
|
--spinner-active = "rgba(79,184,154,0.85)"
|
||||||
|
--shutdown-panel-bg = "rgba(7,18,14,0.90)"
|
||||||
|
--shutdown-panel-border = "rgba(130,205,170,0.07)"
|
||||||
|
--ram-bar-app = "#2FA07A"
|
||||||
|
--ram-bar-system = "rgba(255,255,255,0.18)"
|
||||||
|
--accent-total = "#7FD1B5"
|
||||||
|
--accent-shielded = "#4FB89A"
|
||||||
|
--accent-transparent = "#C9A24E"
|
||||||
|
--accent-action = "#2FA07A"
|
||||||
|
--accent-market = "#4FB89A"
|
||||||
|
--accent-portfolio = "#7FD1B5"
|
||||||
|
--toast-info-accent = "#2FA07A"
|
||||||
|
--toast-info-text = "#7FD1B5"
|
||||||
|
--toast-success-accent = "rgba(50,180,80,1.0)"
|
||||||
|
--toast-success-text = "rgba(180,255,180,1.0)"
|
||||||
|
--toast-warning-accent = "rgba(204,166,50,1.0)"
|
||||||
|
--toast-warning-text = "rgba(255,230,130,1.0)"
|
||||||
|
--toast-error-accent = "rgba(204,64,64,1.0)"
|
||||||
|
--toast-error-text = "rgba(255,153,153,1.0)"
|
||||||
|
--snackbar-bg = "rgba(24,40,34,0.95)"
|
||||||
|
--snackbar-text = "rgba(220,237,228,0.87)"
|
||||||
|
--snackbar-action = "rgba(79,184,154,1.0)"
|
||||||
|
--snackbar-action-hover = "rgba(127,209,181,1.0)"
|
||||||
|
--switch-track-off = "rgba(130,205,170,0.12)"
|
||||||
|
--switch-track-on = "rgba(47,160,122,0.50)"
|
||||||
|
--switch-thumb-off = "#A0C0B4"
|
||||||
|
--switch-thumb-on = "#DCEDE4"
|
||||||
|
--control-shadow = "rgba(0,0,0,0.24)"
|
||||||
|
--checkbox-check = "#000000"
|
||||||
|
--app-bar-shadow = "rgba(0,0,0,0.25)"
|
||||||
|
|
||||||
|
[backdrop]
|
||||||
|
base-color-top = "rgba(14,32,26,210)"
|
||||||
|
base-color-bottom = "rgba(6,18,14,210)"
|
||||||
|
texture-tint-alpha = 120
|
||||||
|
gradient-top-r = 10
|
||||||
|
gradient-top-g = 30
|
||||||
|
gradient-top-b = 22
|
||||||
|
gradient-top-a = 90
|
||||||
|
gradient-bottom-r = 5
|
||||||
|
gradient-bottom-g = 16
|
||||||
|
gradient-bottom-b = 12
|
||||||
|
gradient-bottom-a = 70
|
||||||
|
background-alpha = 0.42
|
||||||
|
surface-alpha = 0.56
|
||||||
|
frame-alpha = 0.78
|
||||||
|
surface-inline-alpha = 0.58
|
||||||
|
background-inline-alpha = 0.40
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Theme Visual Effects — Jade (veins of gold shifting through the stone)
|
||||||
|
# Jade's signature is a slow jade→gold color-shifting border on every glass
|
||||||
|
# panel + the active nav button — a vein of gold surfacing through nephrite.
|
||||||
|
# It's drawn via AddRect so it hugs the real rounded corners (no polygonal
|
||||||
|
# edge-trace). Sparse jade motes drift up the viewport. No other theme turns
|
||||||
|
# gradient-border-panels on, so the panel-wide vein is Jade's own —
|
||||||
|
# deliberately NOT Obsidian's specular glare.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
[effects]
|
||||||
|
hue-cycle-enabled = { size = 0.0 }
|
||||||
|
rainbow-border-enabled = { size = 0.0 }
|
||||||
|
|
||||||
|
# No shimmer sweep — replaced by specular glare
|
||||||
|
shimmer-enabled = { size = 0.0 }
|
||||||
|
|
||||||
|
positional-hue-enabled = { size = 0.0 }
|
||||||
|
|
||||||
|
glow-pulse-enabled = { size = 0.0 }
|
||||||
|
|
||||||
|
# Edge-trace OFF — its hand-walked perimeter chamfers rounded corners.
|
||||||
|
# Jade's vein is the gradient-border below (corner-clean via AddRect).
|
||||||
|
edge-trace-enabled = { size = 0.0 }
|
||||||
|
edge-trace-speed = { size = 0.16 }
|
||||||
|
edge-trace-length = { size = 0.34 }
|
||||||
|
edge-trace-thickness = { size = 1.6 }
|
||||||
|
edge-trace-alpha = { size = 0.55 }
|
||||||
|
edge-trace-color = { color = "#C9A24E" }
|
||||||
|
|
||||||
|
# Specular glare OFF — that's Obsidian's signature; Jade shouldn't echo it.
|
||||||
|
specular-glare-enabled = { size = 0.0 }
|
||||||
|
specular-glare-speed = { size = 0.018 }
|
||||||
|
specular-glare-intensity = { size = 0.008 }
|
||||||
|
specular-glare-radius = { size = 0.65 }
|
||||||
|
specular-glare-count = { size = 1.0 }
|
||||||
|
specular-glare-color = { color = "rgba(150,220,180,1.0)" }
|
||||||
|
|
||||||
|
# HERO — vein of gold: a slow jade→gold color-shifting border on the active
|
||||||
|
# nav button AND (via gradient-border-panels) every glass panel. Drawn with
|
||||||
|
# AddRect so it follows the rounded corners exactly. Panels drift at a softer
|
||||||
|
# alpha and position-phased offset, so a screenful reads like veins at
|
||||||
|
# different depths rather than one synchronized pulse.
|
||||||
|
gradient-border-enabled = { size = 1.0 }
|
||||||
|
gradient-border-panels = { size = 1.0 }
|
||||||
|
gradient-border-speed = { size = 0.10 }
|
||||||
|
gradient-border-thickness = { size = 1.5 }
|
||||||
|
gradient-border-alpha = { size = 0.55 }
|
||||||
|
gradient-border-color-a = { color = "#7FD1B5" }
|
||||||
|
gradient-border-color-b = { color = "#C9A24E" }
|
||||||
|
|
||||||
|
# Ambient jade motes — sparse, slow, cool green particles drifting up the
|
||||||
|
# viewport (recolored ember-rise; a different mood from dragonx's fire embers).
|
||||||
|
ember-rise-enabled = { size = 1.0 }
|
||||||
|
ember-rise-count = { size = 5.0 }
|
||||||
|
ember-rise-speed = { size = 0.18 }
|
||||||
|
ember-rise-particle-size = { size = 1.4 }
|
||||||
|
ember-rise-alpha = { size = 0.26 }
|
||||||
|
ember-rise-color = { color = "#7FD1B5" }
|
||||||
|
|
||||||
|
# Shader-like viewport overlay — deep green stone atmosphere
|
||||||
|
viewport-wash-enabled = { size = 1.0 }
|
||||||
|
viewport-wash-alpha = { size = 0.05 }
|
||||||
|
viewport-wash-tl = { color = "#12402E" }
|
||||||
|
viewport-wash-tr = { color = "#0E3828" }
|
||||||
|
viewport-wash-bl = { color = "#16442E" }
|
||||||
|
viewport-wash-br = { color = "#1A4A34" }
|
||||||
|
viewport-wash-rotate = { size = 0.015 }
|
||||||
|
viewport-wash-pulse = { size = 0.0 }
|
||||||
|
viewport-wash-pulse-depth = { size = 0.0 }
|
||||||
|
|
||||||
|
viewport-vignette-enabled = { size = 1.0 }
|
||||||
|
viewport-vignette-color = { color = "#04140D" }
|
||||||
|
viewport-vignette-radius = { size = 0.22 }
|
||||||
|
viewport-vignette-alpha = { size = 0.15 }
|
||||||
@@ -2232,6 +2232,46 @@ TRANSLATIONS = {
|
|||||||
"ja": "データディレクトリの外 —「開く」はその場でリンクします(コピーなし)。",
|
"ja": "データディレクトリの外 —「開く」はその場でリンクします(コピーなし)。",
|
||||||
"ko": "데이터 디렉터리 밖 — '열기'는 제자리에 링크합니다 (복사 없음)."
|
"ko": "데이터 디렉터리 밖 — '열기'는 제자리에 링크합니다 (복사 없음)."
|
||||||
},
|
},
|
||||||
|
"wallets_scanned_folders": {
|
||||||
|
"es": "Carpetas escaneadas:", "de": "Durchsuchte Ordner:",
|
||||||
|
"fr": "Dossiers analysés :", "pt": "Pastas verificadas:",
|
||||||
|
"ru": "Просканированные папки:", "zh": "已扫描的文件夹:",
|
||||||
|
"ja": "スキャン対象フォルダー:", "ko": "스캔한 폴더:"
|
||||||
|
},
|
||||||
|
"wallets_remove_folder": {
|
||||||
|
"es": "Dejar de escanear esta carpeta", "de": "Diesen Ordner nicht mehr durchsuchen",
|
||||||
|
"fr": "Ne plus analyser ce dossier", "pt": "Parar de verificar esta pasta",
|
||||||
|
"ru": "Больше не сканировать эту папку", "zh": "停止扫描此文件夹",
|
||||||
|
"ja": "このフォルダーのスキャンを停止", "ko": "이 폴더 스캔 중지"
|
||||||
|
},
|
||||||
|
"wallets_empty_hint": {
|
||||||
|
"es": "Escanea una carpeta para encontrar más carteras",
|
||||||
|
"de": "Ordner durchsuchen, um weitere Wallets zu finden",
|
||||||
|
"fr": "Analysez un dossier pour trouver d'autres portefeuilles",
|
||||||
|
"pt": "Verifique uma pasta para encontrar mais carteiras",
|
||||||
|
"ru": "Просканируйте папку, чтобы найти другие кошельки",
|
||||||
|
"zh": "扫描文件夹以查找更多钱包",
|
||||||
|
"ja": "フォルダーをスキャンして他のウォレットを探す",
|
||||||
|
"ko": "폴더를 스캔하여 다른 지갑 찾기"
|
||||||
|
},
|
||||||
|
"market_chart_loading": {
|
||||||
|
"es": "Cargando historial de precios", "de": "Preisverlauf wird geladen",
|
||||||
|
"fr": "Chargement de l'historique des prix", "pt": "Carregando histórico de preços",
|
||||||
|
"ru": "Загрузка истории цен", "zh": "正在加载价格历史",
|
||||||
|
"ja": "価格履歴を読み込み中", "ko": "가격 기록 불러오는 중"
|
||||||
|
},
|
||||||
|
"market_style_line": {
|
||||||
|
"es": "Cambiar a gráfico de líneas", "de": "Zum Liniendiagramm wechseln",
|
||||||
|
"fr": "Passer au graphique en ligne", "pt": "Mudar para gráfico de linhas",
|
||||||
|
"ru": "Переключить на линейный график", "zh": "切换到折线图",
|
||||||
|
"ja": "折れ線チャートに切り替え", "ko": "선형 차트로 전환"
|
||||||
|
},
|
||||||
|
"market_style_candle": {
|
||||||
|
"es": "Cambiar a velas", "de": "Zu Kerzenchart wechseln",
|
||||||
|
"fr": "Passer aux chandeliers", "pt": "Mudar para velas",
|
||||||
|
"ru": "Переключить на свечи", "zh": "切换到蜡烛图",
|
||||||
|
"ja": "ローソク足に切り替え", "ko": "캔들차트로 전환"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
94
scripts/build-freetype-mingw.sh
Executable file
94
scripts/build-freetype-mingw.sh
Executable file
@@ -0,0 +1,94 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Cross-build a MINIMAL static FreeType for the mingw-w64 (Windows) target.
|
||||||
|
#
|
||||||
|
# Why: the wallet's optional color-emoji rendering needs FreeType (to rasterize the COLR/CPAL Twemoji
|
||||||
|
# font). Native Linux/macOS pick up the system FreeType via find_package; the Debian/Ubuntu mingw-w64
|
||||||
|
# cross toolchain ships no FreeType, so we build one here. The Twemoji font is COLRv0 (layered vector),
|
||||||
|
# which FreeType renders WITHOUT libpng / harfbuzz / brotli / zlib — so this is a dependency-free static
|
||||||
|
# build (no external libs to also cross-compile), producing a self-contained libfreetype.a.
|
||||||
|
#
|
||||||
|
# Output: <prefix>/include/freetype2/... + <prefix>/lib/libfreetype.a (default prefix: third_party/freetype-mingw)
|
||||||
|
# build.sh --win-release runs this automatically and passes -DDRAGONX_MINGW_FREETYPE_PREFIX to CMake.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
FT_VERSION="2.13.3"
|
||||||
|
FT_SHA256="5c3a8e78f7b24c20b25b54ee575d6daa40007a5f4eea2845861c3409b3021747" # freetype-2.13.3.tar.gz
|
||||||
|
FT_URL="https://download.savannah.gnu.org/releases/freetype/freetype-${FT_VERSION}.tar.gz"
|
||||||
|
FT_URL_MIRROR="https://downloads.sourceforge.net/project/freetype/freetype2/${FT_VERSION}/freetype-${FT_VERSION}.tar.gz"
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
PREFIX="${1:-$SCRIPT_DIR/third_party/freetype-mingw}"
|
||||||
|
WORK="$SCRIPT_DIR/third_party/.freetype-mingw-build"
|
||||||
|
|
||||||
|
# Already built? (libfreetype.a present) → nothing to do.
|
||||||
|
if [[ -f "$PREFIX/lib/libfreetype.a" && -d "$PREFIX/include/freetype2" ]]; then
|
||||||
|
echo "FreeType (mingw) already built at: $PREFIX"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Pick the mingw compilers (posix threads variant preferred, matching build.sh).
|
||||||
|
if command -v x86_64-w64-mingw32-gcc-posix &>/dev/null; then
|
||||||
|
MINGW_GCC=x86_64-w64-mingw32-gcc-posix; MINGW_GXX=x86_64-w64-mingw32-g++-posix
|
||||||
|
elif command -v x86_64-w64-mingw32-gcc &>/dev/null; then
|
||||||
|
MINGW_GCC=x86_64-w64-mingw32-gcc; MINGW_GXX=x86_64-w64-mingw32-g++
|
||||||
|
else
|
||||||
|
echo "ERROR: x86_64-w64-mingw32-gcc not found (install mingw-w64)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$WORK"
|
||||||
|
cd "$WORK"
|
||||||
|
|
||||||
|
TARBALL="freetype-${FT_VERSION}.tar.gz"
|
||||||
|
if [[ ! -f "$TARBALL" ]]; then
|
||||||
|
echo "Downloading FreeType ${FT_VERSION} ..."
|
||||||
|
curl -fsSL -o "$TARBALL" "$FT_URL" || curl -fsSL -o "$TARBALL" "$FT_URL_MIRROR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Verifying SHA-256 ..."
|
||||||
|
echo "${FT_SHA256} ${TARBALL}" | sha256sum -c - || {
|
||||||
|
echo "ERROR: FreeType tarball checksum mismatch (expected ${FT_SHA256})." >&2
|
||||||
|
echo " got: $(sha256sum "$TARBALL" | cut -d' ' -f1)" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
rm -rf "freetype-${FT_VERSION}"
|
||||||
|
tar xf "$TARBALL"
|
||||||
|
SRC="$WORK/freetype-${FT_VERSION}"
|
||||||
|
|
||||||
|
# Minimal mingw toolchain for FreeType's own CMake.
|
||||||
|
cat > "$WORK/ft-mingw-toolchain.cmake" <<TOOLCHAIN
|
||||||
|
set(CMAKE_SYSTEM_NAME Windows)
|
||||||
|
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||||
|
set(CMAKE_C_COMPILER ${MINGW_GCC})
|
||||||
|
set(CMAKE_CXX_COMPILER ${MINGW_GXX})
|
||||||
|
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
|
TOOLCHAIN
|
||||||
|
|
||||||
|
echo "Configuring FreeType (static, no external deps) ..."
|
||||||
|
rm -rf "$WORK/build"
|
||||||
|
cmake -S "$SRC" -B "$WORK/build" \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE="$WORK/ft-mingw-toolchain.cmake" \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_INSTALL_PREFIX="$PREFIX" \
|
||||||
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
|
-DFT_DISABLE_ZLIB=ON \
|
||||||
|
-DFT_DISABLE_BZIP2=ON \
|
||||||
|
-DFT_DISABLE_PNG=ON \
|
||||||
|
-DFT_DISABLE_HARFBUZZ=ON \
|
||||||
|
-DFT_DISABLE_BROTLI=ON
|
||||||
|
|
||||||
|
echo "Building + installing FreeType ..."
|
||||||
|
cmake --build "$WORK/build" -j "$(nproc)"
|
||||||
|
cmake --install "$WORK/build"
|
||||||
|
|
||||||
|
if [[ -f "$PREFIX/lib/libfreetype.a" ]]; then
|
||||||
|
echo "OK: mingw FreeType -> $PREFIX/lib/libfreetype.a"
|
||||||
|
else
|
||||||
|
echo "ERROR: build did not produce libfreetype.a" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -1,5 +1,17 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# This script uses bash 4+ features (mapfile, safe empty-array expansion under
|
||||||
|
# `set -u`). macOS ships bash 3.2, so re-exec under a newer bash when one is
|
||||||
|
# present (Homebrew), and fail with a clear message otherwise.
|
||||||
|
if [ "${BASH_VERSINFO:-0}" -lt 4 ]; then
|
||||||
|
for _newer_bash in /opt/homebrew/bin/bash /usr/local/bin/bash; do
|
||||||
|
[ -x "$_newer_bash" ] && exec "$_newer_bash" "$0" "$@"
|
||||||
|
done
|
||||||
|
echo "ERROR: build-lite-backend-artifact.sh requires bash 4+ (found ${BASH_VERSION:-unknown})." >&2
|
||||||
|
echo " On macOS: brew install bash" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
@@ -67,25 +79,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 +91,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 +166,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 +184,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"
|
||||||
@@ -374,6 +324,9 @@ prepare_backend_source() {
|
|||||||
ln -s "$BACKEND_SOURCE_DIR/src" "$prepared_root/src"
|
ln -s "$BACKEND_SOURCE_DIR/src" "$prepared_root/src"
|
||||||
[[ -f "$BACKEND_SOURCE_DIR/Cargo.lock" ]] && ln -s "$BACKEND_SOURCE_DIR/Cargo.lock" "$prepared_root/Cargo.lock"
|
[[ -f "$BACKEND_SOURCE_DIR/Cargo.lock" ]] && ln -s "$BACKEND_SOURCE_DIR/Cargo.lock" "$prepared_root/Cargo.lock"
|
||||||
[[ -d "$BACKEND_SOURCE_DIR/.cargo" ]] && ln -s "$BACKEND_SOURCE_DIR/.cargo" "$prepared_root/.cargo"
|
[[ -d "$BACKEND_SOURCE_DIR/.cargo" ]] && ln -s "$BACKEND_SOURCE_DIR/.cargo" "$prepared_root/.cargo"
|
||||||
|
# Honor the pinned Rust toolchain (rust-toolchain.toml) inside the prepared root too,
|
||||||
|
# so builds using --silentdragonxlitelib-dir still select rustc 1.63.
|
||||||
|
[[ -f "$BACKEND_SOURCE_DIR/rust-toolchain.toml" ]] && ln -s "$BACKEND_SOURCE_DIR/rust-toolchain.toml" "$prepared_root/rust-toolchain.toml"
|
||||||
[[ -d "$BACKEND_SOURCE_DIR/libsodium-mingw" ]] && ln -s "$BACKEND_SOURCE_DIR/libsodium-mingw" "$prepared_root/libsodium-mingw"
|
[[ -d "$BACKEND_SOURCE_DIR/libsodium-mingw" ]] && ln -s "$BACKEND_SOURCE_DIR/libsodium-mingw" "$prepared_root/libsodium-mingw"
|
||||||
# Vendored crate deps (offline builds): the .cargo/config.toml's vendored-sources directory is
|
# Vendored crate deps (offline builds): the .cargo/config.toml's vendored-sources directory is
|
||||||
# "vendor" relative to the build root, so expose it inside the prepared root too.
|
# "vendor" relative to the build root, so expose it inside the prepared root too.
|
||||||
@@ -766,6 +719,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'
|
||||||
|
|||||||
87
scripts/build_emoji_subset.py
Normal file
87
scripts/build_emoji_subset.py
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Build a monochrome Noto Emoji subset for the chat/message UI.
|
||||||
|
|
||||||
|
Dear ImGui rasterizes fonts with stb_truetype, which handles only monochrome
|
||||||
|
(outline `glyf`) fonts — NOT color emoji (CBDT/sbix/COLR). So we use Google's
|
||||||
|
*monochrome* Noto Emoji (github.com/google/fonts, ofl/notoemoji, OFL-licensed)
|
||||||
|
and merge it into the text fonts (see Typography::loadFont, the block after the
|
||||||
|
CJK merge). ImGui renders one glyph per codepoint with no shaping, so ZWJ
|
||||||
|
sequences / regional-indicator flags won't compose — single-codepoint emoji
|
||||||
|
(😀 🎉 ❤ 🔥 👍 …) render fine, which covers the overwhelming majority of use.
|
||||||
|
|
||||||
|
Source is the variable font pinned to wght=400 → static, then subset to the
|
||||||
|
emoji planes plus the higher symbol/star ranges the base UI font doesn't cover.
|
||||||
|
The base Ubuntu font already owns U+2600–26FF etc.; ImGui's MergeMode gives the
|
||||||
|
first-loaded glyph precedence, so those stay text-styled and only the codepoints
|
||||||
|
the base lacks fall through to this font.
|
||||||
|
|
||||||
|
Get the source once (OFL, redistributable):
|
||||||
|
curl -fsSL -o /tmp/NotoEmoji-VF.ttf \
|
||||||
|
'https://github.com/google/fonts/raw/main/ofl/notoemoji/NotoEmoji%5Bwght%5D.ttf'
|
||||||
|
|
||||||
|
Then: python3 scripts/build_emoji_subset.py
|
||||||
|
Output: res/fonts/NotoEmoji-Subset.ttf (committed; embedded via INCBIN)
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
from fontTools import ttLib, subset
|
||||||
|
from fontTools.varLib.instancer import instantiateVariableFont
|
||||||
|
|
||||||
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
SOURCE_VF = '/tmp/NotoEmoji-VF.ttf'
|
||||||
|
STATIC = '/tmp/NotoEmoji-Static.ttf'
|
||||||
|
OUTPUT = os.path.join(ROOT, 'res', 'fonts', 'NotoEmoji-Subset.ttf')
|
||||||
|
|
||||||
|
# Emoji codepoints to keep. Ranges are inclusive.
|
||||||
|
RANGES = [
|
||||||
|
(0x1F000, 0x1FAFF), # all the main emoji planes (emoticons, pictographs, transport, supplement, extended)
|
||||||
|
(0x2600, 0x27BF), # Miscellaneous Symbols + Dingbats
|
||||||
|
(0x2B00, 0x2BFF), # stars (⭐ 2B50) and misc arrows
|
||||||
|
(0xFE00, 0xFE0F), # variation selectors (VS16 emoji-style)
|
||||||
|
(0x2194, 0x21AA), # arrows used as emoji
|
||||||
|
(0x231A, 0x231B), # ⌚ ⌛
|
||||||
|
(0x23E9, 0x23FA), # media-control emoji
|
||||||
|
(0x25AA, 0x25FE), # small squares
|
||||||
|
]
|
||||||
|
SINGLES = [0x200D, 0x2934, 0x2935, 0x3030, 0x303D, 0x3297, 0x3299,
|
||||||
|
0x00A9, 0x00AE, 0x2122, 0x2139, 0x24C2]
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if not os.path.exists(SOURCE_VF):
|
||||||
|
raise SystemExit(f"missing source font {SOURCE_VF} — see the header for the curl command")
|
||||||
|
|
||||||
|
# 1. Pin the weight axis so stb_truetype rasterizes a clean static instance.
|
||||||
|
f = ttLib.TTFont(SOURCE_VF)
|
||||||
|
if 'fvar' in f:
|
||||||
|
instantiateVariableFont(f, {'wght': 400}, inplace=True)
|
||||||
|
f.save(STATIC)
|
||||||
|
|
||||||
|
unicodes = list(SINGLES)
|
||||||
|
for lo, hi in RANGES:
|
||||||
|
unicodes.extend(range(lo, hi + 1))
|
||||||
|
|
||||||
|
opts = subset.Options()
|
||||||
|
opts.layout_features = [] # ImGui does no shaping — drop GSUB/GPOS
|
||||||
|
opts.name_IDs = []
|
||||||
|
opts.notdef_outline = True
|
||||||
|
opts.glyph_names = False
|
||||||
|
opts.drop_tables = ['GSUB', 'GPOS', 'GDEF', 'morx', 'kern']
|
||||||
|
|
||||||
|
font = subset.load_font(STATIC, opts)
|
||||||
|
ss = subset.Subsetter(options=opts)
|
||||||
|
ss.populate(unicodes=unicodes)
|
||||||
|
ss.subset(font)
|
||||||
|
subset.save_font(font, OUTPUT, opts)
|
||||||
|
|
||||||
|
out = ttLib.TTFont(OUTPUT)
|
||||||
|
cmap = out.getBestCmap()
|
||||||
|
color = any(t in out.reader.keys() for t in ('CBDT', 'sbix', 'COLR'))
|
||||||
|
print(f"Output: {OUTPUT}")
|
||||||
|
print(f"Size: {os.path.getsize(OUTPUT)//1024} KB | glyphs: {len(cmap)} | color tables: {color}")
|
||||||
|
if color:
|
||||||
|
raise SystemExit("ERROR: subset has color tables — stb_truetype cannot render it")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -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..."
|
||||||
|
|||||||
@@ -256,8 +256,8 @@ HEADER_START
|
|||||||
echo -e "${YELLOW}Note: Daemon binaries not found in prebuilt-binaries/dragonxd-win/ — wallet only${NC}"
|
echo -e "${YELLOW}Note: Daemon binaries not found in prebuilt-binaries/dragonxd-win/ — wallet only${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── xmrig binary (from prebuilt-binaries/xmrig-hac/) ────────────────
|
# ── xmrig binary (from prebuilt-binaries/drg-xmrig/) ────────────────
|
||||||
XMRIG_DIR="$SCRIPT_DIR/prebuilt-binaries/xmrig-hac"
|
XMRIG_DIR="$SCRIPT_DIR/prebuilt-binaries/drg-xmrig"
|
||||||
if [ -f "$XMRIG_DIR/xmrig.exe" ]; then
|
if [ -f "$XMRIG_DIR/xmrig.exe" ]; then
|
||||||
cp -f "$XMRIG_DIR/xmrig.exe" "$EMBED_RES_DIR/xmrig.exe"
|
cp -f "$XMRIG_DIR/xmrig.exe" "$EMBED_RES_DIR/xmrig.exe"
|
||||||
echo " Staged xmrig.exe ($(du -h "$XMRIG_DIR/xmrig.exe" | cut -f1))"
|
echo " Staged xmrig.exe ($(du -h "$XMRIG_DIR/xmrig.exe" | cut -f1))"
|
||||||
|
|||||||
@@ -1,25 +1,31 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Sign dragonx full-node release archives for the wallet's in-app daemon updater (ed25519).
|
# Package the prebuilt dragonx full-node binaries into per-platform release archives and sign them
|
||||||
|
# for the wallet's in-app daemon updater (ed25519 over the EXACT archive bytes).
|
||||||
#
|
#
|
||||||
# The wallet verifies a detached ed25519 signature over the EXACT archive bytes against a public
|
# The wallet verifies a detached ed25519 signature over the archive bytes against a public key
|
||||||
# key pinned in src/util/daemon_updater.h (kDaemonSignaturePublicKeyBase64). Verification is
|
# pinned in src/util/daemon_updater.h (kDaemonSignaturePublicKeyBase64). Verification is MANDATORY
|
||||||
# MANDATORY (kDaemonRequireSignature = true): an in-app update is refused unless a valid signature
|
# (kDaemonRequireSignature = true): an in-app update is refused unless a valid "<archive>.sig" is
|
||||||
# is published. For each archive <name>.zip this produces <name>.zip.sig holding the base64 of the
|
# published next to the archive. The wallet also checks each archive's SHA-256 against a markdown
|
||||||
# raw 64-byte ed25519 signature — upload that .sig next to the .zip as a release asset.
|
# checksum table in the release body, so `release` prints that table for you to paste in.
|
||||||
#
|
#
|
||||||
# Uses OpenSSL (>= 1.1.1) only — no Python/PyNaCl needed. OpenSSL's ed25519 is PureEdDSA (RFC 8032),
|
# Uses OpenSSL (>= 1.1.1) only — no Python/PyNaCl. OpenSSL's ed25519 is PureEdDSA (RFC 8032), the
|
||||||
# the same primitive libsodium's crypto_sign_verify_detached checks, so signatures are compatible
|
# same primitive libsodium's crypto_sign_verify_detached checks, so the signatures are compatible.
|
||||||
# (the same flow the wallet's unit tests verify for the miner updater).
|
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# scripts/sign-daemon-release.sh keygen [out-prefix] # -> <prefix>.ed25519.{key,pub.b64}
|
# scripts/sign-daemon-release.sh keygen [out-prefix] # -> <prefix>.ed25519.{key,pub.b64}
|
||||||
# scripts/sign-daemon-release.sh pubkey <secret.key> # print the base64 public key to pin
|
# scripts/sign-daemon-release.sh pubkey <secret.key> # print the base64 public key to pin
|
||||||
# scripts/sign-daemon-release.sh sign <secret.key> <file>...# -> <file>.sig per file
|
# scripts/sign-daemon-release.sh sign <secret.key> <file>... # sign existing files -> <file>.sig
|
||||||
|
# scripts/sign-daemon-release.sh release <secret.key> <version> [--src DIR] [--out DIR]
|
||||||
|
# # zip prebuilt-binaries/dragonxd-{linux,mac,win}/ into dragonx-<version>-{linux-amd64,macos,
|
||||||
|
# # win64}.zip, sign each, and print the SHA-256 checksum table. Platforms with no dragonxd
|
||||||
|
# # binary staged are skipped.
|
||||||
#
|
#
|
||||||
# Keep the secret key (.ed25519.key) OFFLINE. Paste the base64 public key into
|
# Keep the secret key (.ed25519.key) OFFLINE (mode 600). Paste the base64 public key into
|
||||||
# kDaemonSignaturePublicKeyBase64 in src/util/daemon_updater.h.
|
# kDaemonSignaturePublicKeyBase64 in src/util/daemon_updater.h.
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
die() { echo "error: $*" >&2; exit 1; }
|
die() { echo "error: $*" >&2; exit 1; }
|
||||||
command -v openssl >/dev/null || die "openssl not found (need >= 1.1.1 with ed25519)"
|
command -v openssl >/dev/null || die "openssl not found (need >= 1.1.1 with ed25519)"
|
||||||
|
|
||||||
@@ -27,6 +33,26 @@ command -v openssl >/dev/null || die "openssl not found (need >= 1.1.1 with ed25
|
|||||||
# ed25519 is a fixed 12-byte prefix + the 32-byte key, so the trailing 32 bytes are the raw key.
|
# ed25519 is a fixed 12-byte prefix + the 32-byte key, so the trailing 32 bytes are the raw key.
|
||||||
pubkey_b64() { openssl pkey -in "$1" -pubout -outform DER | tail -c 32 | openssl base64 -A; }
|
pubkey_b64() { openssl pkey -in "$1" -pubout -outform DER | tail -c 32 | openssl base64 -A; }
|
||||||
|
|
||||||
|
sha256_of() {
|
||||||
|
if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | awk '{print $1}';
|
||||||
|
else shasum -a 256 "$1" | awk '{print $1}'; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Detached ed25519 signature over the raw file bytes -> <file>.sig (base64 of the 64-byte sig).
|
||||||
|
sign_file() {
|
||||||
|
local key="$1" f="$2" raw
|
||||||
|
raw="$(mktemp)"
|
||||||
|
openssl pkeyutl -sign -inkey "$key" -rawin -in "$f" -out "$raw"
|
||||||
|
openssl base64 -A -in "$raw" > "$f.sig"
|
||||||
|
printf '\n' >> "$f.sig"
|
||||||
|
rm -f "$raw"
|
||||||
|
}
|
||||||
|
|
||||||
|
# platform -> (staging dir under prebuilt-binaries, release token, expected daemon binary name)
|
||||||
|
plat_dir() { case "$1" in linux) echo dragonxd-linux;; mac) echo dragonxd-mac;; win) echo dragonxd-win;; esac; }
|
||||||
|
plat_token() { case "$1" in linux) echo linux-amd64;; mac) echo macos;; win) echo win64;; esac; }
|
||||||
|
plat_daemon() { case "$1" in win) echo dragonxd.exe;; *) echo dragonxd;; esac; }
|
||||||
|
|
||||||
cmd="${1:-}"; shift || true
|
cmd="${1:-}"; shift || true
|
||||||
case "$cmd" in
|
case "$cmd" in
|
||||||
keygen)
|
keygen)
|
||||||
@@ -42,26 +68,90 @@ case "$cmd" in
|
|||||||
echo "Pin this in src/util/daemon_updater.h (kDaemonSignaturePublicKeyBase64):"
|
echo "Pin this in src/util/daemon_updater.h (kDaemonSignaturePublicKeyBase64):"
|
||||||
echo " $pub"
|
echo " $pub"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
pubkey)
|
pubkey)
|
||||||
[ $# -ge 1 ] || die "usage: pubkey <secret.key>"
|
[ $# -ge 1 ] || die "usage: pubkey <secret.key>"
|
||||||
pubkey_b64 "$1"
|
pubkey_b64 "$1"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
sign)
|
sign)
|
||||||
[ $# -ge 2 ] || die "usage: sign <secret.key> <file>..."
|
[ $# -ge 2 ] || die "usage: sign <secret.key> <file>..."
|
||||||
key="$1"; shift
|
key="$1"; shift
|
||||||
[ -f "$key" ] || die "no such key: $key"
|
[ -f "$key" ] || die "no such key: $key"
|
||||||
for f in "$@"; do
|
for f in "$@"; do
|
||||||
[ -f "$f" ] || die "no such file: $f"
|
[ -f "$f" ] || die "no such file: $f"
|
||||||
raw="$(mktemp)"
|
sign_file "$key" "$f"
|
||||||
openssl pkeyutl -sign -inkey "$key" -rawin -in "$f" -out "$raw"
|
|
||||||
openssl base64 -A -in "$raw" > "$f.sig"
|
|
||||||
printf '\n' >> "$f.sig"
|
|
||||||
rm -f "$raw"
|
|
||||||
echo "signed: $f -> $f.sig"
|
echo "signed: $f -> $f.sig"
|
||||||
done
|
done
|
||||||
echo "Upload each .sig as a release asset next to its archive."
|
echo "Upload each .sig as a release asset next to its archive."
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
release)
|
||||||
|
[ $# -ge 2 ] || die "usage: release <secret.key> <version> [--src DIR] [--out DIR]"
|
||||||
|
key="$1"; version="$2"; shift 2
|
||||||
|
src="$PROJECT_ROOT/prebuilt-binaries"
|
||||||
|
out="$PROJECT_ROOT/release/daemon"
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--src) [ $# -ge 2 ] || die "--src needs a value"; src="$2"; shift 2 ;;
|
||||||
|
--out) [ $# -ge 2 ] || die "--out needs a value"; out="$2"; shift 2 ;;
|
||||||
|
*) die "unknown option: $1" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
[ -f "$key" ] || die "no such key: $key"
|
||||||
|
[ -d "$src" ] || die "no such source dir: $src"
|
||||||
|
command -v zip >/dev/null 2>&1 || die "zip not found (install 'zip')"
|
||||||
|
mkdir -p "$out"
|
||||||
|
|
||||||
|
# Sanity: warn if this key does not match the public key pinned in the wallet (the wallet would
|
||||||
|
# then reject every signature made with it — only expected when deliberately rotating the key).
|
||||||
|
pinned="$(grep -oE '"[A-Za-z0-9+/]{43}="' "$PROJECT_ROOT/src/util/daemon_updater.h" 2>/dev/null | head -1 | tr -d '"')"
|
||||||
|
mine="$(pubkey_b64 "$key")"
|
||||||
|
if [ -n "$pinned" ] && [ "$pinned" != "$mine" ]; then
|
||||||
|
echo "WARNING: this key's public key does not match the one pinned in daemon_updater.h:" >&2
|
||||||
|
echo " signing key -> $mine" >&2
|
||||||
|
echo " pinned key -> $pinned" >&2
|
||||||
|
echo " The wallet will REJECT these signatures unless you are rotating the pinned key." >&2
|
||||||
|
echo >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
made=0
|
||||||
|
table=""
|
||||||
|
for plat in linux mac win; do
|
||||||
|
d="$src/$(plat_dir "$plat")"
|
||||||
|
daemon="$d/$(plat_daemon "$plat")"
|
||||||
|
if [ ! -f "$daemon" ]; then
|
||||||
|
echo "skip $plat: no $(plat_daemon "$plat") staged in $d" >&2
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
archive="dragonx-$version-$(plat_token "$plat").zip"
|
||||||
|
apath="$out/$archive"
|
||||||
|
rm -f "$apath"
|
||||||
|
# Zip the staged files at the archive root (binaries + sapling params + asmap), excluding
|
||||||
|
# the .gitkeep placeholder. The updater flattens paths via baseName(), so a flat zip is fine.
|
||||||
|
files=()
|
||||||
|
while IFS= read -r fn; do files+=("$fn"); done < <(cd "$d" && ls -A | grep -vx '.gitkeep')
|
||||||
|
[ "${#files[@]}" -gt 0 ] || { echo "skip $plat: nothing to package in $d" >&2; continue; }
|
||||||
|
( cd "$d" && zip -q -X "$apath" "${files[@]}" )
|
||||||
|
sign_file "$key" "$apath"
|
||||||
|
sum="$(sha256_of "$apath")"
|
||||||
|
table+="| $archive | \`$sum\` |"$'\n'
|
||||||
|
echo "packaged + signed: $apath (+ .sig) sha256=$sum"
|
||||||
|
made=$((made + 1))
|
||||||
|
done
|
||||||
|
[ "$made" -gt 0 ] || die "no platform had a staged daemon binary under $src/dragonxd-{linux,mac,win}/"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Checksum table (paste into the release body so the wallet can verify SHA-256):"
|
||||||
|
echo "| Archive | SHA-256 |"
|
||||||
|
echo "|---|---|"
|
||||||
|
printf '%s' "$table"
|
||||||
|
echo
|
||||||
|
echo "Upload each .zip AND its .zip.sig as release assets. Wallet enforces the ed25519 signature"
|
||||||
|
echo "(kDaemonRequireSignature=true) and the SHA-256 from the table above."
|
||||||
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
die "usage: $0 {keygen [prefix] | pubkey <secret.key> | sign <secret.key> <file>...}"
|
die "usage: $0 {keygen [prefix] | pubkey <secret.key> | sign <secret.key> <file>... | release <secret.key> <version> [--src DIR] [--out DIR]}"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
68
setup.sh
68
setup.sh
@@ -133,7 +133,7 @@ pkgs_core_arch="base-devel cmake git pkg-config
|
|||||||
libxkbcommon wayland libsodium curl
|
libxkbcommon wayland libsodium curl
|
||||||
autoconf automake libtool wget python xxd"
|
autoconf automake libtool wget python xxd"
|
||||||
|
|
||||||
pkgs_core_macos="cmake python xxd"
|
pkgs_core_macos="bash cmake python xxd"
|
||||||
|
|
||||||
# Windows cross-compile (from Linux)
|
# Windows cross-compile (from Linux)
|
||||||
pkgs_win_debian="mingw-w64 zip"
|
pkgs_win_debian="mingw-w64 zip"
|
||||||
@@ -284,6 +284,14 @@ fi
|
|||||||
header "Windows Cross-Compile"
|
header "Windows Cross-Compile"
|
||||||
|
|
||||||
if $SETUP_WIN; then
|
if $SETUP_WIN; then
|
||||||
|
# Only touch apt / update-alternatives (which need sudo) when the toolchain is missing. If it is
|
||||||
|
# already installed, skip them so `./setup.sh --win` can run WITHOUT sudo — important because the
|
||||||
|
# daemon cross-compile that follows should run as the invoking user. Running the whole setup under
|
||||||
|
# sudo leaves root-owned build artifacts under external/dragonx, which then break `make clean` on
|
||||||
|
# a later non-sudo build (stale objects get relinked -> the mingw link failure recurs).
|
||||||
|
if has_cmd x86_64-w64-mingw32-g++-posix || has_cmd x86_64-w64-mingw32-g++; then
|
||||||
|
ok "Windows cross-compile toolchain already present — skipping apt install"
|
||||||
|
else
|
||||||
win_pkgs="$(get_pkgs win)"
|
win_pkgs="$(get_pkgs win)"
|
||||||
if [[ -n "$win_pkgs" ]]; then
|
if [[ -n "$win_pkgs" ]]; then
|
||||||
install_pkgs "$win_pkgs" "Windows cross-compile"
|
install_pkgs "$win_pkgs" "Windows cross-compile"
|
||||||
@@ -298,6 +306,7 @@ if $SETUP_WIN; then
|
|||||||
/usr/bin/x86_64-w64-mingw32-g++-posix 2>/dev/null || true
|
/usr/bin/x86_64-w64-mingw32-g++-posix 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Fetch libsodium for Windows
|
# Fetch libsodium for Windows
|
||||||
if [[ ! -f "$PROJECT_DIR/libs/libsodium-win/lib/libsodium.a" ]]; then
|
if [[ ! -f "$PROJECT_DIR/libs/libsodium-win/lib/libsodium.a" ]]; then
|
||||||
@@ -391,11 +400,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)"
|
||||||
@@ -684,11 +708,13 @@ if [[ "$STALE_DAEMON" -eq 1 ]]; then
|
|||||||
warn " Linux: ./setup.sh · Windows: ./setup.sh --win · macOS: ./setup.sh --mac"
|
warn " Linux: ./setup.sh · Windows: ./setup.sh --win · macOS: ./setup.sh --mac"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── 7. xmrig-hac (mining binary) ────────────────────────────────────────────
|
# ── 7. drg-xmrig (mining binary) ────────────────────────────────────────────
|
||||||
header "xmrig-hac Mining Binary"
|
header "drg-xmrig Mining Binary"
|
||||||
|
|
||||||
XMRIG_SRC="$PROJECT_DIR/external/xmrig-hac"
|
XMRIG_SRC="$PROJECT_DIR/external/drg-xmrig"
|
||||||
XMRIG_PREBUILT="$PROJECT_DIR/prebuilt-binaries/xmrig-hac"
|
# Output dir bundled by build.sh (Linux zip, AppImage, Windows embed, mac .app)
|
||||||
|
# and scripts/legacy/build-windows.sh — keep this path in sync with those.
|
||||||
|
XMRIG_PREBUILT="$PROJECT_DIR/prebuilt-binaries/drg-xmrig"
|
||||||
|
|
||||||
# Clean previous prebuilt xmrig binaries so we always rebuild
|
# Clean previous prebuilt xmrig binaries so we always rebuild
|
||||||
# Only clean the binary for the platform(s) we are actually building,
|
# Only clean the binary for the platform(s) we are actually building,
|
||||||
@@ -700,14 +726,14 @@ if ! $CHECK_ONLY; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Helper: clone xmrig-hac if not present
|
# Helper: clone drg-xmrig if not present
|
||||||
clone_xmrig_if_needed() {
|
clone_xmrig_if_needed() {
|
||||||
if [[ ! -d "$XMRIG_SRC" ]]; then
|
if [[ ! -d "$XMRIG_SRC" ]]; then
|
||||||
info "Cloning xmrig-hac..."
|
info "Cloning drg-xmrig..."
|
||||||
git clone https://git.dragonx.is/dragonx/xmrig-hac.git "$XMRIG_SRC"
|
git clone https://git.dragonx.is/DragonX/drg-xmrig.git "$XMRIG_SRC"
|
||||||
else
|
else
|
||||||
ok "xmrig-hac source already present"
|
ok "drg-xmrig source already present"
|
||||||
info "Pulling latest xmrig-hac..."
|
info "Pulling latest drg-xmrig..."
|
||||||
(cd "$XMRIG_SRC" && git pull --ff-only 2>/dev/null || true)
|
(cd "$XMRIG_SRC" && git pull --ff-only 2>/dev/null || true)
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -728,15 +754,15 @@ else
|
|||||||
rm -rf "$XMRIG_SRC/build"
|
rm -rf "$XMRIG_SRC/build"
|
||||||
|
|
||||||
# Build dependencies (libuv, hwloc, openssl)
|
# Build dependencies (libuv, hwloc, openssl)
|
||||||
info "Building xmrig-hac dependencies (libuv, hwloc, openssl)..."
|
info "Building drg-xmrig dependencies (libuv, hwloc, openssl)..."
|
||||||
(
|
(
|
||||||
cd "$XMRIG_SRC/scripts"
|
cd "$XMRIG_SRC/scripts"
|
||||||
sh build_deps.sh
|
sh build_deps.sh
|
||||||
)
|
)
|
||||||
ok "xmrig-hac dependencies built"
|
ok "drg-xmrig dependencies built"
|
||||||
|
|
||||||
# Build xmrig
|
# Build xmrig
|
||||||
info "Building xmrig-hac (Linux)..."
|
info "Building drg-xmrig (Linux)..."
|
||||||
mkdir -p "$XMRIG_SRC/build"
|
mkdir -p "$XMRIG_SRC/build"
|
||||||
(
|
(
|
||||||
cd "$XMRIG_SRC/build"
|
cd "$XMRIG_SRC/build"
|
||||||
@@ -753,7 +779,7 @@ else
|
|||||||
mkdir -p "$XMRIG_PREBUILT"
|
mkdir -p "$XMRIG_PREBUILT"
|
||||||
if [[ -f "$XMRIG_SRC/build/xmrig" ]]; then
|
if [[ -f "$XMRIG_SRC/build/xmrig" ]]; then
|
||||||
cp "$XMRIG_SRC/build/xmrig" "$XMRIG_LINUX"
|
cp "$XMRIG_SRC/build/xmrig" "$XMRIG_LINUX"
|
||||||
ok "xmrig (Linux) built and installed to prebuilt-binaries/xmrig-hac/"
|
ok "xmrig (Linux) built and installed to prebuilt-binaries/drg-xmrig/"
|
||||||
else
|
else
|
||||||
err "xmrig (Linux) build failed — binary not found"
|
err "xmrig (Linux) build failed — binary not found"
|
||||||
MISSING=$((MISSING + 1))
|
MISSING=$((MISSING + 1))
|
||||||
@@ -777,7 +803,7 @@ else
|
|||||||
# Clean previous Windows build
|
# Clean previous Windows build
|
||||||
rm -rf "$XMRIG_SRC/build-windows"
|
rm -rf "$XMRIG_SRC/build-windows"
|
||||||
|
|
||||||
info "Building xmrig-hac (Windows cross-compile)..."
|
info "Building drg-xmrig (Windows cross-compile)..."
|
||||||
(
|
(
|
||||||
cd "$XMRIG_SRC/scripts"
|
cd "$XMRIG_SRC/scripts"
|
||||||
bash build_windows.sh
|
bash build_windows.sh
|
||||||
@@ -787,7 +813,7 @@ else
|
|||||||
mkdir -p "$XMRIG_PREBUILT"
|
mkdir -p "$XMRIG_PREBUILT"
|
||||||
if [[ -f "$XMRIG_SRC/build-windows/xmrig.exe" ]]; then
|
if [[ -f "$XMRIG_SRC/build-windows/xmrig.exe" ]]; then
|
||||||
cp "$XMRIG_SRC/build-windows/xmrig.exe" "$XMRIG_WIN"
|
cp "$XMRIG_SRC/build-windows/xmrig.exe" "$XMRIG_WIN"
|
||||||
ok "xmrig.exe (Windows) built and installed to prebuilt-binaries/xmrig-hac/"
|
ok "xmrig.exe (Windows) built and installed to prebuilt-binaries/drg-xmrig/"
|
||||||
else
|
else
|
||||||
err "xmrig.exe (Windows) build failed — binary not found"
|
err "xmrig.exe (Windows) build failed — binary not found"
|
||||||
MISSING=$((MISSING + 1))
|
MISSING=$((MISSING + 1))
|
||||||
@@ -797,7 +823,7 @@ fi
|
|||||||
# ── 8. Binary directories ───────────────────────────────────────────────────
|
# ── 8. Binary directories ───────────────────────────────────────────────────
|
||||||
header "Binary Directories"
|
header "Binary Directories"
|
||||||
|
|
||||||
for platform in dragonxd-linux dragonxd-win dragonxd-mac xmrig; do
|
for platform in dragonxd-linux dragonxd-win dragonxd-mac drg-xmrig; do
|
||||||
dir="$PROJECT_DIR/prebuilt-binaries/$platform"
|
dir="$PROJECT_DIR/prebuilt-binaries/$platform"
|
||||||
if [[ -d "$dir" ]]; then
|
if [[ -d "$dir" ]]; then
|
||||||
# Count actual files (not .gitkeep)
|
# Count actual files (not .gitkeep)
|
||||||
|
|||||||
1304
src/app.cpp
1304
src/app.cpp
File diff suppressed because it is too large
Load Diff
246
src/app.h
246
src/app.h
@@ -13,6 +13,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#include <deque>
|
||||||
#include <nlohmann/json_fwd.hpp>
|
#include <nlohmann/json_fwd.hpp>
|
||||||
#include "data/transaction_history_cache.h"
|
#include "data/transaction_history_cache.h"
|
||||||
#include "data/address_book.h"
|
#include "data/address_book.h"
|
||||||
@@ -139,6 +140,9 @@ public:
|
|||||||
* @brief Whether we are in the shutdown phase
|
* @brief Whether we are in the shutdown phase
|
||||||
*/
|
*/
|
||||||
bool isShuttingDown() const { return shutting_down_; }
|
bool isShuttingDown() const { return shutting_down_; }
|
||||||
|
// True while the wallet-switch progress modal is open — keeps the frame loop redrawing so its live
|
||||||
|
// phase/spinner update in real time even when the app is otherwise idle.
|
||||||
|
bool isWalletSwitchInProgress() const { return wallet_switch_dialog_open_.load(); }
|
||||||
wallet::WalletCapabilities walletCapabilities() const { return wallet::currentWalletCapabilities(); }
|
wallet::WalletCapabilities walletCapabilities() const { return wallet::currentWalletCapabilities(); }
|
||||||
bool isLiteBuild() const { return wallet::isLiteBuild(walletCapabilities()); }
|
bool isLiteBuild() const { return wallet::isLiteBuild(walletCapabilities()); }
|
||||||
bool supportsEmbeddedDaemon() const { return wallet::supportsEmbeddedDaemon(walletCapabilities()); }
|
bool supportsEmbeddedDaemon() const { return wallet::supportsEmbeddedDaemon(walletCapabilities()); }
|
||||||
@@ -168,6 +172,9 @@ public:
|
|||||||
daemon::EmbeddedDaemon* consoleDaemon();
|
daemon::EmbeddedDaemon* consoleDaemon();
|
||||||
daemon::XmrigManager* consoleXmrig();
|
daemon::XmrigManager* consoleXmrig();
|
||||||
config::Settings* settings() { return settings_.get(); }
|
config::Settings* settings() { return settings_.get(); }
|
||||||
|
// Request a font-atlas rebuild before the next frame (e.g. after toggling color emoji). Handled in
|
||||||
|
// preFrame() via Typography::reload — safe to call from UI code mid-frame.
|
||||||
|
void requestFontRebuild() { font_rebuild_requested_ = true; }
|
||||||
// Lite wallet controller (non-null only in lite builds with a linked backend).
|
// Lite wallet controller (non-null only in lite builds with a linked backend).
|
||||||
wallet::LiteWalletController* liteWallet() { return lite_wallet_.get(); }
|
wallet::LiteWalletController* liteWallet() { return lite_wallet_.get(); }
|
||||||
// HushChat service (identity + in-memory message store); the Chat tab reads its store.
|
// HushChat service (identity + in-memory message store); the Chat tab reads its store.
|
||||||
@@ -176,6 +183,9 @@ public:
|
|||||||
// message (to a conversation whose peer key we know) / a new-conversation contact request.
|
// message (to a conversation whose peer key we know) / a new-conversation contact request.
|
||||||
void sendChatMessage(const std::string& conversationId, const std::string& text);
|
void sendChatMessage(const std::string& conversationId, const std::string& text);
|
||||||
void startChatConversation(const std::string& peerZaddr, const std::string& text);
|
void startChatConversation(const std::string& peerZaddr, const std::string& text);
|
||||||
|
// Send a contact request into an existing conversation (used to retry a failed request in place).
|
||||||
|
void sendContactRequestForCid(const std::string& cid, const std::string& peerZaddr,
|
||||||
|
const std::string& text);
|
||||||
// Debug/sweep convenience: give the Chat tab a demo identity + a few sample conversations
|
// Debug/sweep convenience: give the Chat tab a demo identity + a few sample conversations
|
||||||
// (in-memory only, not persisted) so the screenshot sweep captures the populated UI. No-op when
|
// (in-memory only, not persisted) so the screenshot sweep captures the populated UI. No-op when
|
||||||
// the chat feature is off.
|
// the chat feature is off.
|
||||||
@@ -198,10 +208,16 @@ public:
|
|||||||
data::AddressBook& addressBook() { return address_book_; }
|
data::AddressBook& addressBook() { return address_book_; }
|
||||||
const data::AddressBook& addressBook() const { return address_book_; }
|
const data::AddressBook& addressBook() const { return address_book_; }
|
||||||
|
|
||||||
// Hash of the active wallet's identity (derived from its address list), used to scope
|
// Hash of the active wallet's identity (derived from its address list). This is the tx-history
|
||||||
// per-wallet data (e.g. address-book contacts). Empty until addresses are known (pre-connect).
|
// cache key — it changes when the address set changes, so DON'T scope persistent user data on it.
|
||||||
std::string activeWalletIdentityHash() const;
|
std::string activeWalletIdentityHash() const;
|
||||||
|
|
||||||
|
// Stable per-wallet id ("w:"+hex) for scoping persistent per-wallet data (address-book contacts).
|
||||||
|
// Generated once and persisted in the wallet index (keyed by wallet file), never recomputed from
|
||||||
|
// the mutable address set — so creating addresses / locking / disconnecting never changes it.
|
||||||
|
// Empty only when there is no active wallet file. Establishes + persists the id on first call.
|
||||||
|
std::string activeWalletScopeId();
|
||||||
|
|
||||||
data::WalletIndex& walletIndex() { return wallet_index_; }
|
data::WalletIndex& walletIndex() { return wallet_index_; }
|
||||||
const data::WalletIndex& walletIndex() const { return wallet_index_; }
|
const data::WalletIndex& walletIndex() const { return wallet_index_; }
|
||||||
|
|
||||||
@@ -300,7 +316,17 @@ public:
|
|||||||
void exportPrivateKey(const std::string& address, std::function<void(const std::string&)> callback);
|
void exportPrivateKey(const std::string& address, std::function<void(const std::string&)> callback);
|
||||||
// callback receives (keys, exportedCount, totalAddresses) so callers can detect a keyless/partial export.
|
// callback receives (keys, exportedCount, totalAddresses) so callers can detect a keyless/partial export.
|
||||||
void exportAllKeys(std::function<void(const std::string&, int, int)> callback);
|
void exportAllKeys(std::function<void(const std::string&, int, int)> callback);
|
||||||
void importPrivateKey(const std::string& key, std::function<void(bool, const std::string&)> callback);
|
// callback(success, errorOrEmpty, importedAddress). address is "" on failure or when the RPC
|
||||||
|
// returns none; the import routes to z_importviewingkey / z_importkey / importprivkey by key type.
|
||||||
|
// startHeight > 0 rescans from that block (shielded RPCs only; ignored for transparent WIF).
|
||||||
|
void importPrivateKey(const std::string& key, int startHeight,
|
||||||
|
std::function<void(bool, const std::string&, const std::string&)> callback);
|
||||||
|
|
||||||
|
// Sweep a spending key: import it (rescan) then z_sendmany ALL its funds (balance − fee) to a
|
||||||
|
// destination you own — a freshly generated shielded address when destMode == 0, else destExisting.
|
||||||
|
// Drives the sweep_step_ / sweep_status_ / sweep_txid_ state; reuses the async-operation tracker.
|
||||||
|
void sweepPrivateKey(const std::string& key, int startHeight, int destMode,
|
||||||
|
const std::string& destExisting);
|
||||||
|
|
||||||
// Wallet backup
|
// Wallet backup
|
||||||
void backupWallet(const std::string& destination, std::function<void(bool, const std::string&)> callback);
|
void backupWallet(const std::string& destination, std::function<void(bool, const std::string&)> callback);
|
||||||
@@ -335,6 +361,13 @@ public:
|
|||||||
// Fetch historical USD price series (CoinGecko market_chart) that back the portfolio
|
// Fetch historical USD price series (CoinGecko market_chart) that back the portfolio
|
||||||
// sparkline intervals; self-throttled to ~30 min. Safe to call every frame.
|
// sparkline intervals; self-throttled to ~30 min. Safe to call every frame.
|
||||||
void refreshMarketChart();
|
void refreshMarketChart();
|
||||||
|
// Fetch the SELECTED pair's candles from that exchange's own API (data/exchange_candles.h) so the
|
||||||
|
// Market chart shows the real per-exchange price. Re-fetches on pair change; falls back to the
|
||||||
|
// CoinGecko aggregate for unmapped venues / failed fetches. Safe to call every frame.
|
||||||
|
void refreshExchangeChart();
|
||||||
|
// True while a market price-history fetch is in flight (CoinGecko aggregate OR per-exchange). The
|
||||||
|
// Market chart shows a loading indicator instead of the empty state during pair switches.
|
||||||
|
bool isMarketChartLoading() const { return chart_fetch_in_flight_ || exchange_chart_fetch_in_flight_; }
|
||||||
|
|
||||||
/// @brief Per-category refresh intervals, adjusted by active tab
|
/// @brief Per-category refresh intervals, adjusted by active tab
|
||||||
using RefreshIntervals = services::NetworkRefreshService::Intervals;
|
using RefreshIntervals = services::NetworkRefreshService::Intervals;
|
||||||
@@ -363,7 +396,8 @@ public:
|
|||||||
std::string screenshotDir() const; // <config>/screenshots (fixed; sweeps overwrite in place)
|
std::string screenshotDir() const; // <config>/screenshots (fixed; sweeps overwrite in place)
|
||||||
|
|
||||||
// Dialog triggers (used by settings page to open modal dialogs)
|
// Dialog triggers (used by settings page to open modal dialogs)
|
||||||
void showImportKeyDialog() { show_import_key_ = true; }
|
void showImportKeyDialog() { import_view_mode_ = false; show_import_key_ = true; } // spending
|
||||||
|
void showImportViewingKeyDialog() { import_view_mode_ = true; show_import_key_ = true; } // watch-only
|
||||||
void showExportKeyDialog() { show_export_key_ = true; }
|
void showExportKeyDialog() { show_export_key_ = true; }
|
||||||
void showBackupDialog() { show_backup_ = true; }
|
void showBackupDialog() { show_backup_ = true; }
|
||||||
void showSeedBackupDialog() { show_seed_backup_ = true; }
|
void showSeedBackupDialog() { show_seed_backup_ = true; }
|
||||||
@@ -371,6 +405,14 @@ public:
|
|||||||
// True when the current full-node wallet is a legacy, pre-seed-phrase wallet (no BIP39 mnemonic)
|
// True when the current full-node wallet is a legacy, pre-seed-phrase wallet (no BIP39 mnemonic)
|
||||||
// that a capable daemon could migrate — the Migrate-to-seed button glows to nudge the user.
|
// that a capable daemon could migrate — the Migrate-to-seed button glows to nudge the user.
|
||||||
bool isPreSeedWallet() const { return wallet_seed_status_ == WalletSeedStatus::NoMnemonic; }
|
bool isPreSeedWallet() const { return wallet_seed_status_ == WalletSeedStatus::NoMnemonic; }
|
||||||
|
// Authoritative BIP39-mnemonic status of the ACTIVE wallet, decided at runtime by z_exportmnemonic
|
||||||
|
// (not the offline file probe, which can't tell an HD-but-no-mnemonic wallet from a seed-phrase one).
|
||||||
|
// 0 = unknown/undecidable, 1 = has a seed phrase, 2 = legacy (no mnemonic).
|
||||||
|
int activeWalletSeedBadge() const {
|
||||||
|
if (wallet_seed_status_ == WalletSeedStatus::HasMnemonic) return 1;
|
||||||
|
if (wallet_seed_status_ == WalletSeedStatus::NoMnemonic) return 2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
void showAboutDialog() { show_about_ = true; }
|
void showAboutDialog() { show_about_ = true; }
|
||||||
|
|
||||||
// Legacy tab compat — maps int to NavPage
|
// Legacy tab compat — maps int to NavPage
|
||||||
@@ -389,6 +431,12 @@ public:
|
|||||||
// Embedded daemon control
|
// Embedded daemon control
|
||||||
bool startEmbeddedDaemon();
|
bool startEmbeddedDaemon();
|
||||||
void stopEmbeddedDaemon();
|
void stopEmbeddedDaemon();
|
||||||
|
// Stop the node specifically for a wallet switch, which MUST free the RPC port to relaunch on
|
||||||
|
// -wallet=<name>. Unlike stopEmbeddedDaemon() (whose DisconnectOnly policy leaves an adopted/external
|
||||||
|
// daemon running), this sends a graceful RPC "stop" — using our own connection creds, so only our
|
||||||
|
// daemon obeys it — even to an adopted daemon, then waits (bounded) for the port to actually release.
|
||||||
|
// Returns true once the RPC port is free (or we're shutting down).
|
||||||
|
bool stopDaemonForWalletSwitch();
|
||||||
bool isEmbeddedDaemonRunning() const;
|
bool isEmbeddedDaemonRunning() const;
|
||||||
bool isUsingEmbeddedDaemon() const { return supportsEmbeddedDaemon() && use_embedded_daemon_; }
|
bool isUsingEmbeddedDaemon() const { return supportsEmbeddedDaemon() && use_embedded_daemon_; }
|
||||||
void setUseEmbeddedDaemon(bool use) { use_embedded_daemon_ = use && supportsEmbeddedDaemon(); }
|
void setUseEmbeddedDaemon(bool use) { use_embedded_daemon_ = use && supportsEmbeddedDaemon(); }
|
||||||
@@ -431,6 +479,10 @@ public:
|
|||||||
// Coin logo texture accessor (DragonX currency icon for balance tab)
|
// Coin logo texture accessor (DragonX currency icon for balance tab)
|
||||||
ImTextureID getCoinLogoTexture() const { return coin_logo_tex_; }
|
ImTextureID getCoinLogoTexture() const { return coin_logo_tex_; }
|
||||||
|
|
||||||
|
// DragonX custom chat emoji (the ":drgx:" shortcode) — the mark recolored to the theme accent (like
|
||||||
|
// the logo), re-rasterized on theme change. Used by the emoji picker tile + inline in chat bubbles.
|
||||||
|
ImTextureID getDrgxEmojiTexture() const { return drgx_emoji_tex_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reload theme images (background gradient + logo) from new paths
|
* @brief Reload theme images (background gradient + logo) from new paths
|
||||||
* @param bgPath Path to background image override (empty = use default)
|
* @param bgPath Path to background image override (empty = use default)
|
||||||
@@ -438,6 +490,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
void reloadThemeImages(const std::string& bgPath, const std::string& logoPath);
|
void reloadThemeImages(const std::string& bgPath, const std::string& logoPath);
|
||||||
|
|
||||||
|
// Load / recolor-per-theme the DragonX header logo (SVG rasterized to the theme accent). Called at
|
||||||
|
// the top of render() so the wizard, lock screen, and main header all show it.
|
||||||
|
void ensureLogoTexture();
|
||||||
|
|
||||||
// Wizard / first-run
|
// Wizard / first-run
|
||||||
WizardPhase getWizardPhase() const { return wizard_phase_; }
|
WizardPhase getWizardPhase() const { return wizard_phase_; }
|
||||||
bool isFirstRun() const;
|
bool isFirstRun() const;
|
||||||
@@ -456,7 +512,11 @@ public:
|
|||||||
// Switch the active wallet: persist the new -wallet=<name>, stop the node, restart on it
|
// Switch the active wallet: persist the new -wallet=<name>, stop the node, restart on it
|
||||||
// (rescan only if it was never synced in this datadir). Per-wallet data follows automatically
|
// (rescan only if it was never synced in this datadir). Per-wallet data follows automatically
|
||||||
// via the identity-scoped caches (P1). No-op if that wallet is already active.
|
// via the identity-scoped caches (P1). No-op if that wallet is already active.
|
||||||
void switchToWallet(const std::string& walletFile);
|
// stopDaemonConfirmed: skip the "stop the running node?" confirmation (set true when re-entered from
|
||||||
|
// that dialog). salvage: start the target node with -salvagewallet (repair a corrupt wallet).
|
||||||
|
void switchToWallet(const std::string& walletFile, bool stopDaemonConfirmed = false, bool salvage = false);
|
||||||
|
// Main-thread continuation: if a wallet switch's daemon failed to start, revert active_wallet_file.
|
||||||
|
void processWalletSwitchRevert();
|
||||||
|
|
||||||
// Wallet encryption helpers
|
// Wallet encryption helpers
|
||||||
void encryptWalletWithPassphrase(const std::string& passphrase);
|
void encryptWalletWithPassphrase(const std::string& passphrase);
|
||||||
@@ -513,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);
|
||||||
}
|
}
|
||||||
@@ -554,10 +617,13 @@ private:
|
|||||||
// the recipient `to`/`amount`/`memo`/`fee` used to record the optimistic pending-send row).
|
// the recipient `to`/`amount`/`memo`/`fee` used to record the optimistic pending-send row).
|
||||||
// When markFeeGapRetry is set, the returned opid is recorded in send_feegap_retried_opids_ so a
|
// When markFeeGapRetry is set, the returned opid is recorded in send_feegap_retried_opids_ so a
|
||||||
// retry of a retry is reported as a real error.
|
// retry of a retry is reported as a real error.
|
||||||
|
// background=true (autonomous chat sends / note-buffer splits): keep the single-flight + opid
|
||||||
|
// accounting but DON'T raise the global "transaction in progress" UI (status is on the chat message).
|
||||||
void submitZSendMany(const std::string& from, const std::string& to, double amount, double fee,
|
void submitZSendMany(const std::string& from, const std::string& to, double amount, double fee,
|
||||||
const std::string& memo, const nlohmann::json& recipients,
|
const std::string& memo, const nlohmann::json& recipients,
|
||||||
const char* traceLabel, bool markFeeGapRetry,
|
const char* traceLabel, bool markFeeGapRetry,
|
||||||
std::function<void(bool, const std::string&)> callback);
|
std::function<void(bool, const std::string&)> callback,
|
||||||
|
bool background = false);
|
||||||
void markPendingSendTransactionSucceeded(const std::string& opid,
|
void markPendingSendTransactionSucceeded(const std::string& opid,
|
||||||
const std::string& txid);
|
const std::string& txid);
|
||||||
void removePendingSendTransactions(const std::vector<std::string>& opids,
|
void removePendingSendTransactions(const std::vector<std::string>& opids,
|
||||||
@@ -635,11 +701,96 @@ private:
|
|||||||
chat::ChatDatabase chat_db_; // persistent backing (seed-derived encryption at rest)
|
chat::ChatDatabase chat_db_; // persistent backing (seed-derived encryption at rest)
|
||||||
bool chat_identity_provisioned_ = false; // identity set on the service this session
|
bool chat_identity_provisioned_ = false; // identity set on the service this session
|
||||||
bool chat_identity_fetch_in_flight_ = false; // a z_exportmnemonic worker job is pending
|
bool chat_identity_fetch_in_flight_ = false; // a z_exportmnemonic worker job is pending
|
||||||
|
// Bumped by resetChatSession() on every wallet change; an in-flight identity fetch captures the
|
||||||
|
// value at post time and its completion callback discards its (previous-wallet) secret if the id
|
||||||
|
// no longer matches — so wallet A's seed can't be provisioned under wallet B via a stale job.
|
||||||
|
int chat_session_generation_ = 0;
|
||||||
bool chat_identity_unavailable_ = false; // provisioning failed definitively (e.g. non-mnemonic wallet)
|
bool chat_identity_unavailable_ = false; // provisioning failed definitively (e.g. non-mnemonic wallet)
|
||||||
|
// Per-conversation "last seen" watermark (message timestamp) for unread tracking (Q1). Updated when a
|
||||||
|
// thread is viewed (markChatConversationSeen); wiped in resetChatSession so unread doesn't leak across
|
||||||
|
// wallets. In-memory only (resets on app restart).
|
||||||
|
std::map<std::string, std::int64_t> chat_seen_watermark_;
|
||||||
|
|
||||||
|
// ── Chat note buffer (BOTH variants) ────────────────────────────────────────────────────────
|
||||||
|
// Each chat message is a shielded tx that spends a note; its change needs a few confirmations before
|
||||||
|
// it's spendable again (lite: backend ANCHOR_OFFSET+1 = 5; full node: z_sendmany minconf = 1), so
|
||||||
|
// rapid sends run out of verified funds. We keep a buffer of ~kChatBufferTarget small self-notes so a
|
||||||
|
// burst of messages each spends a separate verified note, refilled from change + background self-
|
||||||
|
// splits. Chat sends, self-splits and user sends share the wallet's single send channel; inflight_op_
|
||||||
|
// + (lite) lite_send_callback_ / (full node) send_submissions_in_flight_+pending_opids_ serialize them
|
||||||
|
// so exactly one send is ever outstanding. Implemented in app_network.cpp; pumped from update().
|
||||||
|
enum class LiteOpKind { None, ChatSend, ContactRequest, Split, UserSend };
|
||||||
|
struct LiteInflightOp {
|
||||||
|
LiteOpKind kind = LiteOpKind::None;
|
||||||
|
std::string echoLocalId; // ChatSend/ContactRequest: the echo to resolve when it completes
|
||||||
|
int sessionGen = 0; // chat_session_generation_ snapshot at submit (stale-guard)
|
||||||
|
double submittedAt = 0.0;
|
||||||
|
};
|
||||||
|
struct QueuedChatOp {
|
||||||
|
LiteOpKind kind = LiteOpKind::ChatSend;
|
||||||
|
chat::OutgoingChatMemos memos; // kept so a transient-funds retry re-broadcasts, never recomposes
|
||||||
|
std::string echoLocalId;
|
||||||
|
int sessionGen = 0;
|
||||||
|
int retries = 0;
|
||||||
|
};
|
||||||
|
LiteInflightOp inflight_op_; // the single chat/split op currently on the send channel
|
||||||
|
std::deque<QueuedChatOp> chat_send_queue_; // chat/contact sends awaiting a free channel + verified note
|
||||||
|
// Note-availability estimate between refreshes/scans: reset from a fresh count, decremented on each chat
|
||||||
|
// submit (the count lags a spend by a cycle, but the wallet still picks a fresh note per send). Zeroed on
|
||||||
|
// a transient-funds failure so we stop draining until the next refresh/scan restores the truth.
|
||||||
|
int chat_verified_note_budget_ = 0;
|
||||||
|
int chat_pipeline_note_count_ = 0; // verified + maturing self-notes (drives shouldSplit)
|
||||||
|
std::uint64_t chat_verified_shielded_zat_ = 0; // verified shielded balance (split affordability)
|
||||||
|
bool chat_note_model_seen_ = false; // saw a refresh/scan carrying per-note visibility
|
||||||
|
// Single-split-in-flight guard: a self-split's OUTPUT notes are invisible until mined (~1 block), far
|
||||||
|
// longer than any wall-clock cooldown — so we permit only ONE outstanding split and clear the flag when
|
||||||
|
// the pipeline recovers (outputs mined) or a watchdog expires (a split that never mines mustn't wedge
|
||||||
|
// refill forever). Prevents runaway splitting that would drain balance into fees.
|
||||||
|
bool chat_split_outstanding_ = false;
|
||||||
|
double chat_split_submitted_at_ = 0.0; // ImGui time the outstanding split was submitted (watchdog)
|
||||||
|
// Full-node only: a coordinator-owned z_listunspent worker scan feeds the per-note counts (the shared
|
||||||
|
// balance poll discards per-note data). Mirrors chat_fast_scan_in_flight_.
|
||||||
|
bool chat_note_scan_in_flight_ = false;
|
||||||
|
double chat_note_scan_last_ = 0.0; // ImGui time of the last note scan (rate limit)
|
||||||
|
// Most-recent chain tip, cached across refreshes: a lite refresh model that carries spendableOutputs
|
||||||
|
// may NOT carry sync status that same cycle (tolerated partial refresh), so verifiedSelfNoteCount reads
|
||||||
|
// this cache rather than requiring the current model to have both — else the budget flickers to 0.
|
||||||
|
std::int64_t chat_last_chain_height_ = 0;
|
||||||
|
int chat_fast_scan_last_seen_ = -1; // dedup: last memo-note count logged by the 0-conf scan
|
||||||
|
|
||||||
|
// Coordinator helpers (both variants unless noted; see app_network.cpp).
|
||||||
|
void refreshChatNoteBudget(const wallet::LiteWalletAppRefreshModel& model); // lite: recompute caches on a fresh model
|
||||||
|
void refreshChatNoteBudgetNode(); // full node: rate-limited z_listunspent worker scan
|
||||||
|
void pumpChatNoteBuffer(); // per-frame: drain the queue / build the buffer
|
||||||
|
int verifiedSelfNoteCount(const wallet::LiteWalletAppRefreshModel& model); // lite
|
||||||
|
int pipelineSelfNoteCount(const wallet::LiteWalletAppRefreshModel& model); // lite
|
||||||
|
bool shouldSplitChatBuffer();
|
||||||
|
bool broadcastSelfSplitLite(int noteCount); // lite: self-send minting noteCount reply-address notes
|
||||||
|
bool broadcastSelfSplitNode(int noteCount); // full node: z_sendmany self-send minting noteCount notes
|
||||||
|
void enqueueChatSend(LiteOpKind kind, const chat::OutgoingChatMemos& memos, const std::string& echoLocalId);
|
||||||
|
void onChatBroadcastResult(const LiteInflightOp& op, bool ok, const std::string& error);
|
||||||
|
static bool isTransientVerifiedFundsError(const std::string& error);
|
||||||
|
int chatConfsRequired() const; // verified-note confs threshold: 5 (lite) / 1 (full node)
|
||||||
|
public:
|
||||||
|
// Status-bar summary of the chat note buffer (empty when not applicable). Shown while the Chat tab is
|
||||||
|
// active so the buffer's state (ready / building / sending) is visible.
|
||||||
|
std::string chatBufferStatusText();
|
||||||
|
private:
|
||||||
|
public:
|
||||||
|
// Total unread incoming chat messages across all conversations (for the sidebar badge). 0 when the
|
||||||
|
// feature is off / no identity.
|
||||||
|
int chatUnreadCount() const;
|
||||||
|
// Mark a conversation read up to latestTs (called by the Chat tab while a thread is displayed).
|
||||||
|
void markChatConversationSeen(const std::string& cid, std::int64_t latestTs);
|
||||||
|
private:
|
||||||
// Provision the chat identity once the wallet seed is reachable+unlocked (per-tick, both
|
// Provision the chat identity once the wallet seed is reachable+unlocked (per-tick, both
|
||||||
// variants); derives via deriveChatIdentityFromSecret and wipes the secret. No-op when the
|
// variants); derives via deriveChatIdentityFromSecret and wipes the secret. No-op when the
|
||||||
// feature is off, already provisioned, in flight, or unavailable.
|
// feature is off, already provisioned, in flight, or unavailable.
|
||||||
void maybeProvisionChatIdentity();
|
void maybeProvisionChatIdentity();
|
||||||
|
// Drop the in-memory chat identity + decrypted message store, lock the chat DB, and re-arm
|
||||||
|
// provisioning. MUST be called whenever the loaded wallet changes (switch / seed-migration adopt)
|
||||||
|
// so wallet A's private chat can't surface — or be signed with A's keys — under wallet B.
|
||||||
|
void resetChatSession();
|
||||||
// One-time nudge: on a full-node wallet that has a mnemonic, remind the user (once per
|
// One-time nudge: on a full-node wallet that has a mnemonic, remind the user (once per
|
||||||
// install) to back up their seed phrase. Cheap early-outs keep it idle until it can act.
|
// install) to back up their seed phrase. Cheap early-outs keep it idle until it can act.
|
||||||
void maybeRemindSeedBackup();
|
void maybeRemindSeedBackup();
|
||||||
@@ -653,11 +804,23 @@ private:
|
|||||||
void pollSweepStatus(); // Confirming step: poll sweep confirmations + legacy balance
|
void pollSweepStatus(); // Confirming step: poll sweep confirmations + legacy balance
|
||||||
void beginAdoptSeedWallet(); // stop daemon -> swap wallet.dat -> restart with -rescan
|
void beginAdoptSeedWallet(); // stop daemon -> swap wallet.dat -> restart with -rescan
|
||||||
void provisionChatIdentityFromSecret(std::string secret);
|
void provisionChatIdentityFromSecret(std::string secret);
|
||||||
std::string chatReplyZaddr(); // a stable (persisted) wallet z-addr for chat
|
std::string chatReplyZaddr(); // a stable (persisted) wallet z-addr — chat IDENTITY (reply-to)
|
||||||
|
// A spendable z-address that can actually PAY the fee (balance >= fee), preferring the identity
|
||||||
|
// reply address. The reply-to in the memo stays the identity address, so paying from a different
|
||||||
|
// funded note is transport-transparent. Empty if no z-address can cover the fee.
|
||||||
|
std::string chatPayFromZaddr(double fee) const;
|
||||||
std::string generateChatLocalId(const char* prefix, int numBytes) const; // unique echo id / cid
|
std::string generateChatLocalId(const char* prefix, int numBytes) const; // unique echo id / cid
|
||||||
bool broadcastChatMemos(const chat::OutgoingChatMemos& memos); // returns true if submitted
|
// Broadcast the memos and, when the async op resolves, flip the echo (echoLocalId) to Sent/Failed.
|
||||||
|
bool broadcastChatMemos(const chat::OutgoingChatMemos& memos, const std::string& echoLocalId); // true if submitted
|
||||||
bool broadcastChatMemosLite(const chat::OutgoingChatMemos& memos); // lite two-recipient send
|
bool broadcastChatMemosLite(const chat::OutgoingChatMemos& memos); // lite two-recipient send
|
||||||
void ingestLiteChatMemos(const wallet::LiteWalletAppRefreshModel& model); // lite chat receive harvest
|
void ingestLiteChatMemos(const wallet::LiteWalletAppRefreshModel& model); // lite chat receive harvest
|
||||||
|
// Full-node 0-conf fast path: re-scan just the chat reply address at minconf=0 every refresh cycle
|
||||||
|
// so incoming messages surface at mempool speed (before a block). Hidden conversations are skipped
|
||||||
|
// (they still un-hide via the normal confirmed harvest). Self-gated; no-op without a chat identity.
|
||||||
|
void fastScanChatMemos();
|
||||||
|
bool chat_fast_scan_in_flight_ = false; // guard against overlapping fast-scan RPCs
|
||||||
|
float chat_fast_scan_accum_ = 0.0f; // seconds since the last fast-scan (dedicated ~2.5s poll)
|
||||||
|
bool font_rebuild_requested_ = false; // set by requestFontRebuild(); consumed in preFrame()
|
||||||
// Lite first-run welcome prompt: dismissed for the session once the user picks an action.
|
// Lite first-run welcome prompt: dismissed for the session once the user picks an action.
|
||||||
bool lite_firstrun_dismissed_ = false;
|
bool lite_firstrun_dismissed_ = false;
|
||||||
// Lite send-time unlock: set to show the unlock modal when a spend is attempted while locked.
|
// Lite send-time unlock: set to show the unlock modal when a spend is attempted while locked.
|
||||||
@@ -675,6 +838,19 @@ private:
|
|||||||
bool balance_snapshot_seen_ = false; // the current in-flight snapshot was applied
|
bool balance_snapshot_seen_ = false; // the current in-flight snapshot was applied
|
||||||
bool exchanges_fetch_started_ = false; // once-per-session CoinGecko tickers fetch
|
bool exchanges_fetch_started_ = false; // once-per-session CoinGecko tickers fetch
|
||||||
bool chart_fetch_in_flight_ = false; // a market_chart history fetch is on the worker
|
bool chart_fetch_in_flight_ = false; // a market_chart history fetch is on the worker
|
||||||
|
// Per-exchange candle chart (refreshExchangeChart): which pair the loaded series is for, an
|
||||||
|
// in-flight guard, and a slow refresh timer (candles move slowly, like the aggregate chart).
|
||||||
|
std::string exchange_chart_key_; // "<identifier>:<BASE>/<QUOTE>" of the loaded series ("" = none)
|
||||||
|
bool exchange_chart_fetch_in_flight_ = false;
|
||||||
|
std::chrono::steady_clock::time_point exchange_chart_last_fetch_{};
|
||||||
|
// Per-pair candle cache: switching back to a recently-viewed venue loads instantly (no re-fetch)
|
||||||
|
// instead of overwriting the single active buffer. Keyed like exchange_chart_key_.
|
||||||
|
struct ExchangeChartCache {
|
||||||
|
std::vector<std::pair<std::time_t, double>> closeIntraday, closeDaily;
|
||||||
|
std::vector<data::Candle> ohlcIntraday, ohlcDaily;
|
||||||
|
std::chrono::steady_clock::time_point fetchedAt{};
|
||||||
|
};
|
||||||
|
std::unordered_map<std::string, ExchangeChartCache> exchange_chart_cache_;
|
||||||
util::AsyncTaskManager async_tasks_;
|
util::AsyncTaskManager async_tasks_;
|
||||||
bool pending_antivirus_dialog_ = false; // Show Windows Defender help dialog
|
bool pending_antivirus_dialog_ = false; // Show Windows Defender help dialog
|
||||||
|
|
||||||
@@ -694,6 +870,37 @@ private:
|
|||||||
// Daemon restart (e.g. after changing debug log categories)
|
// Daemon restart (e.g. after changing debug log categories)
|
||||||
std::atomic<bool> daemon_restarting_{false};
|
std::atomic<bool> daemon_restarting_{false};
|
||||||
|
|
||||||
|
// Wallet-switch failure recovery: the switch worker sets wallet_switch_failed_ when the new
|
||||||
|
// wallet's daemon won't start/stay up; the main loop then reverts active_wallet_file to
|
||||||
|
// wallet_switch_prev_file_ (settings writes stay on the main thread) so a broken wallet isn't
|
||||||
|
// persisted across restarts. daemon_restarting_ stays set until the revert re-arms the reconnect.
|
||||||
|
std::atomic<bool> wallet_switch_failed_{false};
|
||||||
|
// Distinguishes the failure reason for the revert message: true when the switch failed because the
|
||||||
|
// running node wouldn't release the RPC port in time (not because the target wallet is bad).
|
||||||
|
std::atomic<bool> switch_stop_failed_{false};
|
||||||
|
// True from a switch until the new wallet's daemon actually connects (onConnected). If the daemon
|
||||||
|
// instead crash-wedges (a wallet that fails LATE in init, past the fast start grace), the connect
|
||||||
|
// loop flags a revert so a broken wallet still can't stick.
|
||||||
|
std::atomic<bool> wallet_switch_pending_confirm_{false};
|
||||||
|
std::string wallet_switch_prev_file_;
|
||||||
|
// Confirm-before-stop for a switch that would stop an ADOPTED (externally-running) node. switchToWallet
|
||||||
|
// sets these and defers to renderSwitchStopDaemonDialog; confirming re-calls switchToWallet(w, true).
|
||||||
|
bool show_switch_stop_daemon_confirm_ = false;
|
||||||
|
std::string pending_switch_wallet_file_;
|
||||||
|
// Live progress for the switch modal: it stays open from confirm through stop → wait-for-exit → start →
|
||||||
|
// reconnect and auto-closes when the new node connects (onConnected). Phase is worker-updated;
|
||||||
|
// dialog_open_ gates rendering — both atomic since onConnected/the worker may run off the main thread.
|
||||||
|
// error_ is written only on the main thread (processWalletSwitchRevert) and read by the (main-thread) UI.
|
||||||
|
enum class WalletSwitchPhase : int { None = 0, Stopping, Starting, Reconnecting, Failed };
|
||||||
|
std::atomic<int> wallet_switch_phase_{0};
|
||||||
|
std::atomic<bool> wallet_switch_dialog_open_{false};
|
||||||
|
std::string wallet_switch_error_;
|
||||||
|
double wallet_switch_started_time_ = 0.0; // ImGui::GetTime() captured on first progress frame (elapsed)
|
||||||
|
// Set when a failed switch's node output indicates the target wallet is CORRUPT — the Failed modal then
|
||||||
|
// offers a one-click "-salvagewallet" repair, retrying the switch to wallet_switch_target_file_.
|
||||||
|
std::atomic<bool> switch_wallet_corrupt_{false};
|
||||||
|
std::string wallet_switch_target_file_; // the wallet we were switching TO (for a salvage retry)
|
||||||
|
|
||||||
// Set by the deleteBlockchainData worker (item count); the main loop surfaces a completion toast
|
// Set by the deleteBlockchainData worker (item count); the main loop surfaces a completion toast
|
||||||
// and resets it to -1. Atomic because the worker thread writes it and the UI thread reads/clears it.
|
// and resets it to -1. Atomic because the worker thread writes it and the UI thread reads/clears it.
|
||||||
std::atomic<int> pending_delete_result_{-1};
|
std::atomic<int> pending_delete_result_{-1};
|
||||||
@@ -777,11 +984,28 @@ private:
|
|||||||
size_t daemon_output_offset_ = 0; // for incremental output parsing (rescan detection)
|
size_t daemon_output_offset_ = 0; // for incremental output parsing (rescan detection)
|
||||||
|
|
||||||
// Export/Import state
|
// Export/Import state
|
||||||
std::string export_result_;
|
char export_result_[256] = {0}; // SECRET exported key — fixed buffer so it can be sodium_memzero'd
|
||||||
|
bool export_in_progress_ = false; // async key fetch running (spinner + disable Export)
|
||||||
|
bool export_error_ = false; // last export returned no key (locked wallet / failure)
|
||||||
char import_key_input_[512] = {0};
|
char import_key_input_[512] = {0};
|
||||||
std::string export_address_;
|
std::string export_address_;
|
||||||
std::string import_status_;
|
std::string import_status_;
|
||||||
bool import_success_ = false;
|
bool import_success_ = false;
|
||||||
|
bool import_key_reveal_ = false; // show the key in plaintext (default masked)
|
||||||
|
bool import_in_progress_ = false; // an import + rescan is running (disable/spinner)
|
||||||
|
std::string import_result_address_; // address imported on success (shown as a copy field)
|
||||||
|
bool import_view_mode_ = false; // dialog mode: false = spending key, true = viewing key
|
||||||
|
char import_key_scan_height_[16] = {0}; // optional rescan start height (shielded spend / viewing-key imports)
|
||||||
|
// --- Sweep: import a spending key then move all its funds to one of your own addresses, instead
|
||||||
|
// of keeping the key in the wallet (spending-key / non-view mode only). ---
|
||||||
|
bool import_sweep_mode_ = false; // sweep instead of a plain import
|
||||||
|
int sweep_dest_mode_ = 0; // destination: 0 = fresh shielded address, 1 = an existing one
|
||||||
|
char sweep_dest_pick_[128] = {0}; // chosen existing destination (sweep_dest_mode_ == 1)
|
||||||
|
enum class SweepStep { Idle, Running, Done, Error };
|
||||||
|
SweepStep sweep_step_ = SweepStep::Idle;
|
||||||
|
std::string sweep_status_; // progress / error text
|
||||||
|
std::string sweep_txid_; // sweep transaction id (on success)
|
||||||
|
std::string sweep_dest_shown_; // the destination address the funds were swept to
|
||||||
std::string backup_status_;
|
std::string backup_status_;
|
||||||
bool backup_success_ = false;
|
bool backup_success_ = false;
|
||||||
|
|
||||||
@@ -867,6 +1091,9 @@ private:
|
|||||||
int logo_h_ = 0;
|
int logo_h_ = 0;
|
||||||
bool logo_loaded_ = false;
|
bool logo_loaded_ = false;
|
||||||
bool logo_is_dark_variant_ = true; // tracks which variant is currently loaded
|
bool logo_is_dark_variant_ = true; // tracks which variant is currently loaded
|
||||||
|
ImU32 logo_accent_ = 0; // theme accent the SVG logo was last rasterized with (re-render on change)
|
||||||
|
ImTextureID drgx_emoji_tex_ = 0; // ":drgx:" custom chat emoji (themed to the accent, like the logo)
|
||||||
|
int drgx_emoji_w_ = 0, drgx_emoji_h_ = 0;
|
||||||
|
|
||||||
// Coin logo texture (DragonX currency icon, separate from wallet branding)
|
// Coin logo texture (DragonX currency icon, separate from wallet branding)
|
||||||
ImTextureID coin_logo_tex_ = 0;
|
ImTextureID coin_logo_tex_ = 0;
|
||||||
@@ -1077,6 +1304,7 @@ private:
|
|||||||
void renderDecryptWalletDialog();
|
void renderDecryptWalletDialog();
|
||||||
void renderPinDialogs();
|
void renderPinDialogs();
|
||||||
void renderAntivirusHelpDialog();
|
void renderAntivirusHelpDialog();
|
||||||
|
void renderSwitchStopDaemonDialog(); // confirm before stopping an adopted node to switch wallets
|
||||||
void processDeferredEncryption();
|
void processDeferredEncryption();
|
||||||
|
|
||||||
// Private methods - connection
|
// Private methods - connection
|
||||||
|
|||||||
1295
src/app_network.cpp
1295
src/app_network.cpp
File diff suppressed because it is too large
Load Diff
@@ -241,16 +241,23 @@ void App::restartDaemonAfterEncryption(const char* taskName, bool announceRestar
|
|||||||
// the daemon is restarting.
|
// the daemon is restarting.
|
||||||
connection_status_ = TR("restarting_after_encryption");
|
connection_status_ = TR("restarting_after_encryption");
|
||||||
}
|
}
|
||||||
|
// Gate the main-loop reconnect while the daemon is down (so tryConnect can't hit the stopped
|
||||||
|
// node or start a duplicate) and block a concurrent wallet switch/rescan, which check this flag.
|
||||||
|
daemon_restarting_ = true;
|
||||||
// Give daemon a moment to shut down, then restart
|
// Give daemon a moment to shut down, then restart
|
||||||
// (do this off the main thread to avoid stalling the UI)
|
// (do this off the main thread to avoid stalling the UI)
|
||||||
async_tasks_.submit(taskName, [this](const util::AsyncTaskManager::Token& token) {
|
async_tasks_.submit(taskName, [this](const util::AsyncTaskManager::Token& token) {
|
||||||
|
// daemon_restarting_ MUST be cleared on every exit (incl. an early-out or a throw), else it
|
||||||
|
// stays stuck true and wedges reconnect + all future switch/rescan/encryption operations.
|
||||||
|
try {
|
||||||
for (int i = 0; i < 20 && !token.cancelled() && !shutting_down_; ++i)
|
for (int i = 0; i < 20 && !token.cancelled() && !shutting_down_; ++i)
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
if (token.cancelled() || shutting_down_) return;
|
if (!token.cancelled() && !shutting_down_) {
|
||||||
stopEmbeddedDaemon();
|
stopEmbeddedDaemon();
|
||||||
if (token.cancelled() || shutting_down_) return;
|
if (!token.cancelled() && !shutting_down_) startEmbeddedDaemon();
|
||||||
startEmbeddedDaemon();
|
}
|
||||||
// tryConnect will be called by the update loop
|
} catch (...) {}
|
||||||
|
daemon_restarting_ = false; // re-arm reconnect (tryConnect runs from the update loop)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
ui::Notifications::instance().warning(
|
ui::Notifications::instance().warning(
|
||||||
@@ -892,8 +899,8 @@ void App::renderLockScreen() {
|
|||||||
cy += captionFont->LegacySize + 12.0f * dp;
|
cy += captionFont->LegacySize + 12.0f * dp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if PIN vault is available
|
// Check if PIN vault is available (per-wallet vault presence; not the global getPinEnabled flag).
|
||||||
bool hasPinVault = vault_ && vault_->hasVault() && settings_ && settings_->getPinEnabled();
|
bool hasPinVault = vault_ && vault_->hasVault();
|
||||||
|
|
||||||
// Mode toggle (PIN / Passphrase) — only show if PIN vault exists
|
// Mode toggle (PIN / Passphrase) — only show if PIN vault exists
|
||||||
if (hasPinVault) {
|
if (hasPinVault) {
|
||||||
@@ -1125,35 +1132,33 @@ void App::renderEncryptWalletDialog() {
|
|||||||
// Encrypt wallet dialog — multi-phase: passphrase → encrypting → PIN setup
|
// Encrypt wallet dialog — multi-phase: passphrase → encrypting → PIN setup
|
||||||
if (show_encrypt_dialog_) {
|
if (show_encrypt_dialog_) {
|
||||||
const char* dlgTitle = (encrypt_dialog_phase_ == EncryptDialogPhase::PinSetup)
|
const char* dlgTitle = (encrypt_dialog_phase_ == EncryptDialogPhase::PinSetup)
|
||||||
? "Quick-Unlock PIN" : "Encrypt Wallet";
|
? TR("wiz_pin_title") : TR("settings_encrypt_wallet");
|
||||||
|
|
||||||
// Prevent closing via X button while encrypting
|
// Prevent closing via X button while encrypting
|
||||||
bool canClose = (encrypt_dialog_phase_ != EncryptDialogPhase::Encrypting);
|
bool canClose = (encrypt_dialog_phase_ != EncryptDialogPhase::Encrypting);
|
||||||
bool* pOpen = canClose ? &show_encrypt_dialog_ : nullptr;
|
bool* pOpen = canClose ? &show_encrypt_dialog_ : nullptr;
|
||||||
|
|
||||||
if (BeginOverlayDialog(dlgTitle, pOpen, 460.0f, 0.94f)) {
|
OverlayDialogSpec ov;
|
||||||
|
ov.title = dlgTitle; ov.p_open = pOpen;
|
||||||
|
ov.style = OverlayStyle::BlurFloat;
|
||||||
|
ov.cardWidth = 480.0f; ov.idSuffix = "encrypt";
|
||||||
|
if (BeginOverlayDialog(ov)) {
|
||||||
|
|
||||||
// ---- Phase 1: Passphrase entry ----
|
// ---- Phase 1: Passphrase entry ----
|
||||||
if (encrypt_dialog_phase_ == EncryptDialogPhase::PassphraseEntry) {
|
if (encrypt_dialog_phase_ == EncryptDialogPhase::PassphraseEntry) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1, 0.7f, 0.3f, 1));
|
DialogWarningHeader(TR("wiz_encrypt_warning"));
|
||||||
ImGui::TextWrapped(ICON_MD_WARNING
|
|
||||||
" If you lose your passphrase, you lose access to your funds.");
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
ImGui::TextWrapped("Encrypting your wallet protects your private keys "
|
ImGui::TextWrapped("%s", TR("enc_desc"));
|
||||||
"with a passphrase. After encryption, the daemon will restart.");
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
ImGui::Text("Passphrase:");
|
ImGui::TextUnformatted(TR("wiz_passphrase"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
ImGui::InputText("##enc_pass", encrypt_pass_buf_, sizeof(encrypt_pass_buf_),
|
ImGui::InputText("##enc_pass", encrypt_pass_buf_, sizeof(encrypt_pass_buf_),
|
||||||
ImGuiInputTextFlags_Password);
|
ImGuiInputTextFlags_Password);
|
||||||
ImGui::PopItemWidth();
|
ImGui::PopItemWidth();
|
||||||
|
|
||||||
ImGui::Text("Confirm:");
|
ImGui::TextUnformatted(TR("enc_confirm"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
ImGui::InputText("##enc_confirm", encrypt_confirm_buf_, sizeof(encrypt_confirm_buf_),
|
ImGui::InputText("##enc_confirm", encrypt_confirm_buf_, sizeof(encrypt_confirm_buf_),
|
||||||
ImGuiInputTextFlags_Password);
|
ImGuiInputTextFlags_Password);
|
||||||
@@ -1174,7 +1179,7 @@ void App::renderEncryptWalletDialog() {
|
|||||||
}
|
}
|
||||||
int classes = (int)hasDigit + (int)hasLower + (int)hasUpper + (int)hasSymbol;
|
int classes = (int)hasDigit + (int)hasLower + (int)hasUpper + (int)hasSymbol;
|
||||||
|
|
||||||
const char* strengthLabel = "Weak";
|
const char* strengthLabel = TR("wiz_strength_weak");
|
||||||
ImVec4 strengthCol(0.9f, 0.2f, 0.2f, 1.0f);
|
ImVec4 strengthCol(0.9f, 0.2f, 0.2f, 1.0f);
|
||||||
float strengthPct = 0.25f;
|
float strengthPct = 0.25f;
|
||||||
int tier = 0; // 0=Weak, 1=Fair, 2=Good, 3=Strong
|
int tier = 0; // 0=Weak, 1=Fair, 2=Good, 3=Strong
|
||||||
@@ -1183,9 +1188,9 @@ void App::renderEncryptWalletDialog() {
|
|||||||
else if (len >= 8) tier = 1;
|
else if (len >= 8) tier = 1;
|
||||||
// Downgrade one tier when only a single character class is used.
|
// Downgrade one tier when only a single character class is used.
|
||||||
if (classes <= 1 && tier > 0) tier -= 1;
|
if (classes <= 1 && tier > 0) tier -= 1;
|
||||||
if (tier == 3) { strengthLabel = "Strong"; strengthCol = ImVec4(0.3f,0.9f,0.5f,1); strengthPct = 1.0f; }
|
if (tier == 3) { strengthLabel = TR("wiz_strength_strong"); strengthCol = ImVec4(0.3f,0.9f,0.5f,1); strengthPct = 1.0f; }
|
||||||
else if (tier == 2) { strengthLabel = "Good"; strengthCol = ImVec4(0.3f,0.9f,0.5f,1); strengthPct = 0.75f; }
|
else if (tier == 2) { strengthLabel = TR("wiz_strength_good"); strengthCol = ImVec4(0.3f,0.9f,0.5f,1); strengthPct = 0.75f; }
|
||||||
else if (tier == 1) { strengthLabel = "Fair"; strengthCol = ImVec4(1,0.7f,0.3f,1); strengthPct = 0.5f; }
|
else if (tier == 1) { strengthLabel = TR("wiz_strength_fair"); strengthCol = ImVec4(1,0.7f,0.3f,1); strengthPct = 0.5f; }
|
||||||
|
|
||||||
float barW = ImGui::GetContentRegionAvail().x;
|
float barW = ImGui::GetContentRegionAvail().x;
|
||||||
float barH = 4.0f;
|
float barH = 4.0f;
|
||||||
@@ -1197,7 +1202,7 @@ void App::renderEncryptWalletDialog() {
|
|||||||
dl->AddRectFilled(p, ImVec2(p.x + barW * strengthPct, p.y + barH),
|
dl->AddRectFilled(p, ImVec2(p.x + barW * strengthPct, p.y + barH),
|
||||||
ImGui::ColorConvertFloat4ToU32(strengthCol), 2.0f);
|
ImGui::ColorConvertFloat4ToU32(strengthCol), 2.0f);
|
||||||
ImGui::Dummy(ImVec2(barW, barH));
|
ImGui::Dummy(ImVec2(barW, barH));
|
||||||
ImGui::Text("Strength: %s", strengthLabel);
|
ImGui::Text(TR("wiz_strength"), strengthLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!encrypt_status_.empty()) {
|
if (!encrypt_status_.empty()) {
|
||||||
@@ -1210,7 +1215,7 @@ void App::renderEncryptWalletDialog() {
|
|||||||
|
|
||||||
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||||
ImGui::BeginDisabled(!valid || encrypt_in_progress_);
|
ImGui::BeginDisabled(!valid || encrypt_in_progress_);
|
||||||
if (ui::material::TactileButton("Encrypt Wallet", ImVec2(btnW, 40))) {
|
if (ui::material::TactileButton(TR("settings_encrypt_wallet"), ImVec2(btnW, 40))) {
|
||||||
std::string pass(encrypt_pass_buf_);
|
std::string pass(encrypt_pass_buf_);
|
||||||
enc_dlg_saved_passphrase_ = pass;
|
enc_dlg_saved_passphrase_ = pass;
|
||||||
memset(encrypt_pass_buf_, 0, sizeof(encrypt_pass_buf_));
|
memset(encrypt_pass_buf_, 0, sizeof(encrypt_pass_buf_));
|
||||||
@@ -1222,7 +1227,7 @@ void App::renderEncryptWalletDialog() {
|
|||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ui::material::TactileButton("Cancel", ImVec2(btnW, 40))) {
|
if (ui::material::TactileButton(TR("cancel"), ImVec2(btnW, 40))) {
|
||||||
memset(encrypt_pass_buf_, 0, sizeof(encrypt_pass_buf_));
|
memset(encrypt_pass_buf_, 0, sizeof(encrypt_pass_buf_));
|
||||||
memset(encrypt_confirm_buf_, 0, sizeof(encrypt_confirm_buf_));
|
memset(encrypt_confirm_buf_, 0, sizeof(encrypt_confirm_buf_));
|
||||||
show_encrypt_dialog_ = false;
|
show_encrypt_dialog_ = false;
|
||||||
@@ -1231,7 +1236,7 @@ void App::renderEncryptWalletDialog() {
|
|||||||
// ---- Phase 2: Encrypting in progress ----
|
// ---- Phase 2: Encrypting in progress ----
|
||||||
} else if (encrypt_dialog_phase_ == EncryptDialogPhase::Encrypting) {
|
} else if (encrypt_dialog_phase_ == EncryptDialogPhase::Encrypting) {
|
||||||
const char* statusTitle = encrypt_in_progress_
|
const char* statusTitle = encrypt_in_progress_
|
||||||
? "Encrypting wallet..." : encrypt_status_.c_str();
|
? TR("enc_encrypting") : encrypt_status_.c_str();
|
||||||
ImGui::Text("%s", statusTitle);
|
ImGui::Text("%s", statusTitle);
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
@@ -1258,7 +1263,7 @@ void App::renderEncryptWalletDialog() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::TextColored(ImVec4(1,1,1,0.4f), "Please wait, do not close the application.");
|
ImGui::TextColored(ImVec4(1,1,1,0.4f), "%s", TR("enc_wait"));
|
||||||
|
|
||||||
// Transition to PIN phase when encryption finishes successfully
|
// Transition to PIN phase when encryption finishes successfully
|
||||||
if (!encrypt_in_progress_ && encrypt_dialog_phase_ == EncryptDialogPhase::Encrypting) {
|
if (!encrypt_in_progress_ && encrypt_dialog_phase_ == EncryptDialogPhase::Encrypting) {
|
||||||
@@ -1268,23 +1273,20 @@ void App::renderEncryptWalletDialog() {
|
|||||||
// ---- Phase 3: PIN setup (after successful encryption) ----
|
// ---- Phase 3: PIN setup (after successful encryption) ----
|
||||||
} else if (encrypt_dialog_phase_ == EncryptDialogPhase::PinSetup) {
|
} else if (encrypt_dialog_phase_ == EncryptDialogPhase::PinSetup) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.3f, 0.9f, 0.5f, 1));
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.3f, 0.9f, 0.5f, 1));
|
||||||
ImGui::Text(ICON_MD_CHECK_CIRCLE " Wallet encrypted successfully!");
|
ImGui::Text(ICON_MD_CHECK_CIRCLE " %s", TR("enc_success"));
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
ImGui::TextWrapped("A 4-8 digit PIN lets you unlock your wallet "
|
ImGui::TextWrapped("%s", TR("enc_pin_desc"));
|
||||||
"without typing the full passphrase every time.");
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
ImGui::Text("PIN (4-8 digits):");
|
ImGui::TextUnformatted(TR("wiz_pin_label"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
ImGui::InputText("##enc_dlg_pin", enc_dlg_pin_buf_, sizeof(enc_dlg_pin_buf_),
|
ImGui::InputText("##enc_dlg_pin", enc_dlg_pin_buf_, sizeof(enc_dlg_pin_buf_),
|
||||||
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
|
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
|
||||||
ImGui::PopItemWidth();
|
ImGui::PopItemWidth();
|
||||||
|
|
||||||
ImGui::Text("Confirm PIN:");
|
ImGui::TextUnformatted(TR("wiz_pin_confirm"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
ImGui::InputText("##enc_dlg_pin_confirm", enc_dlg_pin_confirm_buf_,
|
ImGui::InputText("##enc_dlg_pin_confirm", enc_dlg_pin_confirm_buf_,
|
||||||
sizeof(enc_dlg_pin_confirm_buf_),
|
sizeof(enc_dlg_pin_confirm_buf_),
|
||||||
@@ -1304,7 +1306,7 @@ void App::renderEncryptWalletDialog() {
|
|||||||
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||||
|
|
||||||
ImGui::BeginDisabled(!pinValid || !hasPassphrase || pin_in_progress_);
|
ImGui::BeginDisabled(!pinValid || !hasPassphrase || pin_in_progress_);
|
||||||
if (ui::material::TactileButton("Set PIN", ImVec2(btnW, 40))) {
|
if (ui::material::TactileButton(TR("settings_set_pin"), ImVec2(btnW, 40))) {
|
||||||
pin_in_progress_ = true;
|
pin_in_progress_ = true;
|
||||||
enc_dlg_pin_status_.clear();
|
enc_dlg_pin_status_.clear();
|
||||||
std::string savedPass = enc_dlg_saved_passphrase_;
|
std::string savedPass = enc_dlg_saved_passphrase_;
|
||||||
@@ -1317,7 +1319,7 @@ void App::renderEncryptWalletDialog() {
|
|||||||
settings_->setPinEnabled(true);
|
settings_->setPinEnabled(true);
|
||||||
settings_->save();
|
settings_->save();
|
||||||
pin_in_progress_ = false;
|
pin_in_progress_ = false;
|
||||||
ui::Notifications::instance().info("PIN set successfully");
|
ui::Notifications::instance().info(TR("enc_pin_set_ok"));
|
||||||
// Clean up
|
// Clean up
|
||||||
if (!enc_dlg_saved_passphrase_.empty()) {
|
if (!enc_dlg_saved_passphrase_.empty()) {
|
||||||
util::SecureVault::secureZero(&enc_dlg_saved_passphrase_[0],
|
util::SecureVault::secureZero(&enc_dlg_saved_passphrase_[0],
|
||||||
@@ -1328,20 +1330,20 @@ void App::renderEncryptWalletDialog() {
|
|||||||
memset(enc_dlg_pin_confirm_buf_, 0, sizeof(enc_dlg_pin_confirm_buf_));
|
memset(enc_dlg_pin_confirm_buf_, 0, sizeof(enc_dlg_pin_confirm_buf_));
|
||||||
show_encrypt_dialog_ = false;
|
show_encrypt_dialog_ = false;
|
||||||
} else {
|
} else {
|
||||||
enc_dlg_pin_status_ = "Failed to create PIN vault";
|
enc_dlg_pin_status_ = TR("enc_pin_vault_fail");
|
||||||
pin_in_progress_ = false;
|
pin_in_progress_ = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
enc_dlg_pin_status_ = "Failed to create PIN vault";
|
enc_dlg_pin_status_ = TR("enc_pin_vault_fail");
|
||||||
pin_in_progress_ = false;
|
pin_in_progress_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ui::material::TactileButton("Skip", ImVec2(btnW, 40))) {
|
if (ui::material::TactileButton(TR("wiz_skip"), ImVec2(btnW, 40))) {
|
||||||
if (!enc_dlg_saved_passphrase_.empty()) {
|
if (!enc_dlg_saved_passphrase_.empty()) {
|
||||||
util::SecureVault::secureZero(&enc_dlg_saved_passphrase_[0],
|
util::SecureVault::secureZero(&enc_dlg_saved_passphrase_[0],
|
||||||
enc_dlg_saved_passphrase_.size());
|
enc_dlg_saved_passphrase_.size());
|
||||||
@@ -1350,8 +1352,7 @@ void App::renderEncryptWalletDialog() {
|
|||||||
memset(enc_dlg_pin_buf_, 0, sizeof(enc_dlg_pin_buf_));
|
memset(enc_dlg_pin_buf_, 0, sizeof(enc_dlg_pin_buf_));
|
||||||
memset(enc_dlg_pin_confirm_buf_, 0, sizeof(enc_dlg_pin_confirm_buf_));
|
memset(enc_dlg_pin_confirm_buf_, 0, sizeof(enc_dlg_pin_confirm_buf_));
|
||||||
show_encrypt_dialog_ = false;
|
show_encrypt_dialog_ = false;
|
||||||
ui::Notifications::instance().info(
|
ui::Notifications::instance().info(TR("enc_pin_skipped"));
|
||||||
"PIN skipped. You can set one later in Settings.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EndOverlayDialog();
|
EndOverlayDialog();
|
||||||
@@ -1367,21 +1368,25 @@ void App::renderEncryptWalletDialog() {
|
|||||||
|
|
||||||
// Change passphrase dialog
|
// Change passphrase dialog
|
||||||
if (show_change_passphrase_) {
|
if (show_change_passphrase_) {
|
||||||
if (BeginOverlayDialog("Change Passphrase", &show_change_passphrase_, 440.0f, 0.94f)) {
|
OverlayDialogSpec ov;
|
||||||
|
ov.title = TR("change_pass_title"); ov.p_open = &show_change_passphrase_;
|
||||||
|
ov.style = OverlayStyle::BlurFloat;
|
||||||
|
ov.cardWidth = 460.0f; ov.idSuffix = "changepass";
|
||||||
|
if (BeginOverlayDialog(ov)) {
|
||||||
|
|
||||||
ImGui::Text("Current Passphrase:");
|
ImGui::TextUnformatted(TR("change_pass_current"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
ImGui::InputText("##chg_old", change_old_pass_buf_, sizeof(change_old_pass_buf_),
|
ImGui::InputText("##chg_old", change_old_pass_buf_, sizeof(change_old_pass_buf_),
|
||||||
ImGuiInputTextFlags_Password);
|
ImGuiInputTextFlags_Password);
|
||||||
ImGui::PopItemWidth();
|
ImGui::PopItemWidth();
|
||||||
|
|
||||||
ImGui::Text("New Passphrase:");
|
ImGui::TextUnformatted(TR("change_pass_new"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
ImGui::InputText("##chg_new", change_new_pass_buf_, sizeof(change_new_pass_buf_),
|
ImGui::InputText("##chg_new", change_new_pass_buf_, sizeof(change_new_pass_buf_),
|
||||||
ImGuiInputTextFlags_Password);
|
ImGuiInputTextFlags_Password);
|
||||||
ImGui::PopItemWidth();
|
ImGui::PopItemWidth();
|
||||||
|
|
||||||
ImGui::Text("Confirm New:");
|
ImGui::TextUnformatted(TR("change_pass_confirm"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
ImGui::InputText("##chg_confirm", change_confirm_buf_, sizeof(change_confirm_buf_),
|
ImGui::InputText("##chg_confirm", change_confirm_buf_, sizeof(change_confirm_buf_),
|
||||||
ImGuiInputTextFlags_Password);
|
ImGuiInputTextFlags_Password);
|
||||||
@@ -1396,13 +1401,22 @@ void App::renderEncryptWalletDialog() {
|
|||||||
strlen(change_new_pass_buf_) >= 8 &&
|
strlen(change_new_pass_buf_) >= 8 &&
|
||||||
strcmp(change_new_pass_buf_, change_confirm_buf_) == 0;
|
strcmp(change_new_pass_buf_, change_confirm_buf_) == 0;
|
||||||
ImGui::BeginDisabled(!valid || encrypt_in_progress_);
|
ImGui::BeginDisabled(!valid || encrypt_in_progress_);
|
||||||
if (ui::material::TactileButton("Change Passphrase", ImVec2(-1, 40))) {
|
if (ui::material::TactileButton(TR("change_pass_title"), ImVec2(-1, 40))) {
|
||||||
changePassphrase(std::string(change_old_pass_buf_),
|
changePassphrase(std::string(change_old_pass_buf_),
|
||||||
std::string(change_new_pass_buf_));
|
std::string(change_new_pass_buf_));
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
EndOverlayDialog();
|
EndOverlayDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wipe the passphrase buffers if the dialog was dismissed (X / Esc /
|
||||||
|
// outside-click) without submitting. The success path already zeroes
|
||||||
|
// them in changePassphrase(); the failure path keeps them for retry.
|
||||||
|
if (!show_change_passphrase_) {
|
||||||
|
memset(change_old_pass_buf_, 0, sizeof(change_old_pass_buf_));
|
||||||
|
memset(change_new_pass_buf_, 0, sizeof(change_new_pass_buf_));
|
||||||
|
memset(change_confirm_buf_, 0, sizeof(change_confirm_buf_));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1427,26 +1441,21 @@ void App::renderDecryptWalletDialog() {
|
|||||||
bool canClose = wallet_security_workflow_.canClose();
|
bool canClose = wallet_security_workflow_.canClose();
|
||||||
bool* pOpen = canClose ? &show_decrypt_dialog_ : nullptr;
|
bool* pOpen = canClose ? &show_decrypt_dialog_ : nullptr;
|
||||||
|
|
||||||
if (BeginOverlayDialog("Remove Wallet Encryption", pOpen, 480.0f, 0.94f)) {
|
OverlayDialogSpec ov;
|
||||||
|
ov.title = TR("decrypt_title"); ov.p_open = pOpen;
|
||||||
|
ov.style = OverlayStyle::BlurFloat;
|
||||||
|
ov.cardWidth = 480.0f; ov.idSuffix = "decrypt";
|
||||||
|
if (BeginOverlayDialog(ov)) {
|
||||||
|
|
||||||
// ---- Phase 0: Passphrase entry ----
|
// ---- Phase 0: Passphrase entry ----
|
||||||
if (decryptState.phase == DecryptPhase::PassphraseEntry) {
|
if (decryptState.phase == DecryptPhase::PassphraseEntry) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1, 0.7f, 0.3f, 1));
|
DialogWarningHeader(TR("decrypt_warning"));
|
||||||
ImGui::TextWrapped(ICON_MD_WARNING
|
|
||||||
" This will remove encryption from your wallet. "
|
|
||||||
"Your private keys will be stored unprotected on disk.");
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped("%s", TR("decrypt_desc"));
|
||||||
"The wallet will be exported, the daemon restarted with a fresh "
|
|
||||||
"unencrypted wallet, and all keys re-imported. This may take "
|
|
||||||
"several minutes depending on wallet size.");
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
ImGui::Text("Current Passphrase:");
|
ImGui::TextUnformatted(TR("change_pass_current"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
bool enterPressed = ImGui::InputText("##decrypt_pass", decrypt_pass_buf_,
|
bool enterPressed = ImGui::InputText("##decrypt_pass", decrypt_pass_buf_,
|
||||||
sizeof(decrypt_pass_buf_), ImGuiInputTextFlags_Password |
|
sizeof(decrypt_pass_buf_), ImGuiInputTextFlags_Password |
|
||||||
@@ -1462,7 +1471,7 @@ void App::renderDecryptWalletDialog() {
|
|||||||
|
|
||||||
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||||
ImGui::BeginDisabled(!valid || decryptState.inProgress);
|
ImGui::BeginDisabled(!valid || decryptState.inProgress);
|
||||||
if (ui::material::TactileButton("Remove Encryption", ImVec2(btnW, 40)) || (enterPressed && valid)) {
|
if (ui::material::TactileButton(TR("settings_remove_encryption"), ImVec2(btnW, 40)) || (enterPressed && valid)) {
|
||||||
std::string passphrase(decrypt_pass_buf_);
|
std::string passphrase(decrypt_pass_buf_);
|
||||||
memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_));
|
memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_));
|
||||||
wallet_security_workflow_.start(std::chrono::steady_clock::now());
|
wallet_security_workflow_.start(std::chrono::steady_clock::now());
|
||||||
@@ -1653,7 +1662,7 @@ void App::renderDecryptWalletDialog() {
|
|||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ui::material::TactileButton("Cancel", ImVec2(btnW, 40))) {
|
if (ui::material::TactileButton(TR("cancel"), ImVec2(btnW, 40))) {
|
||||||
memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_));
|
memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_));
|
||||||
show_decrypt_dialog_ = false;
|
show_decrypt_dialog_ = false;
|
||||||
}
|
}
|
||||||
@@ -1662,11 +1671,11 @@ void App::renderDecryptWalletDialog() {
|
|||||||
} else if (decryptState.phase == DecryptPhase::Working) {
|
} else if (decryptState.phase == DecryptPhase::Working) {
|
||||||
// Step checklist
|
// Step checklist
|
||||||
const char* stepLabels[] = {
|
const char* stepLabels[] = {
|
||||||
"Unlocking wallet",
|
TR("decrypt_step_unlock"),
|
||||||
"Exporting wallet keys",
|
TR("decrypt_step_export"),
|
||||||
"Stopping daemon",
|
TR("decrypt_step_stop"),
|
||||||
"Backing up encrypted wallet",
|
TR("decrypt_step_backup"),
|
||||||
"Restarting daemon"
|
TR("decrypt_step_restart")
|
||||||
};
|
};
|
||||||
const int numSteps = 5;
|
const int numSteps = 5;
|
||||||
|
|
||||||
@@ -1743,10 +1752,9 @@ void App::renderDecryptWalletDialog() {
|
|||||||
|
|
||||||
// Step-specific hints
|
// Step-specific hints
|
||||||
if (decryptState.step == DecryptStep::RestartDaemon) {
|
if (decryptState.step == DecryptStep::RestartDaemon) {
|
||||||
ImGui::TextWrapped("Waiting for the daemon to finish starting up...");
|
ImGui::TextWrapped("%s", TR("decrypt_wait_restart"));
|
||||||
} else {
|
} else {
|
||||||
ImGui::TextWrapped("Please wait. The daemon is exporting keys, restarting, "
|
ImGui::TextWrapped("%s", TR("decrypt_wait_general"));
|
||||||
"and re-importing. This may take several minutes.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Total elapsed
|
// Total elapsed
|
||||||
@@ -1763,15 +1771,13 @@ void App::renderDecryptWalletDialog() {
|
|||||||
ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.5f, 1.0f), ICON_MD_CHECK_CIRCLE);
|
ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.5f, 1.0f), ICON_MD_CHECK_CIRCLE);
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.5f, 1.0f), "Wallet decrypted successfully!");
|
ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.5f, 1.0f), "%s", TR("decrypt_success_title"));
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped("%s", TR("decrypt_success_desc"));
|
||||||
"Your wallet is now unencrypted. A backup of the encrypted wallet "
|
|
||||||
"was saved as wallet.dat.encrypted.bak in your data directory.");
|
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
if (ui::material::TactileButton("Close", ImVec2(-1, 40))) {
|
if (ui::material::TactileButton(TR("close"), ImVec2(-1, 40))) {
|
||||||
show_decrypt_dialog_ = false;
|
show_decrypt_dialog_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1781,23 +1787,31 @@ void App::renderDecryptWalletDialog() {
|
|||||||
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), ICON_MD_ERROR);
|
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), ICON_MD_ERROR);
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "Decryption failed");
|
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%s", TR("decrypt_error_title"));
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::TextWrapped("%s", decryptState.status.c_str());
|
ImGui::TextWrapped("%s", decryptState.status.c_str());
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||||
if (ui::material::TactileButton("Try Again", ImVec2(btnW, 40))) {
|
if (ui::material::TactileButton(TR("try_again"), ImVec2(btnW, 40))) {
|
||||||
wallet_security_workflow_.reset();
|
wallet_security_workflow_.reset();
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ui::material::TactileButton("Close", ImVec2(btnW, 40))) {
|
if (ui::material::TactileButton(TR("close"), ImVec2(btnW, 40))) {
|
||||||
show_decrypt_dialog_ = false;
|
show_decrypt_dialog_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EndOverlayDialog();
|
EndOverlayDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wipe the passphrase buffer if the dialog was dismissed (X / Esc / outside-
|
||||||
|
// click) without submitting. The submit and Cancel paths already memset it;
|
||||||
|
// this covers the BlurFloat dismiss paths. (canClose is false during Working,
|
||||||
|
// so an in-flight decrypt cannot be dismissed here.)
|
||||||
|
if (!show_decrypt_dialog_) {
|
||||||
|
memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@@ -1809,29 +1823,28 @@ void App::renderPinDialogs() {
|
|||||||
|
|
||||||
// ---- Set PIN dialog ----
|
// ---- Set PIN dialog ----
|
||||||
if (show_pin_setup_) {
|
if (show_pin_setup_) {
|
||||||
if (BeginOverlayDialog("Set PIN", &show_pin_setup_, 420.0f, 0.94f)) {
|
OverlayDialogSpec ov;
|
||||||
|
ov.title = TR("settings_set_pin"); ov.p_open = &show_pin_setup_;
|
||||||
|
ov.style = OverlayStyle::BlurFloat;
|
||||||
|
ov.cardWidth = 420.0f; ov.idSuffix = "pinsetup";
|
||||||
|
if (BeginOverlayDialog(ov)) {
|
||||||
|
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped("%s", TR("pin_setup_desc"));
|
||||||
"Set a 4-8 digit PIN for quick wallet unlock. "
|
|
||||||
"Your wallet passphrase will be encrypted with this PIN "
|
|
||||||
"and stored locally.");
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
ImGui::Text("Wallet Passphrase:");
|
ImGui::TextUnformatted(TR("pin_wallet_passphrase"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
ImGui::InputText("##pin_passphrase", pin_passphrase_buf_, sizeof(pin_passphrase_buf_),
|
ImGui::InputText("##pin_passphrase", pin_passphrase_buf_, sizeof(pin_passphrase_buf_),
|
||||||
ImGuiInputTextFlags_Password);
|
ImGuiInputTextFlags_Password);
|
||||||
ImGui::PopItemWidth();
|
ImGui::PopItemWidth();
|
||||||
|
|
||||||
ImGui::Text("New PIN (4-8 digits):");
|
ImGui::TextUnformatted(TR("pin_new_label"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
ImGui::InputText("##pin_new", pin_buf_, sizeof(pin_buf_),
|
ImGui::InputText("##pin_new", pin_buf_, sizeof(pin_buf_),
|
||||||
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
|
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
|
||||||
ImGui::PopItemWidth();
|
ImGui::PopItemWidth();
|
||||||
|
|
||||||
ImGui::Text("Confirm PIN:");
|
ImGui::TextUnformatted(TR("wiz_pin_confirm"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
ImGui::InputText("##pin_confirm", pin_confirm_buf_, sizeof(pin_confirm_buf_),
|
ImGui::InputText("##pin_confirm", pin_confirm_buf_, sizeof(pin_confirm_buf_),
|
||||||
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
|
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
|
||||||
@@ -1848,7 +1861,7 @@ void App::renderPinDialogs() {
|
|||||||
strcmp(pin_buf_, pin_confirm_buf_) == 0;
|
strcmp(pin_buf_, pin_confirm_buf_) == 0;
|
||||||
|
|
||||||
ImGui::BeginDisabled(!valid || pin_in_progress_);
|
ImGui::BeginDisabled(!valid || pin_in_progress_);
|
||||||
if (ui::material::TactileButton("Set PIN", ImVec2(-1, 40))) {
|
if (ui::material::TactileButton(TR("settings_set_pin"), ImVec2(-1, 40))) {
|
||||||
pin_in_progress_ = true;
|
pin_in_progress_ = true;
|
||||||
pin_status_ = "Verifying passphrase...";
|
pin_status_ = "Verifying passphrase...";
|
||||||
|
|
||||||
@@ -1904,30 +1917,39 @@ void App::renderPinDialogs() {
|
|||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
EndOverlayDialog();
|
EndOverlayDialog();
|
||||||
}
|
}
|
||||||
|
// Wipe the passphrase/PIN buffers if the dialog was dismissed (X / Esc /
|
||||||
|
// outside-click) without submitting. The submit path already memsets them.
|
||||||
|
if (!show_pin_setup_) {
|
||||||
|
memset(pin_passphrase_buf_, 0, sizeof(pin_passphrase_buf_));
|
||||||
|
memset(pin_buf_, 0, sizeof(pin_buf_));
|
||||||
|
memset(pin_confirm_buf_, 0, sizeof(pin_confirm_buf_));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Change PIN dialog ----
|
// ---- Change PIN dialog ----
|
||||||
if (show_pin_change_) {
|
if (show_pin_change_) {
|
||||||
if (BeginOverlayDialog("Change PIN", &show_pin_change_, 420.0f, 0.94f)) {
|
OverlayDialogSpec ov;
|
||||||
|
ov.title = TR("settings_change_pin"); ov.p_open = &show_pin_change_;
|
||||||
|
ov.style = OverlayStyle::BlurFloat;
|
||||||
|
ov.cardWidth = 420.0f; ov.idSuffix = "pinchange";
|
||||||
|
if (BeginOverlayDialog(ov)) {
|
||||||
|
|
||||||
ImGui::TextWrapped("Change your unlock PIN. You need your current PIN and a new PIN.");
|
ImGui::TextWrapped("%s", TR("pin_change_desc"));
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
ImGui::Text("Current PIN:");
|
ImGui::TextUnformatted(TR("pin_current_label"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
ImGui::InputText("##pin_old", pin_old_buf_, sizeof(pin_old_buf_),
|
ImGui::InputText("##pin_old", pin_old_buf_, sizeof(pin_old_buf_),
|
||||||
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
|
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
|
||||||
ImGui::PopItemWidth();
|
ImGui::PopItemWidth();
|
||||||
|
|
||||||
ImGui::Text("New PIN (4-8 digits):");
|
ImGui::TextUnformatted(TR("pin_new_label"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
ImGui::InputText("##pin_change_new", pin_buf_, sizeof(pin_buf_),
|
ImGui::InputText("##pin_change_new", pin_buf_, sizeof(pin_buf_),
|
||||||
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
|
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
|
||||||
ImGui::PopItemWidth();
|
ImGui::PopItemWidth();
|
||||||
|
|
||||||
ImGui::Text("Confirm New PIN:");
|
ImGui::TextUnformatted(TR("pin_confirm_new_label"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
ImGui::InputText("##pin_change_confirm", pin_confirm_buf_, sizeof(pin_confirm_buf_),
|
ImGui::InputText("##pin_change_confirm", pin_confirm_buf_, sizeof(pin_confirm_buf_),
|
||||||
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
|
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
|
||||||
@@ -1944,7 +1966,7 @@ void App::renderPinDialogs() {
|
|||||||
strcmp(pin_buf_, pin_confirm_buf_) == 0;
|
strcmp(pin_buf_, pin_confirm_buf_) == 0;
|
||||||
|
|
||||||
ImGui::BeginDisabled(!valid || pin_in_progress_);
|
ImGui::BeginDisabled(!valid || pin_in_progress_);
|
||||||
if (ui::material::TactileButton("Change PIN", ImVec2(-1, 40))) {
|
if (ui::material::TactileButton(TR("settings_change_pin"), ImVec2(-1, 40))) {
|
||||||
pin_in_progress_ = true;
|
pin_in_progress_ = true;
|
||||||
pin_status_ = "Changing PIN...";
|
pin_status_ = "Changing PIN...";
|
||||||
std::string oldPin(pin_old_buf_);
|
std::string oldPin(pin_old_buf_);
|
||||||
@@ -1977,20 +1999,26 @@ void App::renderPinDialogs() {
|
|||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
EndOverlayDialog();
|
EndOverlayDialog();
|
||||||
}
|
}
|
||||||
|
// Wipe the PIN buffers if the dialog was dismissed without submitting.
|
||||||
|
if (!show_pin_change_) {
|
||||||
|
memset(pin_old_buf_, 0, sizeof(pin_old_buf_));
|
||||||
|
memset(pin_buf_, 0, sizeof(pin_buf_));
|
||||||
|
memset(pin_confirm_buf_, 0, sizeof(pin_confirm_buf_));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Remove PIN dialog ----
|
// ---- Remove PIN dialog ----
|
||||||
if (show_pin_remove_) {
|
if (show_pin_remove_) {
|
||||||
if (BeginOverlayDialog("Remove PIN", &show_pin_remove_, 400.0f, 0.94f)) {
|
OverlayDialogSpec ov;
|
||||||
|
ov.title = TR("settings_remove_pin"); ov.p_open = &show_pin_remove_;
|
||||||
|
ov.style = OverlayStyle::BlurFloat;
|
||||||
|
ov.cardWidth = 400.0f; ov.idSuffix = "pinremove";
|
||||||
|
if (BeginOverlayDialog(ov)) {
|
||||||
|
|
||||||
ImGui::TextWrapped(
|
ImGui::TextWrapped("%s", TR("pin_remove_desc"));
|
||||||
"Enter your current PIN to confirm removal. "
|
|
||||||
"You will need to use your full passphrase to unlock.");
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
ImGui::Text("Current PIN:");
|
ImGui::TextUnformatted(TR("pin_current_label"));
|
||||||
ImGui::PushItemWidth(-1);
|
ImGui::PushItemWidth(-1);
|
||||||
ImGui::InputText("##pin_remove", pin_old_buf_, sizeof(pin_old_buf_),
|
ImGui::InputText("##pin_remove", pin_old_buf_, sizeof(pin_old_buf_),
|
||||||
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
|
ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsDecimal);
|
||||||
@@ -2003,7 +2031,7 @@ void App::renderPinDialogs() {
|
|||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
bool valid = strlen(pin_old_buf_) >= 4;
|
bool valid = strlen(pin_old_buf_) >= 4;
|
||||||
ImGui::BeginDisabled(!valid || pin_in_progress_);
|
ImGui::BeginDisabled(!valid || pin_in_progress_);
|
||||||
if (ui::material::TactileButton("Remove PIN", ImVec2(-1, 40))) {
|
if (ui::material::TactileButton(TR("settings_remove_pin"), ImVec2(-1, 40))) {
|
||||||
pin_in_progress_ = true;
|
pin_in_progress_ = true;
|
||||||
pin_status_ = "Verifying PIN...";
|
pin_status_ = "Verifying PIN...";
|
||||||
std::string oldPin(pin_old_buf_);
|
std::string oldPin(pin_old_buf_);
|
||||||
@@ -2040,6 +2068,10 @@ void App::renderPinDialogs() {
|
|||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
EndOverlayDialog();
|
EndOverlayDialog();
|
||||||
}
|
}
|
||||||
|
// Wipe the PIN buffer if the dialog was dismissed without submitting.
|
||||||
|
if (!show_pin_remove_) {
|
||||||
|
memset(pin_old_buf_, 0, sizeof(pin_old_buf_));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,13 +14,25 @@
|
|||||||
#include "app.h"
|
#include "app.h"
|
||||||
|
|
||||||
#include "config/settings.h"
|
#include "config/settings.h"
|
||||||
|
#include "data/address_book.h"
|
||||||
#include "ui/schema/skin_manager.h"
|
#include "ui/schema/skin_manager.h"
|
||||||
#include "ui/notifications.h"
|
#include "ui/notifications.h"
|
||||||
#include "ui/sidebar.h"
|
#include "ui/sidebar.h"
|
||||||
#include "ui/windows/send_tab.h"
|
#include "ui/windows/send_tab.h"
|
||||||
#include "ui/windows/wallets_dialog.h"
|
#include "ui/windows/wallets_dialog.h"
|
||||||
|
#include "ui/windows/export_transactions_dialog.h"
|
||||||
|
#include "ui/windows/export_all_keys_dialog.h"
|
||||||
|
#include "ui/windows/bootstrap_download_dialog.h"
|
||||||
#include "ui/windows/daemon_download_dialog.h"
|
#include "ui/windows/daemon_download_dialog.h"
|
||||||
#include "ui/windows/xmrig_download_dialog.h"
|
#include "ui/windows/xmrig_download_dialog.h"
|
||||||
|
#include "ui/windows/qr_popup_dialog.h"
|
||||||
|
#include "ui/windows/request_payment_dialog.h"
|
||||||
|
#include "ui/windows/validate_address_dialog.h"
|
||||||
|
#include "ui/windows/address_label_dialog.h"
|
||||||
|
#include "ui/windows/shield_dialog.h"
|
||||||
|
#include "ui/windows/address_transfer_dialog.h"
|
||||||
|
#include "ui/windows/key_export_dialog.h"
|
||||||
|
#include "ui/windows/contacts_tab.h"
|
||||||
#include "ui/pages/settings_page.h"
|
#include "ui/pages/settings_page.h"
|
||||||
#include "util/platform.h"
|
#include "util/platform.h"
|
||||||
#include "wallet/wallet_capabilities.h"
|
#include "wallet/wallet_capabilities.h"
|
||||||
@@ -80,6 +92,30 @@ const char* kDemoMnemonic =
|
|||||||
const char* kDemoZAddr =
|
const char* kDemoZAddr =
|
||||||
"zs1demoseedwalletreceiveaddressforuisweepcaptures00000000000000000000000000";
|
"zs1demoseedwalletreceiveaddressforuisweepcaptures00000000000000000000000000";
|
||||||
const char* kDemoTxid = "b647077c471fdd2877d35c9d8b70e3c785547fae618458b4068d913ee3d1dc4f";
|
const char* kDemoTxid = "b647077c471fdd2877d35c9d8b70e3c785547fae618458b4068d913ee3d1dc4f";
|
||||||
|
|
||||||
|
// Contacts-view sweep: seed a few demo contacts (Z + T types, some global) so the Cards/List/Table
|
||||||
|
// modes render with data, and restore the real book afterward. Uses sweepSetEntries (no disk write),
|
||||||
|
// so the user's persisted address book is never touched even if the sweep is interrupted.
|
||||||
|
std::vector<data::AddressBookEntry> s_contactsSweepBackup;
|
||||||
|
void seedSweepContacts(App& a)
|
||||||
|
{
|
||||||
|
s_contactsSweepBackup = a.addressBook().entries();
|
||||||
|
data::AddressBookEntry e1("drgx pool payout address", kDemoZAddr, "mining pool payouts"); // Z, global
|
||||||
|
e1.avatar = "icon:account_balance"; // icon avatar (verifies the icon-badge render path)
|
||||||
|
data::AddressBookEntry e2("exchange deposit", "t1DemoTransparentAddressForUiSweep00000", ""); // T
|
||||||
|
// Scope to the active wallet so it's visible (the tab hides contacts not in the active wallet);
|
||||||
|
// non-empty hash also keeps it non-global -> no globe badge, so the list shows a global/non-global mix.
|
||||||
|
e2.scope = a.activeWalletIdentityHash();
|
||||||
|
data::AddressBookEntry e3("cold savings",
|
||||||
|
"zs1sweepdemocoldsavingsaddressforuicapture0000000000000000000000000000", "long-term storage"); // Z, global
|
||||||
|
a.addressBook().sweepSetEntries({ e1, e2, e3 });
|
||||||
|
}
|
||||||
|
void restoreSweepContacts(App& a)
|
||||||
|
{
|
||||||
|
a.addressBook().sweepSetEntries(s_contactsSweepBackup);
|
||||||
|
s_contactsSweepBackup.clear();
|
||||||
|
if (a.settings()) a.settings()->setContactsViewMode(0);
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::string App::screenshotDir() const
|
std::string App::screenshotDir() const
|
||||||
@@ -261,11 +297,139 @@ void App::buildSweepCatalog()
|
|||||||
|
|
||||||
// Simple bool-flag modals.
|
// Simple bool-flag modals.
|
||||||
add("modal-import-key", ui::NavPage::Overview,
|
add("modal-import-key", ui::NavPage::Overview,
|
||||||
[](App& a) { a.show_import_key_ = true; }, [](App& a) { a.show_import_key_ = false; });
|
[](App& a) { a.import_view_mode_ = false; a.show_import_key_ = true; }, [](App& a) { a.show_import_key_ = false; });
|
||||||
|
add("modal-import-viewkey", ui::NavPage::Overview,
|
||||||
|
[](App& a) { a.import_view_mode_ = true; a.show_import_key_ = true; }, [](App& a) { a.show_import_key_ = false; a.import_view_mode_ = false; });
|
||||||
add("modal-export-key", ui::NavPage::Overview,
|
add("modal-export-key", ui::NavPage::Overview,
|
||||||
[](App& a) { a.show_export_key_ = true; }, [](App& a) { a.show_export_key_ = false; });
|
[](App& a) { a.show_export_key_ = true; }, [](App& a) { a.show_export_key_ = false; });
|
||||||
|
add("modal-export-transactions", ui::NavPage::Settings,
|
||||||
|
[](App&) { ui::ExportTransactionsDialog::show(); }, [](App&) { ui::ExportTransactionsDialog::hide(); });
|
||||||
|
add("modal-export-all-keys", ui::NavPage::Settings,
|
||||||
|
[](App&) { ui::ExportAllKeysDialog::show(); }, [](App&) { ui::ExportAllKeysDialog::hide(); });
|
||||||
|
add("modal-bootstrap", ui::NavPage::Settings,
|
||||||
|
[](App& a) { ui::BootstrapDownloadDialog::show(&a); }, [](App&) { ui::BootstrapDownloadDialog::hide(); });
|
||||||
add("modal-backup", ui::NavPage::Overview,
|
add("modal-backup", ui::NavPage::Overview,
|
||||||
[](App& a) { a.show_backup_ = true; }, [](App& a) { a.show_backup_ = false; a.backup_status_.clear(); });
|
[](App& a) { a.show_backup_ = true; }, [](App& a) { a.show_backup_ = false; a.backup_status_.clear(); });
|
||||||
|
// Encrypt-wallet dialog — the redesigned passphrase-entry phase (never fires the async encrypt).
|
||||||
|
add("modal-encrypt", ui::NavPage::Settings,
|
||||||
|
[](App& a) { a.encrypt_dialog_phase_ = EncryptDialogPhase::PassphraseEntry; a.show_encrypt_dialog_ = true; },
|
||||||
|
[](App& a) {
|
||||||
|
a.show_encrypt_dialog_ = false; a.encrypt_dialog_phase_ = EncryptDialogPhase::PassphraseEntry;
|
||||||
|
a.encrypt_status_.clear();
|
||||||
|
memset(a.encrypt_pass_buf_, 0, sizeof(a.encrypt_pass_buf_));
|
||||||
|
memset(a.encrypt_confirm_buf_, 0, sizeof(a.encrypt_confirm_buf_));
|
||||||
|
});
|
||||||
|
add("modal-change-passphrase", ui::NavPage::Settings,
|
||||||
|
[](App& a) { a.show_change_passphrase_ = true; },
|
||||||
|
[](App& a) {
|
||||||
|
a.show_change_passphrase_ = false; a.encrypt_status_.clear();
|
||||||
|
memset(a.change_old_pass_buf_, 0, sizeof(a.change_old_pass_buf_));
|
||||||
|
memset(a.change_new_pass_buf_, 0, sizeof(a.change_new_pass_buf_));
|
||||||
|
memset(a.change_confirm_buf_, 0, sizeof(a.change_confirm_buf_));
|
||||||
|
});
|
||||||
|
// Remove-encryption dialog — the redesigned passphrase-entry phase (reset() keeps the
|
||||||
|
// workflow in PassphraseEntry; nothing fires the async unlock/export/restart pyramid).
|
||||||
|
add("modal-decrypt", ui::NavPage::Settings,
|
||||||
|
[](App& a) { a.wallet_security_workflow_.reset(); a.show_decrypt_dialog_ = true; },
|
||||||
|
[](App& a) {
|
||||||
|
a.show_decrypt_dialog_ = false; a.wallet_security_workflow_.reset();
|
||||||
|
memset(a.decrypt_pass_buf_, 0, sizeof(a.decrypt_pass_buf_));
|
||||||
|
});
|
||||||
|
// PIN setup / change / remove dialogs (never fire the async vault store/verify).
|
||||||
|
add("modal-pin-setup", ui::NavPage::Settings,
|
||||||
|
[](App& a) { a.show_pin_setup_ = true; },
|
||||||
|
[](App& a) {
|
||||||
|
a.show_pin_setup_ = false; a.pin_status_.clear();
|
||||||
|
memset(a.pin_passphrase_buf_, 0, sizeof(a.pin_passphrase_buf_));
|
||||||
|
memset(a.pin_buf_, 0, sizeof(a.pin_buf_));
|
||||||
|
memset(a.pin_confirm_buf_, 0, sizeof(a.pin_confirm_buf_));
|
||||||
|
});
|
||||||
|
add("modal-pin-change", ui::NavPage::Settings,
|
||||||
|
[](App& a) { a.show_pin_change_ = true; },
|
||||||
|
[](App& a) {
|
||||||
|
a.show_pin_change_ = false; a.pin_status_.clear();
|
||||||
|
memset(a.pin_old_buf_, 0, sizeof(a.pin_old_buf_));
|
||||||
|
memset(a.pin_buf_, 0, sizeof(a.pin_buf_));
|
||||||
|
memset(a.pin_confirm_buf_, 0, sizeof(a.pin_confirm_buf_));
|
||||||
|
});
|
||||||
|
add("modal-pin-remove", ui::NavPage::Settings,
|
||||||
|
[](App& a) { a.show_pin_remove_ = true; },
|
||||||
|
[](App& a) {
|
||||||
|
a.show_pin_remove_ = false; a.pin_status_.clear();
|
||||||
|
memset(a.pin_old_buf_, 0, sizeof(a.pin_old_buf_));
|
||||||
|
});
|
||||||
|
// Wave-1 standalone dialogs (rendered globally from App::render, so any page works).
|
||||||
|
add("modal-qr-popup", ui::NavPage::Receive,
|
||||||
|
[](App&) { ui::QRPopupDialog::show(kDemoZAddr, "Savings"); },
|
||||||
|
[](App&) { ui::QRPopupDialog::close(); });
|
||||||
|
add("modal-request-payment", ui::NavPage::Receive,
|
||||||
|
[](App&) { ui::RequestPaymentDialog::show(kDemoZAddr); },
|
||||||
|
[](App&) { ui::RequestPaymentDialog::hide(); });
|
||||||
|
add("modal-validate-address", ui::NavPage::Receive,
|
||||||
|
[](App&) { ui::ValidateAddressDialog::show(); },
|
||||||
|
[](App&) { ui::ValidateAddressDialog::hide(); });
|
||||||
|
add("modal-address-label", ui::NavPage::Overview,
|
||||||
|
[](App& a) { ui::AddressLabelDialog::show(&a, kDemoZAddr, true); },
|
||||||
|
[](App&) { ui::AddressLabelDialog::hide(); });
|
||||||
|
// (No modal-daemon-prompt surface: renderDaemonUpdatePrompt is gated behind !capture_mode_,
|
||||||
|
// so it can't render during a sweep. Its migration is verified by build + the shared overlay pattern.)
|
||||||
|
add("modal-antivirus", ui::NavPage::Mining,
|
||||||
|
[](App& a) { a.pending_antivirus_dialog_ = true; },
|
||||||
|
[](App& a) { a.pending_antivirus_dialog_ = false; });
|
||||||
|
add("modal-switch-stopnode", ui::NavPage::Settings,
|
||||||
|
[](App& a) { a.pending_switch_wallet_file_ = "wallet-savings.dat"; a.show_switch_stop_daemon_confirm_ = true; },
|
||||||
|
[](App& a) { a.show_switch_stop_daemon_confirm_ = false; a.pending_switch_wallet_file_.clear(); });
|
||||||
|
add("modal-switch-progress", ui::NavPage::Settings,
|
||||||
|
[](App& a) { a.wallet_switch_phase_.store(static_cast<int>(App::WalletSwitchPhase::Stopping));
|
||||||
|
a.wallet_switch_dialog_open_.store(true); },
|
||||||
|
[](App& a) { a.wallet_switch_dialog_open_.store(false);
|
||||||
|
a.wallet_switch_phase_.store(static_cast<int>(App::WalletSwitchPhase::None)); });
|
||||||
|
// Wave-2 fund/secret dialogs (setup never fires the async RPC — no button is clicked).
|
||||||
|
add("modal-shield", ui::NavPage::Send,
|
||||||
|
[](App&) { ui::ShieldDialog::showShieldCoinbase(); },
|
||||||
|
[](App&) { ui::ShieldDialog::hide(); });
|
||||||
|
add("modal-merge", ui::NavPage::Send,
|
||||||
|
[](App&) { ui::ShieldDialog::showMerge(); },
|
||||||
|
[](App&) { ui::ShieldDialog::hide(); });
|
||||||
|
// z->t transfer so the (converted) deshielding DialogWarningHeader renders.
|
||||||
|
add("modal-transfer", ui::NavPage::Overview,
|
||||||
|
[](App& a) {
|
||||||
|
ui::AddressTransferInfo info;
|
||||||
|
info.fromAddr = kDemoZAddr;
|
||||||
|
info.toAddr = "t1DemoTransparentReceiveAddress00000";
|
||||||
|
info.fromBalance = 12.5; info.toBalance = 3.0;
|
||||||
|
info.fromIsZ = true; info.toIsZ = false;
|
||||||
|
ui::AddressTransferDialog::show(&a, info);
|
||||||
|
},
|
||||||
|
[](App&) { ui::AddressTransferDialog::close(); });
|
||||||
|
// Distinct from the settings "modal-export-key" (App::renderExportKeyDialog) — this is the
|
||||||
|
// per-address KeyExportDialog opened from Overview address rows.
|
||||||
|
add("modal-key-export", ui::NavPage::Overview,
|
||||||
|
[](App&) { ui::KeyExportDialog::show(kDemoZAddr, ui::KeyExportDialog::KeyType::Private); },
|
||||||
|
[](App&) { ui::KeyExportDialog::hide(); });
|
||||||
|
// Contacts address-list view modes, each seeded with demo contacts (restored after; no disk write).
|
||||||
|
add("contacts-cards", ui::NavPage::Contacts,
|
||||||
|
[](App& a) { seedSweepContacts(a); if (a.settings()) a.settings()->setContactsViewMode(0); },
|
||||||
|
[](App& a) { restoreSweepContacts(a); });
|
||||||
|
add("contacts-list", ui::NavPage::Contacts,
|
||||||
|
[](App& a) { seedSweepContacts(a); if (a.settings()) a.settings()->setContactsViewMode(1); },
|
||||||
|
[](App& a) { restoreSweepContacts(a); });
|
||||||
|
add("contacts-table", ui::NavPage::Contacts,
|
||||||
|
[](App& a) { seedSweepContacts(a); if (a.settings()) a.settings()->setContactsViewMode(2); },
|
||||||
|
[](App& a) { restoreSweepContacts(a); });
|
||||||
|
// Revamped edit dialog: live preview + avatar picker, one surface per avatar mode.
|
||||||
|
add("contacts-edit-icon", ui::NavPage::Contacts,
|
||||||
|
[](App& a) { seedSweepContacts(a); if (a.settings()) a.settings()->setContactsViewMode(1);
|
||||||
|
ui::ContactsSweepOpenEditDialog(1); },
|
||||||
|
[](App& a) { ui::ContactsSweepCloseDialog(); restoreSweepContacts(a); });
|
||||||
|
add("contacts-edit-badge", ui::NavPage::Contacts,
|
||||||
|
[](App& a) { seedSweepContacts(a); if (a.settings()) a.settings()->setContactsViewMode(1);
|
||||||
|
ui::ContactsSweepOpenEditDialog(0); },
|
||||||
|
[](App& a) { ui::ContactsSweepCloseDialog(); restoreSweepContacts(a); });
|
||||||
|
add("contacts-edit-image", ui::NavPage::Contacts,
|
||||||
|
[](App& a) { seedSweepContacts(a); if (a.settings()) a.settings()->setContactsViewMode(1);
|
||||||
|
ui::ContactsSweepOpenEditDialog(2); },
|
||||||
|
[](App& a) { ui::ContactsSweepCloseDialog(); restoreSweepContacts(a); });
|
||||||
add("modal-about", ui::NavPage::Overview,
|
add("modal-about", ui::NavPage::Overview,
|
||||||
[](App& a) { a.show_about_ = true; }, [](App& a) { a.show_about_ = false; });
|
[](App& a) { a.show_about_ = true; }, [](App& a) { a.show_about_ = false; });
|
||||||
add("modal-settings", ui::NavPage::Settings,
|
add("modal-settings", ui::NavPage::Settings,
|
||||||
|
|||||||
@@ -129,6 +129,35 @@ bool ChatDatabase::append(const ChatMessage& message)
|
|||||||
return sqlite3_changes(db_) > 0;
|
return sqlite3_changes(db_) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChatDatabase::upsert(const ChatMessage& message)
|
||||||
|
{
|
||||||
|
if (!key_ready_ || !ensureOpen()) return false;
|
||||||
|
|
||||||
|
std::vector<unsigned char> nonce;
|
||||||
|
std::vector<unsigned char> cipher;
|
||||||
|
std::string plain = serialize(message);
|
||||||
|
const bool encrypted = encrypt(plain, nonce, cipher);
|
||||||
|
if (!plain.empty()) sodium_memzero(&plain[0], plain.size());
|
||||||
|
if (!encrypted) return false;
|
||||||
|
|
||||||
|
const std::string dedup = dedupHash(message.txid, message.payload_position);
|
||||||
|
|
||||||
|
sqlite3_stmt* stmt = nullptr;
|
||||||
|
if (sqlite3_prepare_v2(db_,
|
||||||
|
"INSERT INTO chat_messages (wallet_tag, dedup_hash, nonce, payload) VALUES (?, ?, ?, ?) "
|
||||||
|
"ON CONFLICT(wallet_tag, dedup_hash) DO UPDATE SET nonce=excluded.nonce, payload=excluded.payload",
|
||||||
|
-1, &stmt, nullptr) != SQLITE_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sqlite3_bind_text(stmt, 1, wallet_tag_.c_str(), -1, SQLITE_TRANSIENT);
|
||||||
|
sqlite3_bind_text(stmt, 2, dedup.c_str(), -1, SQLITE_TRANSIENT);
|
||||||
|
sqlite3_bind_blob(stmt, 3, nonce.data(), static_cast<int>(nonce.size()), SQLITE_TRANSIENT);
|
||||||
|
sqlite3_bind_blob(stmt, 4, cipher.data(), static_cast<int>(cipher.size()), SQLITE_TRANSIENT);
|
||||||
|
const bool done = sqlite3_step(stmt) == SQLITE_DONE;
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<ChatMessage> ChatDatabase::load()
|
std::vector<ChatMessage> ChatDatabase::load()
|
||||||
{
|
{
|
||||||
std::vector<ChatMessage> out;
|
std::vector<ChatMessage> out;
|
||||||
@@ -199,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;
|
||||||
@@ -267,7 +307,10 @@ bool ChatDatabase::deserialize(const std::string& json, ChatMessage& out) const
|
|||||||
out.body = parsed.value("b", std::string());
|
out.body = parsed.value("b", std::string());
|
||||||
out.timestamp = parsed.value("ts", static_cast<std::int64_t>(0));
|
out.timestamp = parsed.value("ts", static_cast<std::int64_t>(0));
|
||||||
out.payload_position = static_cast<std::size_t>(parsed.value("pos", static_cast<std::uint64_t>(0)));
|
out.payload_position = static_cast<std::size_t>(parsed.value("pos", static_cast<std::uint64_t>(0)));
|
||||||
out.delivery = static_cast<ChatDelivery>(parsed.value("dl", 0)); // old rows → Sent
|
out.delivery = static_cast<ChatDelivery>(parsed.value("dl", 0)); // old rows → Sent (0)
|
||||||
|
// A persisted "Sending" means we crashed mid-broadcast; the outcome is unknown. Resolve it
|
||||||
|
// optimistically to Sent on load so it can't show a stuck spinner forever.
|
||||||
|
if (out.delivery == ChatDelivery::Sending) out.delivery = ChatDelivery::Sent;
|
||||||
return true;
|
return true;
|
||||||
} catch (const std::exception&) {
|
} catch (const std::exception&) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ public:
|
|||||||
// Returns true if newly inserted; false on duplicate or while locked.
|
// Returns true if newly inserted; false on duplicate or while locked.
|
||||||
bool append(const ChatMessage& message);
|
bool append(const ChatMessage& message);
|
||||||
|
|
||||||
|
// Persist-or-overwrite one message by its (txid+position) dedup key. Unlike append(), this
|
||||||
|
// updates an existing row's payload — used for outgoing echoes whose delivery status changes
|
||||||
|
// (Sending → Sent/Failed). Returns true on success; false while locked / on error.
|
||||||
|
bool upsert(const ChatMessage& message);
|
||||||
|
|
||||||
// Decrypt and return every stored message for the unlocked wallet, in insertion order. Empty
|
// Decrypt and return every stored message for the unlocked wallet, in insertion order. Empty
|
||||||
// while locked or if none. Rows that fail to decrypt/parse are skipped.
|
// while locked or if none. Rows that fail to decrypt/parse are skipped.
|
||||||
std::vector<ChatMessage> load();
|
std::vector<ChatMessage> load();
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ namespace dragonx::chat {
|
|||||||
|
|
||||||
enum class ChatDirection { Incoming, Outgoing };
|
enum class ChatDirection { Incoming, Outgoing };
|
||||||
enum class ChatMessageKind { Message, ContactRequest };
|
enum class ChatMessageKind { Message, ContactRequest };
|
||||||
// Outgoing delivery: Sent = the broadcast was submitted; Failed = it wasn't (not connected, no
|
// Outgoing delivery status. Sending = broadcast in flight (async op not yet resolved); Sent = the
|
||||||
// spendable address, a send already in progress). Always Sent for incoming.
|
// daemon accepted + broadcast the tx; Failed = it didn't (not connected, no funded address, rejected).
|
||||||
enum class ChatDelivery { Sent, Failed };
|
// Always Sent for incoming. NB: the values are persisted (chat DB serializes the int), so Sent MUST
|
||||||
|
// stay 0 and new states are APPENDED — never reordered.
|
||||||
|
enum class ChatDelivery { Sent, Failed, Sending };
|
||||||
|
|
||||||
struct ChatMessage {
|
struct ChatMessage {
|
||||||
ChatDirection direction = ChatDirection::Incoming;
|
ChatDirection direction = ChatDirection::Incoming;
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ std::string buildHeaderMemo(const std::string& replyZaddr,
|
|||||||
const std::string& conversationId,
|
const std::string& conversationId,
|
||||||
const char* type,
|
const char* type,
|
||||||
const std::string& streamHeaderHex,
|
const std::string& streamHeaderHex,
|
||||||
const std::string& publicKeyHex)
|
const std::string& publicKeyHex,
|
||||||
|
std::int64_t sentAt)
|
||||||
{
|
{
|
||||||
nlohmann::json header;
|
nlohmann::json header;
|
||||||
header["h"] = 1; // header number (>= 1)
|
header["h"] = 1; // header number (>= 1)
|
||||||
@@ -26,6 +27,7 @@ std::string buildHeaderMemo(const std::string& replyZaddr,
|
|||||||
header["t"] = type; // "Memo" or "Cont"
|
header["t"] = type; // "Memo" or "Cont"
|
||||||
header["e"] = streamHeaderHex; // 48-hex secretstream header (Memo) / "" (Cont)
|
header["e"] = streamHeaderHex; // 48-hex secretstream header (Memo) / "" (Cont)
|
||||||
header["p"] = publicKeyHex; // my 64-hex crypto_kx public key
|
header["p"] = publicKeyHex; // my 64-hex crypto_kx public key
|
||||||
|
if (sentAt > 0) header["ts"] = sentAt; // optional sender compose time (Unix s) — receiver shows this
|
||||||
return header.dump();
|
return header.dump();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +69,8 @@ ChatComposeStatus buildOutgoingMessage(const ChatKeyPair& mine,
|
|||||||
|
|
||||||
OutgoingChatMemos memos;
|
OutgoingChatMemos memos;
|
||||||
memos.recipientZaddr = peerZaddr;
|
memos.recipientZaddr = peerZaddr;
|
||||||
memos.headerMemo = buildHeaderMemo(myReplyZaddr, conversationId, "Memo", streamHeaderHex, myPublicKeyHex);
|
memos.headerMemo = buildHeaderMemo(myReplyZaddr, conversationId, "Memo", streamHeaderHex, myPublicKeyHex,
|
||||||
|
static_cast<std::int64_t>(std::time(nullptr)));
|
||||||
memos.payloadMemo = ciphertextHex;
|
memos.payloadMemo = ciphertextHex;
|
||||||
if (memos.headerMemo.size() > kHushChatMemoByteLimit ||
|
if (memos.headerMemo.size() > kHushChatMemoByteLimit ||
|
||||||
memos.payloadMemo.size() > kHushChatMemoByteLimit) {
|
memos.payloadMemo.size() > kHushChatMemoByteLimit) {
|
||||||
@@ -95,7 +98,8 @@ ChatComposeStatus buildOutgoingContactRequest(const std::string& myPublicKeyHex,
|
|||||||
|
|
||||||
OutgoingChatMemos memos;
|
OutgoingChatMemos memos;
|
||||||
memos.recipientZaddr = peerZaddr;
|
memos.recipientZaddr = peerZaddr;
|
||||||
memos.headerMemo = buildHeaderMemo(myReplyZaddr, conversationId, "Cont", "", myPublicKeyHex);
|
memos.headerMemo = buildHeaderMemo(myReplyZaddr, conversationId, "Cont", "", myPublicKeyHex,
|
||||||
|
static_cast<std::int64_t>(std::time(nullptr)));
|
||||||
memos.payloadMemo = requestText;
|
memos.payloadMemo = requestText;
|
||||||
if (memos.headerMemo.size() > kHushChatMemoByteLimit ||
|
if (memos.headerMemo.size() > kHushChatMemoByteLimit ||
|
||||||
memos.payloadMemo.size() > kHushChatMemoByteLimit) {
|
memos.payloadMemo.size() > kHushChatMemoByteLimit) {
|
||||||
|
|||||||
@@ -124,6 +124,10 @@ HushChatHeaderParseResult parseHushChatHeaderMemo(const std::string& memo)
|
|||||||
if (!readRequiredString(object, "t", type, error)) return fail(error);
|
if (!readRequiredString(object, "t", type, error)) return fail(error);
|
||||||
if (!readRequiredString(object, "e", header.secretstream_header_hex, error)) return fail(error);
|
if (!readRequiredString(object, "e", header.secretstream_header_hex, error)) return fail(error);
|
||||||
if (!readRequiredString(object, "p", header.public_key_hex, error)) return fail(error);
|
if (!readRequiredString(object, "p", header.public_key_hex, error)) return fail(error);
|
||||||
|
// Optional sender compose time (Unix seconds). Absent on older senders — leave sent_at = 0 so the
|
||||||
|
// receiver falls back to the tx/receive time. Read leniently; never fail the header on a bad value.
|
||||||
|
if (auto it = object.find("ts"); it != object.end() && it->is_number_integer())
|
||||||
|
header.sent_at = it->get<std::int64_t>();
|
||||||
|
|
||||||
if (header.header_number < 1) return fail("header number must be positive");
|
if (header.header_number < 1) return fail("header number must be positive");
|
||||||
if (header.version != kHushChatSupportedVersion) return fail("unsupported HushChat version");
|
if (header.version != kHushChatSupportedVersion) return fail("unsupported HushChat version");
|
||||||
@@ -303,6 +307,7 @@ HushChatTransactionExtractionResult extractHushChatTransactionMetadata(
|
|||||||
metadata.sender_public_key_hex = pair.header.public_key_hex;
|
metadata.sender_public_key_hex = pair.header.public_key_hex;
|
||||||
metadata.secretstream_header_hex = pair.header.secretstream_header_hex;
|
metadata.secretstream_header_hex = pair.header.secretstream_header_hex;
|
||||||
metadata.payload_memo = pair.payload_memo;
|
metadata.payload_memo = pair.payload_memo;
|
||||||
|
metadata.sent_at = pair.header.sent_at; // carry the sender's compose time (0 if absent)
|
||||||
result.metadata.push_back(std::move(metadata));
|
result.metadata.push_back(std::move(metadata));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ struct HushChatHeader {
|
|||||||
HushChatHeaderType type = HushChatHeaderType::Message;
|
HushChatHeaderType type = HushChatHeaderType::Message;
|
||||||
std::string secretstream_header_hex;
|
std::string secretstream_header_hex;
|
||||||
std::string public_key_hex;
|
std::string public_key_hex;
|
||||||
|
// Optional sender-stamped compose time (header "ts", Unix seconds). 0 = absent (older sender) → the
|
||||||
|
// receiver falls back to the tx/receive time. Lets both sides show the SAME (send) time.
|
||||||
|
std::int64_t sent_at = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HushChatHeaderParseResult {
|
struct HushChatHeaderParseResult {
|
||||||
@@ -80,6 +83,7 @@ struct HushChatTransactionMetadata {
|
|||||||
std::string sender_public_key_hex; // header "p": peer crypto_kx public key (hex)
|
std::string sender_public_key_hex; // header "p": peer crypto_kx public key (hex)
|
||||||
std::string secretstream_header_hex; // header "e": secretstream header (hex; empty for ContactRequest)
|
std::string secretstream_header_hex; // header "e": secretstream header (hex; empty for ContactRequest)
|
||||||
std::string payload_memo; // ciphertext hex (Message) or plaintext request text (ContactRequest)
|
std::string payload_memo; // ciphertext hex (Message) or plaintext request text (ContactRequest)
|
||||||
|
std::int64_t sent_at = 0; // header "ts": sender compose time (Unix s); 0 = absent → use tx time
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HushChatTransactionExtractionResult {
|
struct HushChatTransactionExtractionResult {
|
||||||
|
|||||||
@@ -25,19 +25,41 @@ void ChatService::clearIdentity() {
|
|||||||
|
|
||||||
int ChatService::ingest(const std::vector<HushChatTransactionMetadata>& metadata,
|
int ChatService::ingest(const std::vector<HushChatTransactionMetadata>& metadata,
|
||||||
const std::unordered_map<std::string, std::int64_t>& txTimestamps,
|
const std::unordered_map<std::string, std::int64_t>& txTimestamps,
|
||||||
std::int64_t fallbackTimestamp) {
|
std::int64_t fallbackTimestamp,
|
||||||
|
std::vector<std::string>* newIncomingCids) {
|
||||||
if (!has_identity_) return 0;
|
if (!has_identity_) return 0;
|
||||||
|
|
||||||
|
const std::string myPubKey = chatIdentityPublicKeyHex(identity_);
|
||||||
|
|
||||||
int added = 0;
|
int added = 0;
|
||||||
for (const auto& meta : metadata) {
|
for (const auto& meta : metadata) {
|
||||||
|
// A memo whose sender is our OWN identity is something we sent (only we hold our key). The local
|
||||||
|
// echo already records it as outgoing — ingesting it as incoming would duplicate it as a phantom
|
||||||
|
// "from peer" message. (This also collapses same-seed self-chat, where the "peer" wallet shares
|
||||||
|
// our identity, so every message would otherwise loop back.)
|
||||||
|
if (!myPubKey.empty() && meta.sender_public_key_hex == myPubKey) continue;
|
||||||
|
|
||||||
ChatMessage message;
|
ChatMessage message;
|
||||||
message.direction = ChatDirection::Incoming;
|
message.direction = ChatDirection::Incoming;
|
||||||
message.txid = meta.txid;
|
message.txid = meta.txid;
|
||||||
message.conversation_id = meta.conversation_id;
|
message.conversation_id = meta.conversation_id;
|
||||||
message.peer_zaddr = meta.reply_zaddr;
|
message.peer_zaddr = meta.reply_zaddr;
|
||||||
message.peer_public_key_hex = meta.sender_public_key_hex;
|
message.peer_public_key_hex = meta.sender_public_key_hex;
|
||||||
|
// Reference time: the tx/receive time (block time if confirmed, else the receiver's wall clock for
|
||||||
|
// a mempool receive).
|
||||||
const auto timeIt = txTimestamps.find(meta.txid);
|
const auto timeIt = txTimestamps.find(meta.txid);
|
||||||
message.timestamp = timeIt != txTimestamps.end() ? timeIt->second : fallbackTimestamp;
|
const std::int64_t refTime = timeIt != txTimestamps.end() ? timeIt->second : fallbackTimestamp;
|
||||||
|
// Prefer the sender's stamped compose time (header "ts") — the true send time, shown identically on
|
||||||
|
// both ends. But REJECT a value implausibly in the FUTURE vs the reference: a wrong/ahead peer clock
|
||||||
|
// would otherwise pin their messages to the bottom of the thread forever. A compose time in the
|
||||||
|
// PAST is fine — the note buffer can broadcast a queued message long after it was composed, and a
|
||||||
|
// confirmed tx's block time is always >= the compose time.
|
||||||
|
constexpr std::int64_t kSenderTsFutureToleranceSec = 3600; // 1 hour of clock skew tolerated
|
||||||
|
if (meta.sent_at > 0 && (refTime <= 0 || meta.sent_at <= refTime + kSenderTsFutureToleranceSec)) {
|
||||||
|
message.timestamp = meta.sent_at;
|
||||||
|
} else {
|
||||||
|
message.timestamp = refTime;
|
||||||
|
}
|
||||||
message.payload_position = meta.payload_position;
|
message.payload_position = meta.payload_position;
|
||||||
|
|
||||||
if (meta.type == HushChatHeaderType::ContactRequest) {
|
if (meta.type == HushChatHeaderType::ContactRequest) {
|
||||||
@@ -59,6 +81,9 @@ int ChatService::ingest(const std::vector<HushChatTransactionMetadata>& metadata
|
|||||||
if (store_.append(message)) {
|
if (store_.append(message)) {
|
||||||
if (db_) db_->append(message);
|
if (db_) db_->append(message);
|
||||||
++added;
|
++added;
|
||||||
|
// Every ingested message is incoming — report its cid so the caller can notify without
|
||||||
|
// relying on a seen-watermark delta (which block-time vs wall-clock skew can swallow).
|
||||||
|
if (newIncomingCids) newIncomingCids->push_back(message.conversation_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return added;
|
return added;
|
||||||
@@ -105,4 +130,18 @@ bool ChatService::recordOutgoing(const ChatMessage& message) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChatService::recordOutgoingPending(const ChatMessage& message) {
|
||||||
|
// Persist immediately (as Sending) so a send survives an app quit before the broadcast resolves;
|
||||||
|
// resolveOutgoing() then UPSERTS the row to the final status. A stray persisted Sending (crash mid-
|
||||||
|
// broadcast) loads as Sent (see ChatDatabase::deserialize).
|
||||||
|
const bool appended = store_.append(message);
|
||||||
|
if (appended && db_) db_->upsert(message);
|
||||||
|
return appended;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatService::resolveOutgoing(const std::string& txid, ChatDelivery delivery) {
|
||||||
|
const ChatMessage* updated = store_.updateDelivery(txid, delivery);
|
||||||
|
if (updated && db_) db_->upsert(*updated); // overwrite the Sending row with the final status
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dragonx::chat
|
} // namespace dragonx::chat
|
||||||
|
|||||||
@@ -40,9 +40,13 @@ public:
|
|||||||
// when the txid isn't present. Newly-added messages are also persisted (if a database is
|
// when the txid isn't present. Newly-added messages are also persisted (if a database is
|
||||||
// attached). Returns the number of NEW messages added; 0 with no identity. Undecryptable
|
// attached). Returns the number of NEW messages added; 0 with no identity. Undecryptable
|
||||||
// Messages are dropped silently (no logging of memo/plaintext).
|
// Messages are dropped silently (no logging of memo/plaintext).
|
||||||
|
// When `newIncomingCids` is non-null it is filled with the conversation ids of the genuinely-new
|
||||||
|
// incoming (non-request) messages appended this call — a reliable "a new message arrived here"
|
||||||
|
// signal for notifications that doesn't depend on any timestamp/seen-watermark comparison.
|
||||||
int ingest(const std::vector<HushChatTransactionMetadata>& metadata,
|
int ingest(const std::vector<HushChatTransactionMetadata>& metadata,
|
||||||
const std::unordered_map<std::string, std::int64_t>& txTimestamps,
|
const std::unordered_map<std::string, std::int64_t>& txTimestamps,
|
||||||
std::int64_t fallbackTimestamp = 0);
|
std::int64_t fallbackTimestamp = 0,
|
||||||
|
std::vector<std::string>* newIncomingCids = nullptr);
|
||||||
|
|
||||||
// Attach a persistent backing store (Phase 2). Not owned. Pass nullptr to detach. New messages
|
// Attach a persistent backing store (Phase 2). Not owned. Pass nullptr to detach. New messages
|
||||||
// from ingest() are written through; loadFromDatabase() rehydrates the in-memory store from it.
|
// from ingest() are written through; loadFromDatabase() rehydrates the in-memory store from it.
|
||||||
@@ -72,6 +76,12 @@ public:
|
|||||||
// only local record of what we sent.)
|
// only local record of what we sent.)
|
||||||
bool recordOutgoing(const ChatMessage& message);
|
bool recordOutgoing(const ChatMessage& message);
|
||||||
|
|
||||||
|
// Two-phase echo for delivery tracking: record + persist immediately as Sending (survives an app
|
||||||
|
// quit), then resolveOutgoing() upserts the final status once the broadcast completes. A stray
|
||||||
|
// persisted Sending (crash mid-broadcast) loads back as Sent.
|
||||||
|
bool recordOutgoingPending(const ChatMessage& message);
|
||||||
|
void resolveOutgoing(const std::string& txid, ChatDelivery delivery);
|
||||||
|
|
||||||
const ChatStore& store() const { return store_; }
|
const ChatStore& store() const { return store_; }
|
||||||
ChatStore& store() { return store_; }
|
ChatStore& store() { return store_; }
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include "chat_store.h"
|
#include "chat_store.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace dragonx::chat {
|
namespace dragonx::chat {
|
||||||
|
|
||||||
std::string ChatStore::dedupKey(const ChatMessage& message) {
|
std::string ChatStore::dedupKey(const ChatMessage& message) {
|
||||||
@@ -19,9 +21,29 @@ std::vector<ChatMessage> ChatStore::conversation(const std::string& conversation
|
|||||||
for (const auto& message : messages_) {
|
for (const auto& message : messages_) {
|
||||||
if (message.conversation_id == conversationId) out.push_back(message);
|
if (message.conversation_id == conversationId) out.push_back(message);
|
||||||
}
|
}
|
||||||
|
// Return chronological (oldest→newest). messages_ is in scan/insertion order, which is NOT
|
||||||
|
// time-ordered (a full scan harvests txids in std::set/unordered_map order) — that would mis-order the
|
||||||
|
// rendered thread AND let the reply-target pin (first-seen peer address) latch onto a non-establishing
|
||||||
|
// message (B2). timestamp is the block/tx time (peer can't set it); tie-break txid + payload_position
|
||||||
|
// for determinism.
|
||||||
|
std::stable_sort(out.begin(), out.end(), [](const ChatMessage& a, const ChatMessage& b) {
|
||||||
|
if (a.timestamp != b.timestamp) return a.timestamp < b.timestamp;
|
||||||
|
if (a.txid != b.txid) return a.txid < b.txid;
|
||||||
|
return a.payload_position < b.payload_position;
|
||||||
|
});
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ChatMessage* ChatStore::updateDelivery(const std::string& txid, ChatDelivery delivery) {
|
||||||
|
for (auto& message : messages_) {
|
||||||
|
if (message.txid == txid) {
|
||||||
|
message.delivery = delivery;
|
||||||
|
return &message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> ChatStore::conversationIds() const {
|
std::vector<std::string> ChatStore::conversationIds() const {
|
||||||
std::vector<std::string> ids;
|
std::vector<std::string> ids;
|
||||||
std::unordered_set<std::string> seenIds;
|
std::unordered_set<std::string> seenIds;
|
||||||
|
|||||||
@@ -21,9 +21,23 @@ public:
|
|||||||
// Messages in a conversation, in insertion order.
|
// Messages in a conversation, in insertion order.
|
||||||
std::vector<ChatMessage> conversation(const std::string& conversationId) const;
|
std::vector<ChatMessage> conversation(const std::string& conversationId) const;
|
||||||
|
|
||||||
|
// Update an outgoing echo's delivery status by its local txid. Returns a pointer to the updated
|
||||||
|
// message (for the caller to persist), or nullptr if no message has that txid.
|
||||||
|
const ChatMessage* updateDelivery(const std::string& txid, ChatDelivery delivery);
|
||||||
|
|
||||||
// Distinct conversation ids, in first-seen order.
|
// Distinct conversation ids, in first-seen order.
|
||||||
std::vector<std::string> conversationIds() const;
|
std::vector<std::string> conversationIds() const;
|
||||||
|
|
||||||
|
// The set of on-chain txids that carried a chat message (sent or received, messages + contact
|
||||||
|
// requests) — used by the History tab to badge / filter chat transactions. O(messages), no copies.
|
||||||
|
std::unordered_set<std::string> chatTxids() const {
|
||||||
|
std::unordered_set<std::string> out;
|
||||||
|
out.reserve(messages_.size());
|
||||||
|
for (const auto& m : messages_)
|
||||||
|
if (!m.txid.empty()) out.insert(m.txid);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t size() const { return messages_.size(); }
|
std::size_t size() const { return messages_.size(); }
|
||||||
bool empty() const { return messages_.empty(); }
|
bool empty() const { return messages_.empty(); }
|
||||||
void clear();
|
void clear();
|
||||||
|
|||||||
@@ -147,6 +147,26 @@ bool Settings::load(const std::string& path)
|
|||||||
loadScalar(j, "language", language_);
|
loadScalar(j, "language", language_);
|
||||||
loadScalar(j, "skin_id", skin_id_);
|
loadScalar(j, "skin_id", skin_id_);
|
||||||
loadScalar(j, "chat_reply_zaddr", chat_reply_zaddr_);
|
loadScalar(j, "chat_reply_zaddr", chat_reply_zaddr_);
|
||||||
|
if (j.contains("muted_chat_cids") && j["muted_chat_cids"].is_array()) {
|
||||||
|
muted_chat_cids_.clear();
|
||||||
|
for (const auto& c : j["muted_chat_cids"])
|
||||||
|
if (c.is_string()) muted_chat_cids_.push_back(c.get<std::string>());
|
||||||
|
}
|
||||||
|
if (j.contains("hidden_chat_cids") && j["hidden_chat_cids"].is_array()) {
|
||||||
|
hidden_chat_cids_.clear();
|
||||||
|
for (const auto& c : j["hidden_chat_cids"])
|
||||||
|
if (c.is_string()) hidden_chat_cids_.push_back(c.get<std::string>());
|
||||||
|
}
|
||||||
|
// Chat-tab customization (re-clamped through the setters so hand-edited JSON stays in range).
|
||||||
|
loadScalar(j, "chat_emoji_color", chat_emoji_color_);
|
||||||
|
loadScalar(j, "chat_poll_rate_sec", chat_poll_rate_sec_); setChatPollRateSec(chat_poll_rate_sec_);
|
||||||
|
loadScalar(j, "chat_bubble_style", chat_bubble_style_); setChatBubbleStyle(chat_bubble_style_);
|
||||||
|
loadScalar(j, "chat_bubble_accent", chat_bubble_accent_); setChatBubbleAccent(chat_bubble_accent_);
|
||||||
|
loadScalar(j, "chat_density", chat_density_); setChatDensity(chat_density_);
|
||||||
|
loadScalar(j, "chat_font_scale", chat_font_scale_); setChatFontScale(chat_font_scale_);
|
||||||
|
loadScalar(j, "chat_time_format", chat_time_format_); setChatTimeFormat(chat_time_format_);
|
||||||
|
loadScalar(j, "chat_enter_sends", chat_enter_sends_);
|
||||||
|
loadScalar(j, "time_format", time_format_); setTimeFormat(time_format_);
|
||||||
loadScalar(j, "acrylic_enabled", acrylic_enabled_);
|
loadScalar(j, "acrylic_enabled", acrylic_enabled_);
|
||||||
loadScalar(j, "acrylic_quality", acrylic_quality_);
|
loadScalar(j, "acrylic_quality", acrylic_quality_);
|
||||||
loadScalar(j, "blur_multiplier", blur_multiplier_);
|
loadScalar(j, "blur_multiplier", blur_multiplier_);
|
||||||
@@ -174,7 +194,16 @@ bool Settings::load(const std::string& path)
|
|||||||
}
|
}
|
||||||
loadScalar(j, "portfolio_style", portfolio_style_);
|
loadScalar(j, "portfolio_style", portfolio_style_);
|
||||||
if (portfolio_style_ < 0 || portfolio_style_ > 2) portfolio_style_ = 0;
|
if (portfolio_style_ < 0 || portfolio_style_ > 2) portfolio_style_ = 0;
|
||||||
|
loadScalar(j, "contacts_view_mode", contacts_view_mode_);
|
||||||
|
if (contacts_view_mode_ < 0 || contacts_view_mode_ > 2) contacts_view_mode_ = 0;
|
||||||
|
loadScalar(j, "contacts_avatar_shape", contacts_avatar_shape_); setContactsAvatarShape(contacts_avatar_shape_);
|
||||||
|
loadScalar(j, "contacts_list_scale", contacts_list_scale_); setContactsListScale(contacts_list_scale_);
|
||||||
|
loadScalar(j, "animate_avatars", animate_avatars_);
|
||||||
loadScalar(j, "scanline_enabled", scanline_enabled_);
|
loadScalar(j, "scanline_enabled", scanline_enabled_);
|
||||||
|
loadScalar(j, "console_line_accents", console_line_accents_);
|
||||||
|
loadScalar(j, "console_text_color", console_text_color_);
|
||||||
|
loadScalar(j, "console_zoom", console_zoom_);
|
||||||
|
if (!(console_zoom_ >= 0.25f && console_zoom_ <= 4.0f)) console_zoom_ = 1.0f; // guard bad/NaN
|
||||||
if (j.contains("hidden_addresses") && j["hidden_addresses"].is_array()) {
|
if (j.contains("hidden_addresses") && j["hidden_addresses"].is_array()) {
|
||||||
hidden_addresses_.clear();
|
hidden_addresses_.clear();
|
||||||
for (const auto& a : j["hidden_addresses"])
|
for (const auto& a : j["hidden_addresses"])
|
||||||
@@ -285,6 +314,8 @@ bool Settings::load(const std::string& path)
|
|||||||
loadScalar(j, "reduce_motion", reduce_motion_);
|
loadScalar(j, "reduce_motion", reduce_motion_);
|
||||||
loadScalar(j, "selected_exchange", selected_exchange_);
|
loadScalar(j, "selected_exchange", selected_exchange_);
|
||||||
loadScalar(j, "selected_pair", selected_pair_);
|
loadScalar(j, "selected_pair", selected_pair_);
|
||||||
|
loadScalar(j, "chart_interval", chart_interval_);
|
||||||
|
loadScalar(j, "chart_style", chart_style_);
|
||||||
loadScalar(j, "pool_url", pool_url_);
|
loadScalar(j, "pool_url", pool_url_);
|
||||||
// Migrate old default pool URL that was missing the stratum port
|
// Migrate old default pool URL that was missing the stratum port
|
||||||
if (pool_url_ == "pool.dragonx.is") pool_url_ = "pool.dragonx.is:3433";
|
if (pool_url_ == "pool.dragonx.is") pool_url_ = "pool.dragonx.is:3433";
|
||||||
@@ -413,6 +444,21 @@ bool Settings::save(const std::string& path)
|
|||||||
j["language"] = language_;
|
j["language"] = language_;
|
||||||
j["skin_id"] = skin_id_;
|
j["skin_id"] = skin_id_;
|
||||||
j["chat_reply_zaddr"] = chat_reply_zaddr_;
|
j["chat_reply_zaddr"] = chat_reply_zaddr_;
|
||||||
|
j["muted_chat_cids"] = json::array();
|
||||||
|
for (const auto& c : muted_chat_cids_)
|
||||||
|
j["muted_chat_cids"].push_back(c);
|
||||||
|
j["hidden_chat_cids"] = json::array();
|
||||||
|
for (const auto& c : hidden_chat_cids_)
|
||||||
|
j["hidden_chat_cids"].push_back(c);
|
||||||
|
j["chat_emoji_color"] = chat_emoji_color_;
|
||||||
|
j["chat_poll_rate_sec"] = chat_poll_rate_sec_;
|
||||||
|
j["chat_bubble_style"] = chat_bubble_style_;
|
||||||
|
j["chat_bubble_accent"] = chat_bubble_accent_;
|
||||||
|
j["chat_density"] = chat_density_;
|
||||||
|
j["chat_font_scale"] = chat_font_scale_;
|
||||||
|
j["chat_time_format"] = chat_time_format_;
|
||||||
|
j["chat_enter_sends"] = chat_enter_sends_;
|
||||||
|
j["time_format"] = time_format_;
|
||||||
j["acrylic_enabled"] = acrylic_enabled_;
|
j["acrylic_enabled"] = acrylic_enabled_;
|
||||||
j["acrylic_quality"] = acrylic_quality_;
|
j["acrylic_quality"] = acrylic_quality_;
|
||||||
j["blur_multiplier"] = blur_multiplier_;
|
j["blur_multiplier"] = blur_multiplier_;
|
||||||
@@ -422,7 +468,14 @@ bool Settings::save(const std::string& path)
|
|||||||
j["window_opacity"] = window_opacity_;
|
j["window_opacity"] = window_opacity_;
|
||||||
j["balance_layout"] = balance_layout_; // saved as string ID
|
j["balance_layout"] = balance_layout_; // saved as string ID
|
||||||
j["portfolio_style"] = portfolio_style_;
|
j["portfolio_style"] = portfolio_style_;
|
||||||
|
j["contacts_view_mode"] = contacts_view_mode_;
|
||||||
|
j["contacts_avatar_shape"] = contacts_avatar_shape_;
|
||||||
|
j["contacts_list_scale"] = contacts_list_scale_;
|
||||||
|
j["animate_avatars"] = animate_avatars_;
|
||||||
j["scanline_enabled"] = scanline_enabled_;
|
j["scanline_enabled"] = scanline_enabled_;
|
||||||
|
j["console_line_accents"] = console_line_accents_;
|
||||||
|
j["console_text_color"] = console_text_color_;
|
||||||
|
j["console_zoom"] = console_zoom_;
|
||||||
j["hidden_addresses"] = json::array();
|
j["hidden_addresses"] = json::array();
|
||||||
for (const auto& addr : hidden_addresses_)
|
for (const auto& addr : hidden_addresses_)
|
||||||
j["hidden_addresses"].push_back(addr);
|
j["hidden_addresses"].push_back(addr);
|
||||||
@@ -486,6 +539,8 @@ bool Settings::save(const std::string& path)
|
|||||||
j["reduce_motion"] = reduce_motion_;
|
j["reduce_motion"] = reduce_motion_;
|
||||||
j["selected_exchange"] = selected_exchange_;
|
j["selected_exchange"] = selected_exchange_;
|
||||||
j["selected_pair"] = selected_pair_;
|
j["selected_pair"] = selected_pair_;
|
||||||
|
j["chart_interval"] = chart_interval_;
|
||||||
|
j["chart_style"] = chart_style_;
|
||||||
j["pool_url"] = pool_url_;
|
j["pool_url"] = pool_url_;
|
||||||
j["pool_algo"] = pool_algo_;
|
j["pool_algo"] = pool_algo_;
|
||||||
j["pool_worker"] = pool_worker_;
|
j["pool_worker"] = pool_worker_;
|
||||||
|
|||||||
@@ -91,7 +91,8 @@ public:
|
|||||||
bool showValue = true; // show the converted/fiat value on the card
|
bool showValue = true; // show the converted/fiat value on the card
|
||||||
bool show24h = false; // show the 24h % change (live-market bases only)
|
bool show24h = false; // show the 24h % change (live-market bases only)
|
||||||
bool showSparkline = false; // show a price-trend sparkline (live-market bases only)
|
bool showSparkline = false; // show a price-trend sparkline (live-market bases only)
|
||||||
int sparklineInterval = 0; // 0=minute 1=hour 2=day 3=week 4=month (resample of price history)
|
int sparklineInterval = 4; // 0=minute 1=hour 2=day 3=week 4=month (default month: a real curve
|
||||||
|
// from the daily series, vs the young in-session minute buffer)
|
||||||
// Per-wallet visibility: "" (shown in every wallet — legacy/global) or a wallet-identity
|
// Per-wallet visibility: "" (shown in every wallet — legacy/global) or a wallet-identity
|
||||||
// hash (shown only when that wallet is active). New entries are tagged with the current
|
// hash (shown only when that wallet is active). New entries are tagged with the current
|
||||||
// wallet so a portfolio built for wallet A doesn't clutter wallet B.
|
// wallet so a portfolio built for wallet A doesn't clutter wallet B.
|
||||||
@@ -118,6 +119,52 @@ public:
|
|||||||
std::string getChatReplyZaddr() const { return chat_reply_zaddr_; }
|
std::string getChatReplyZaddr() const { return chat_reply_zaddr_; }
|
||||||
void setChatReplyZaddr(const std::string& z) { chat_reply_zaddr_ = z; }
|
void setChatReplyZaddr(const std::string& z) { chat_reply_zaddr_ = z; }
|
||||||
|
|
||||||
|
// Muted chat conversations (by cid) — muted conversations don't badge or raise a toast (Q10).
|
||||||
|
bool isChatMuted(const std::string& cid) const {
|
||||||
|
return std::find(muted_chat_cids_.begin(), muted_chat_cids_.end(), cid) != muted_chat_cids_.end();
|
||||||
|
}
|
||||||
|
void setChatMuted(const std::string& cid, bool muted) {
|
||||||
|
const bool already = isChatMuted(cid);
|
||||||
|
if (muted && !already) muted_chat_cids_.push_back(cid);
|
||||||
|
else if (!muted && already)
|
||||||
|
muted_chat_cids_.erase(std::remove(muted_chat_cids_.begin(), muted_chat_cids_.end(), cid),
|
||||||
|
muted_chat_cids_.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hidden chat conversations (by cid) — hidden ones are filtered out of the list; a new incoming
|
||||||
|
// message un-hides them (you can't un-receive) so nothing is silently lost.
|
||||||
|
bool isChatHidden(const std::string& cid) const {
|
||||||
|
return std::find(hidden_chat_cids_.begin(), hidden_chat_cids_.end(), cid) != hidden_chat_cids_.end();
|
||||||
|
}
|
||||||
|
void setChatHidden(const std::string& cid, bool hidden) {
|
||||||
|
const bool already = isChatHidden(cid);
|
||||||
|
if (hidden && !already) hidden_chat_cids_.push_back(cid);
|
||||||
|
else if (!hidden && already)
|
||||||
|
hidden_chat_cids_.erase(std::remove(hidden_chat_cids_.begin(), hidden_chat_cids_.end(), cid),
|
||||||
|
hidden_chat_cids_.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Chat-tab customization (chat settings modal + Settings → Chat & Contacts) ──────
|
||||||
|
bool getChatEmojiColor() const { return chat_emoji_color_; }
|
||||||
|
void setChatEmojiColor(bool v) { chat_emoji_color_ = v; }
|
||||||
|
float getChatPollRateSec() const { return chat_poll_rate_sec_; }
|
||||||
|
void setChatPollRateSec(float v) { chat_poll_rate_sec_ = std::max(0.5f, std::min(15.0f, v)); }
|
||||||
|
int getChatBubbleStyle() const { return chat_bubble_style_; }
|
||||||
|
void setChatBubbleStyle(int v) { chat_bubble_style_ = (v < 0 || v > 2) ? 0 : v; }
|
||||||
|
int getChatBubbleAccent() const { return chat_bubble_accent_; }
|
||||||
|
void setChatBubbleAccent(int v) { chat_bubble_accent_ = (v < 0 || v > 5) ? 0 : v; }
|
||||||
|
int getChatDensity() const { return chat_density_; }
|
||||||
|
void setChatDensity(int v) { chat_density_ = (v < 0 || v > 1) ? 0 : v; }
|
||||||
|
float getChatFontScale() const { return chat_font_scale_; }
|
||||||
|
void setChatFontScale(float v) { chat_font_scale_ = std::max(0.8f, std::min(1.5f, v)); }
|
||||||
|
int getChatTimeFormat() const { return chat_time_format_; } // 0=follow global, 1=24h, 2=12h
|
||||||
|
void setChatTimeFormat(int v) { chat_time_format_ = (v < 0 || v > 2) ? 0 : v; }
|
||||||
|
bool getChatEnterSends() const { return chat_enter_sends_; }
|
||||||
|
void setChatEnterSends(bool v) { chat_enter_sends_ = v; }
|
||||||
|
// Global clock format (0=24h, 1=12h) — chat can override it for the Chat tab only.
|
||||||
|
int getTimeFormat() const { return time_format_; }
|
||||||
|
void setTimeFormat(int v) { time_format_ = (v < 0 || v > 1) ? 0 : v; }
|
||||||
|
|
||||||
// Privacy
|
// Privacy
|
||||||
bool getSaveZtxs() const { return save_ztxs_; }
|
bool getSaveZtxs() const { return save_ztxs_; }
|
||||||
void setSaveZtxs(bool save) { save_ztxs_ = save; }
|
void setSaveZtxs(bool save) { save_ztxs_ = save; }
|
||||||
@@ -180,15 +227,36 @@ public:
|
|||||||
std::string getBalanceLayout() const { return balance_layout_; }
|
std::string getBalanceLayout() const { return balance_layout_; }
|
||||||
void setBalanceLayout(const std::string& v) { balance_layout_ = v; }
|
void setBalanceLayout(const std::string& v) { balance_layout_ = v; }
|
||||||
|
|
||||||
// Market-tab portfolio row style: 0 = single-line, 1 = two-line, 2 = value-hero. Cycled with
|
// Market-tab portfolio row style: 0 = Table (borderless grid), 1 = Cards (glass card + Z/T bar),
|
||||||
// Left/Right arrows on the Market tab (like the Overview layouts).
|
// 2 = Spotlight (hero value). Cycled with Left/Right arrows or set in the Market settings modal.
|
||||||
int getPortfolioStyle() const { return portfolio_style_; }
|
int getPortfolioStyle() const { return portfolio_style_; }
|
||||||
void setPortfolioStyle(int v) { portfolio_style_ = (v < 0 || v > 2) ? 0 : v; }
|
void setPortfolioStyle(int v) { portfolio_style_ = (v < 0 || v > 2) ? 0 : v; }
|
||||||
|
// Contacts tab address-list view: 0 = cards, 1 = list, 2 = table.
|
||||||
|
int getContactsViewMode() const { return contacts_view_mode_; }
|
||||||
|
void setContactsViewMode(int v) { contacts_view_mode_ = (v < 0 || v > 2) ? 0 : v; }
|
||||||
|
// Contacts customization (gear modal): avatar shape + card/list row scale.
|
||||||
|
// Avatar shape: 0 = circle, 1 = rounded square, 2 = full-row-height left tab (rounded-left, flat right).
|
||||||
|
int getContactsAvatarShape() const { return contacts_avatar_shape_; }
|
||||||
|
void setContactsAvatarShape(int v) { contacts_avatar_shape_ = (v < 0 || v > 2) ? 0 : v; }
|
||||||
|
float getContactsListScale() const { return contacts_list_scale_; }
|
||||||
|
void setContactsListScale(float v) { contacts_list_scale_ = std::max(0.8f, std::min(1.5f, v)); }
|
||||||
|
// Play animated contact avatars (GIF/WebP). Off = show the first frame only.
|
||||||
|
bool getAnimateAvatars() const { return animate_avatars_; }
|
||||||
|
void setAnimateAvatars(bool v) { animate_avatars_ = v; }
|
||||||
|
|
||||||
// Console scanline effect
|
// Console scanline effect
|
||||||
bool getScanlineEnabled() const { return scanline_enabled_; }
|
bool getScanlineEnabled() const { return scanline_enabled_; }
|
||||||
void setScanlineEnabled(bool v) { scanline_enabled_ = v; }
|
void setScanlineEnabled(bool v) { scanline_enabled_ = v; }
|
||||||
|
|
||||||
|
// Console output appearance: per-line left color accent bars, and per-channel text coloring.
|
||||||
|
// (Defaults match the ConsoleTab statics so an upgrade re-save doesn't flip visible behavior.)
|
||||||
|
bool getConsoleLineAccents() const { return console_line_accents_; }
|
||||||
|
void setConsoleLineAccents(bool v) { console_line_accents_ = v; }
|
||||||
|
bool getConsoleTextColor() const { return console_text_color_; }
|
||||||
|
void setConsoleTextColor(bool v) { console_text_color_ = v; }
|
||||||
|
float getConsoleZoom() const { return console_zoom_; }
|
||||||
|
void setConsoleZoom(float v) { console_zoom_ = v; }
|
||||||
|
|
||||||
// Hidden addresses (addresses hidden from the UI by the user)
|
// Hidden addresses (addresses hidden from the UI by the user)
|
||||||
const std::set<std::string>& getHiddenAddresses() const { return hidden_addresses_; }
|
const std::set<std::string>& getHiddenAddresses() const { return hidden_addresses_; }
|
||||||
bool isAddressHidden(const std::string& addr) const { return hidden_addresses_.count(addr) > 0; }
|
bool isAddressHidden(const std::string& addr) const { return hidden_addresses_.count(addr) > 0; }
|
||||||
@@ -370,6 +438,10 @@ public:
|
|||||||
void setSelectedExchange(const std::string& v) { selected_exchange_ = v; }
|
void setSelectedExchange(const std::string& v) { selected_exchange_ = v; }
|
||||||
std::string getSelectedPair() const { return selected_pair_; }
|
std::string getSelectedPair() const { return selected_pair_; }
|
||||||
void setSelectedPair(const std::string& v) { selected_pair_ = v; }
|
void setSelectedPair(const std::string& v) { selected_pair_ = v; }
|
||||||
|
int getChartInterval() const { return chart_interval_; } // Market chart range 0=Live..4=1M
|
||||||
|
void setChartInterval(int v) { chart_interval_ = v; }
|
||||||
|
int getChartStyle() const { return chart_style_; } // 0 = line, 1 = candlestick
|
||||||
|
void setChartStyle(int v) { chart_style_ = v; }
|
||||||
|
|
||||||
// Pool mining
|
// Pool mining
|
||||||
std::string getPoolUrl() const { return pool_url_; }
|
std::string getPoolUrl() const { return pool_url_; }
|
||||||
@@ -454,6 +526,18 @@ private:
|
|||||||
std::string theme_ = "dragonx";
|
std::string theme_ = "dragonx";
|
||||||
std::string skin_id_ = "dragonx";
|
std::string skin_id_ = "dragonx";
|
||||||
std::string chat_reply_zaddr_;
|
std::string chat_reply_zaddr_;
|
||||||
|
std::vector<std::string> muted_chat_cids_; // muted chat conversations by cid (Q10)
|
||||||
|
std::vector<std::string> hidden_chat_cids_; // hidden chat conversations by cid
|
||||||
|
// Chat-tab customization (chat settings modal + Settings → Chat & Contacts).
|
||||||
|
bool chat_emoji_color_ = true; // true = color (needs FreeType; falls back to mono if absent), false = monochrome
|
||||||
|
float chat_poll_rate_sec_ = 2.5f; // 0-conf chat fast-scan cadence (full node)
|
||||||
|
int chat_bubble_style_ = 0; // 0 = rounded, 1 = square, 2 = minimal
|
||||||
|
int chat_bubble_accent_ = 0; // outgoing-bubble accent preset (0 = theme primary)
|
||||||
|
int chat_density_ = 0; // 0 = comfortable, 1 = compact
|
||||||
|
float chat_font_scale_ = 1.0f; // message text scale
|
||||||
|
int chat_time_format_ = 0; // 0 = follow global, 1 = 24h, 2 = 12h (Chat tab only)
|
||||||
|
bool chat_enter_sends_ = true; // Enter sends (vs. inserts newline; Ctrl+Enter sends)
|
||||||
|
int time_format_ = 0; // global clock: 0 = 24h, 1 = 12h
|
||||||
bool save_ztxs_ = true;
|
bool save_ztxs_ = true;
|
||||||
bool auto_shield_ = true;
|
bool auto_shield_ = true;
|
||||||
bool use_tor_ = false;
|
bool use_tor_ = false;
|
||||||
@@ -470,14 +554,21 @@ private:
|
|||||||
bool gradient_background_ = false;
|
bool gradient_background_ = false;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
float ui_opacity_ = 0.50f; // Card/sidebar opacity (0.3–1.0, 1.0 = opaque)
|
float ui_opacity_ = 0.50f; // Card/sidebar opacity (0.3–1.0, 1.0 = opaque)
|
||||||
float window_opacity_ = 0.75f; // Background alpha (0.3–1.0, <1 = desktop visible)
|
float window_opacity_ = 0.90f; // Background alpha (0.3–1.0, <1 = desktop visible)
|
||||||
#else
|
#else
|
||||||
float ui_opacity_ = 1.0f; // Mac/Linux: default fully opaque
|
float ui_opacity_ = 1.0f; // Mac/Linux: default fully opaque
|
||||||
float window_opacity_ = 1.0f; // Mac/Linux: default fully opaque
|
float window_opacity_ = 1.0f; // Mac/Linux: default fully opaque
|
||||||
#endif
|
#endif
|
||||||
std::string balance_layout_ = "classic";
|
std::string balance_layout_ = "classic";
|
||||||
int portfolio_style_ = 0; // Market portfolio row style (0 single / 1 two-line / 2 hero)
|
int portfolio_style_ = 0; // Market portfolio row style (0 Table / 1 Cards / 2 Spotlight)
|
||||||
|
int contacts_view_mode_ = 0; // Contacts address-list view (0 cards / 1 list / 2 table)
|
||||||
|
int contacts_avatar_shape_ = 0; // 0 = circle, 1 = rounded square, 2 = full-height left tab
|
||||||
|
float contacts_list_scale_ = 1.0f; // card/list row scale (does not affect the table view)
|
||||||
|
bool animate_avatars_ = true; // play animated (GIF/WebP) contact avatars
|
||||||
bool scanline_enabled_ = true;
|
bool scanline_enabled_ = true;
|
||||||
|
bool console_line_accents_ = true; // left color accent bars in console output
|
||||||
|
bool console_text_color_ = true; // per-channel text coloring in console output
|
||||||
|
float console_zoom_ = 1.0f; // console output font zoom factor
|
||||||
std::set<std::string> hidden_addresses_;
|
std::set<std::string> hidden_addresses_;
|
||||||
std::set<std::string> favorite_addresses_;
|
std::set<std::string> favorite_addresses_;
|
||||||
std::map<std::string, AddressMeta> address_meta_;
|
std::map<std::string, AddressMeta> address_meta_;
|
||||||
@@ -524,6 +615,8 @@ private:
|
|||||||
bool reduce_motion_ = false;
|
bool reduce_motion_ = false;
|
||||||
std::string selected_exchange_ = "Nonkyc.io";
|
std::string selected_exchange_ = "Nonkyc.io";
|
||||||
std::string selected_pair_ = "DRGX/USDT";
|
std::string selected_pair_ = "DRGX/USDT";
|
||||||
|
int chart_interval_ = 4; // Market chart range (0=Live 1=1H 2=1D 3=1W 4=1M)
|
||||||
|
int chart_style_ = 1; // Market chart style (0=line, 1=candlestick)
|
||||||
|
|
||||||
// Pool mining
|
// Pool mining
|
||||||
std::string pool_url_ = "pool.dragonx.is:3433";
|
std::string pool_url_ = "pool.dragonx.is:3433";
|
||||||
|
|||||||
@@ -61,6 +61,11 @@ bool DaemonController::externalDaemonDetected() const
|
|||||||
return daemon_->externalDaemonDetected();
|
return daemon_->externalDaemonDetected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DaemonController::clearExternalDaemonDetected()
|
||||||
|
{
|
||||||
|
daemon_->clearExternalDaemonDetected();
|
||||||
|
}
|
||||||
|
|
||||||
DaemonController::State DaemonController::state() const
|
DaemonController::State DaemonController::state() const
|
||||||
{
|
{
|
||||||
return daemon_->getState();
|
return daemon_->getState();
|
||||||
@@ -116,6 +121,11 @@ void DaemonController::setZapOnNextStart(bool enabled)
|
|||||||
daemon_->setZapOnNextStart(enabled);
|
daemon_->setZapOnNextStart(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DaemonController::setSalvageOnNextStart(bool enabled)
|
||||||
|
{
|
||||||
|
daemon_->setSalvageOnNextStart(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
bool DaemonController::zapOnNextStart() const
|
bool DaemonController::zapOnNextStart() const
|
||||||
{
|
{
|
||||||
return daemon_->zapOnNextStart();
|
return daemon_->zapOnNextStart();
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ public:
|
|||||||
|
|
||||||
bool isRunning() const;
|
bool isRunning() const;
|
||||||
bool externalDaemonDetected() const;
|
bool externalDaemonDetected() const;
|
||||||
|
void clearExternalDaemonDetected();
|
||||||
State state() const;
|
State state() const;
|
||||||
const std::string& lastError() const;
|
const std::string& lastError() const;
|
||||||
int crashCount() const;
|
int crashCount() const;
|
||||||
@@ -106,6 +107,7 @@ public:
|
|||||||
bool rescanOnNextStart() const;
|
bool rescanOnNextStart() const;
|
||||||
void setZapOnNextStart(bool enabled);
|
void setZapOnNextStart(bool enabled);
|
||||||
bool zapOnNextStart() const;
|
bool zapOnNextStart() const;
|
||||||
|
void setSalvageOnNextStart(bool enabled);
|
||||||
|
|
||||||
static ShutdownDecision evaluateShutdownPolicy(bool hasDaemon,
|
static ShutdownDecision evaluateShutdownPolicy(bool hasDaemon,
|
||||||
bool externalDaemonDetected,
|
bool externalDaemonDetected,
|
||||||
|
|||||||
@@ -386,52 +386,80 @@ static std::string getPortOwnerInfo(int port)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a TCP port is already in use (something is LISTENING)
|
// Check if a TCP port is already in use (something is LISTENING). The daemon binds BOTH 127.0.0.1 (IPv4)
|
||||||
|
// and ::1 (IPv6); during shutdown one can linger after the other releases (and a "Binding RPC on ::1 …
|
||||||
|
// failed" is fatal to a fresh start), so we treat the port as in use if EITHER localhost family has it.
|
||||||
static bool isPortInUse(int port)
|
static bool isPortInUse(int port)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
WSADATA wsa;
|
WSADATA wsa;
|
||||||
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return false;
|
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return false;
|
||||||
|
bool inUse = false;
|
||||||
|
{ // IPv4 127.0.0.1
|
||||||
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||||
if (sock == INVALID_SOCKET) { WSACleanup(); return false; }
|
if (sock != INVALID_SOCKET) {
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr; memset(&addr, 0, sizeof(addr));
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_port = htons(static_cast<u_short>(port));
|
addr.sin_port = htons(static_cast<u_short>(port));
|
||||||
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||||
int result = connect(sock, (struct sockaddr*)&addr, sizeof(addr));
|
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0) inUse = true;
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!inUse) { // IPv6 ::1
|
||||||
|
SOCKET sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
if (sock != INVALID_SOCKET) {
|
||||||
|
struct sockaddr_in6 addr; memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sin6_family = AF_INET6;
|
||||||
|
addr.sin6_port = htons(static_cast<u_short>(port));
|
||||||
|
addr.sin6_addr = in6addr_loopback; // ::1 — avoids inet_pton's _WIN32_WINNT gating on mingw
|
||||||
|
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0) inUse = true;
|
||||||
|
closesocket(sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
WSACleanup();
|
WSACleanup();
|
||||||
return (result == 0);
|
return inUse;
|
||||||
#else
|
#else
|
||||||
// On macOS /proc doesn't exist; on Linux prefer /proc/net/tcp to avoid
|
// On macOS /proc doesn't exist; on Linux prefer /proc/net/tcp{,6} to avoid creating sockets. The
|
||||||
// creating sockets. Fall back to connect() if /proc is unavailable.
|
// parse is family-agnostic: %*X skips the local IP (8 hex for v4, 32 for v6), %X grabs the port.
|
||||||
FILE* fp = fopen("/proc/net/tcp", "r");
|
auto scanProc = [port](const char* path) -> bool {
|
||||||
if (fp) {
|
FILE* fp = fopen(path, "r");
|
||||||
char line[256];
|
if (!fp) return false;
|
||||||
|
char line[512];
|
||||||
unsigned int localPort, state;
|
unsigned int localPort, state;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
while (fgets(line, sizeof(line), fp)) {
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
if (sscanf(line, " %*d: %*X:%X %*X:%*X %X", &localPort, &state) == 2) {
|
if (sscanf(line, " %*d: %*X:%X %*X:%*X %X", &localPort, &state) == 2) {
|
||||||
if (localPort == static_cast<unsigned int>(port) && state == 0x0A) {
|
if (localPort == static_cast<unsigned int>(port) && state == 0x0A) { found = true; break; }
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return found;
|
return found;
|
||||||
|
};
|
||||||
|
if (FILE* probe = fopen("/proc/net/tcp", "r")) { // /proc available → authoritative LISTEN check
|
||||||
|
fclose(probe);
|
||||||
|
return scanProc("/proc/net/tcp") || scanProc("/proc/net/tcp6");
|
||||||
}
|
}
|
||||||
// Fallback (macOS): try to connect
|
// Fallback (macOS): connect() probe on both loopback families.
|
||||||
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
auto connProbe = [port](int family, const char* addr) -> bool {
|
||||||
|
int sock = socket(family, SOCK_STREAM, 0);
|
||||||
if (sock < 0) return false;
|
if (sock < 0) return false;
|
||||||
struct sockaddr_in addr;
|
bool ok = false;
|
||||||
memset(&addr, 0, sizeof(addr));
|
if (family == AF_INET) {
|
||||||
addr.sin_family = AF_INET;
|
struct sockaddr_in a; memset(&a, 0, sizeof(a));
|
||||||
addr.sin_port = htons(static_cast<uint16_t>(port));
|
a.sin_family = AF_INET; a.sin_port = htons(static_cast<uint16_t>(port));
|
||||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||||
int result = connect(sock, (struct sockaddr*)&addr, sizeof(addr));
|
ok = (connect(sock, (struct sockaddr*)&a, sizeof(a)) == 0);
|
||||||
|
} else {
|
||||||
|
struct sockaddr_in6 a; memset(&a, 0, sizeof(a));
|
||||||
|
a.sin6_family = AF_INET6; a.sin6_port = htons(static_cast<uint16_t>(port));
|
||||||
|
inet_pton(AF_INET6, addr, &a.sin6_addr);
|
||||||
|
ok = (connect(sock, (struct sockaddr*)&a, sizeof(a)) == 0);
|
||||||
|
}
|
||||||
close(sock);
|
close(sock);
|
||||||
return (result == 0);
|
return ok;
|
||||||
|
};
|
||||||
|
return connProbe(AF_INET, "127.0.0.1") || connProbe(AF_INET6, "::1");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,9 +524,16 @@ bool EmbeddedDaemon::start(const std::string& binary_path)
|
|||||||
args.push_back("-wallet=" + wallet_file_);
|
args.push_back("-wallet=" + wallet_file_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add wallet-repair flag if requested (one-shot). -zapwallettxes=2 wipes all wallet tx/note
|
// Add wallet-repair flag if requested (one-shot). Precedence: salvage > zap > rescan; each implies a
|
||||||
// records and rebuilds them from the chain; it implies -rescan, so don't also pass -rescan.
|
// rescan in the daemon, so we don't stack them.
|
||||||
if (zap_on_next_start_.exchange(false)) {
|
if (salvage_on_next_start_.exchange(false)) {
|
||||||
|
// -salvagewallet recovers readable keypairs from a corrupt wallet.dat; the daemon then implies -rescan.
|
||||||
|
DEBUG_LOGF("[INFO] Adding -salvagewallet flag to recover a corrupt wallet\n");
|
||||||
|
args.push_back("-salvagewallet");
|
||||||
|
zap_on_next_start_.store(false);
|
||||||
|
rescan_on_next_start_.store(false);
|
||||||
|
} else if (zap_on_next_start_.exchange(false)) {
|
||||||
|
// -zapwallettxes=2 wipes all wallet tx/note records and rebuilds them from the chain (implies -rescan).
|
||||||
DEBUG_LOGF("[INFO] Adding -zapwallettxes=2 flag for wallet repair (zap & rebuild)\n");
|
DEBUG_LOGF("[INFO] Adding -zapwallettxes=2 flag for wallet repair (zap & rebuild)\n");
|
||||||
args.push_back("-zapwallettxes=2");
|
args.push_back("-zapwallettxes=2");
|
||||||
rescan_on_next_start_.store(false); // implied by zap; avoid redundant -rescan
|
rescan_on_next_start_.store(false); // implied by zap; avoid redundant -rescan
|
||||||
@@ -656,17 +691,24 @@ static DWORD findProcessByName(const char* name)
|
|||||||
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
if (snap == INVALID_HANDLE_VALUE) return 0;
|
if (snap == INVALID_HANDLE_VALUE) return 0;
|
||||||
|
|
||||||
PROCESSENTRY32 entry;
|
// Use the explicit WIDE Toolhelp API + a wide compare so this is correct regardless of the UNICODE
|
||||||
|
// macro. (The non-suffixed PROCESSENTRY32/Process32First map to the wide variants when UNICODE is
|
||||||
|
// defined, in which case szExeFile is WCHAR[] and an ANSI _stricmp would compare garbage and NEVER
|
||||||
|
// match — silently making findProcessByName a no-op that returns 0 for a running process.)
|
||||||
|
wchar_t wname[MAX_PATH];
|
||||||
|
if (MultiByteToWideChar(CP_ACP, 0, name, -1, wname, MAX_PATH) == 0) { CloseHandle(snap); return 0; }
|
||||||
|
|
||||||
|
PROCESSENTRY32W entry;
|
||||||
entry.dwSize = sizeof(entry);
|
entry.dwSize = sizeof(entry);
|
||||||
|
|
||||||
DWORD pid = 0;
|
DWORD pid = 0;
|
||||||
if (Process32First(snap, &entry)) {
|
if (Process32FirstW(snap, &entry)) {
|
||||||
do {
|
do {
|
||||||
if (_stricmp(entry.szExeFile, name) == 0) {
|
if (lstrcmpiW(entry.szExeFile, wname) == 0) { // Win32 case-insensitive wide compare
|
||||||
pid = entry.th32ProcessID;
|
pid = entry.th32ProcessID;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (Process32Next(snap, &entry));
|
} while (Process32NextW(snap, &entry));
|
||||||
}
|
}
|
||||||
CloseHandle(snap);
|
CloseHandle(snap);
|
||||||
return pid;
|
return pid;
|
||||||
@@ -1247,5 +1289,28 @@ bool EmbeddedDaemon::tcpPortInUse(int port)
|
|||||||
return isPortInUse(port);
|
return isPortInUse(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EmbeddedDaemon::isDaemonProcessRunning()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return findProcessByName("dragonxd.exe") != 0;
|
||||||
|
#elif defined(__linux__)
|
||||||
|
// Scan /proc for a process whose comm is exactly "dragonxd". Iterate with an error_code so a proc
|
||||||
|
// entry vanishing mid-scan (a process exiting) can't throw.
|
||||||
|
std::error_code ec;
|
||||||
|
fs::directory_iterator it("/proc", ec), end;
|
||||||
|
for (; !ec && it != end; it.increment(ec)) {
|
||||||
|
const std::string pid = it->path().filename().string();
|
||||||
|
if (pid.empty() || pid[0] < '0' || pid[0] > '9') continue; // numeric pid dirs only
|
||||||
|
std::ifstream f((it->path() / "comm").string());
|
||||||
|
std::string comm;
|
||||||
|
if (f && std::getline(f, comm) && comm == "dragonxd") return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
// macOS has no /proc; fall back to the RPC-port probe (best-effort).
|
||||||
|
return isPortInUse(std::atoi(DRAGONX_DEFAULT_RPC_PORT));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace daemon
|
} // namespace daemon
|
||||||
} // namespace dragonx
|
} // namespace dragonx
|
||||||
|
|||||||
@@ -142,6 +142,10 @@ public:
|
|||||||
* When true the wallet should connect to it instead of showing an error.
|
* When true the wallet should connect to it instead of showing an error.
|
||||||
*/
|
*/
|
||||||
bool externalDaemonDetected() const { return external_daemon_detected_; }
|
bool externalDaemonDetected() const { return external_daemon_detected_; }
|
||||||
|
// Clear the adopted-external latch before relaunching our OWN process (e.g. a wallet switch that
|
||||||
|
// stopped the adopted daemon), so the freshly spawned daemon is treated as owned. start() also
|
||||||
|
// clears it on the port-free fall-through, but only if not short-circuited by an early guard.
|
||||||
|
void clearExternalDaemonDetected() { external_daemon_detected_ = false; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set callback for state changes
|
* @brief Set callback for state changes
|
||||||
@@ -197,6 +201,11 @@ public:
|
|||||||
void setZapOnNextStart(bool v) { zap_on_next_start_ = v; }
|
void setZapOnNextStart(bool v) { zap_on_next_start_ = v; }
|
||||||
bool zapOnNextStart() const { return zap_on_next_start_.load(); }
|
bool zapOnNextStart() const { return zap_on_next_start_.load(); }
|
||||||
|
|
||||||
|
// -salvagewallet: attempt to recover keys from a corrupt wallet.dat on startup (implies -rescan in
|
||||||
|
// the daemon). One-shot, consumed on the next start. Used to repair a wallet a switch flagged corrupt.
|
||||||
|
void setSalvageOnNextStart(bool v) { salvage_on_next_start_ = v; }
|
||||||
|
bool salvageOnNextStart() const { return salvage_on_next_start_.load(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief One-shot isolated-datadir override for the NEXT start(): run the daemon against a
|
* @brief One-shot isolated-datadir override for the NEXT start(): run the daemon against a
|
||||||
* different datadir (with its own DRAGONX.conf) plus the given extra args. Used by the
|
* different datadir (with its own DRAGONX.conf) plus the given extra args. Used by the
|
||||||
@@ -217,6 +226,15 @@ public:
|
|||||||
*/
|
*/
|
||||||
void setSkipPortCheck(bool v) { skip_port_check_ = v; }
|
void setSkipPortCheck(bool v) { skip_port_check_ = v; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief True while ANY dragonxd process is running (by process name), regardless of who started it.
|
||||||
|
* Unlike isRpcPortInUse()/isRunning(), this reflects the actual PROCESS still being alive — a
|
||||||
|
* graceful shutdown stops accepting RPC (port reads "free") but keeps the datadir lock until the
|
||||||
|
* process exits, which can take up to ~90s. Use this to know a stopped node has FULLY released
|
||||||
|
* the datadir before starting a replacement. Matches the daemon binary name on all platforms.
|
||||||
|
*/
|
||||||
|
static bool isDaemonProcessRunning();
|
||||||
|
|
||||||
/** @brief Is an arbitrary TCP port currently in use on localhost? (used to pick a free port) */
|
/** @brief Is an arbitrary TCP port currently in use on localhost? (used to pick a free port) */
|
||||||
static bool tcpPortInUse(int port);
|
static bool tcpPortInUse(int port);
|
||||||
|
|
||||||
@@ -261,6 +279,7 @@ private:
|
|||||||
std::atomic<int> crash_count_{0}; // consecutive crash counter
|
std::atomic<int> crash_count_{0}; // consecutive crash counter
|
||||||
std::atomic<bool> rescan_on_next_start_{false}; // -rescan flag for next start
|
std::atomic<bool> rescan_on_next_start_{false}; // -rescan flag for next start
|
||||||
std::atomic<bool> zap_on_next_start_{false}; // -zapwallettxes=2 flag for next start
|
std::atomic<bool> zap_on_next_start_{false}; // -zapwallettxes=2 flag for next start
|
||||||
|
std::atomic<bool> salvage_on_next_start_{false}; // -salvagewallet flag for next start
|
||||||
std::string override_datadir_; // one-shot: -datadir for the next start
|
std::string override_datadir_; // one-shot: -datadir for the next start
|
||||||
std::vector<std::string> override_extra_args_; // one-shot: extra args for the next start
|
std::vector<std::string> override_extra_args_; // one-shot: extra args for the next start
|
||||||
bool skip_port_check_ = false; // isolated instance on a non-default port
|
bool skip_port_check_ = false; // isolated instance on a non-default port
|
||||||
|
|||||||
@@ -110,9 +110,13 @@ SeedWalletResult SeedWalletCreator::create(bool keepDatadir,
|
|||||||
|
|
||||||
// 6. Export the new seed phrase + a fresh shielded receive address (the future sweep target).
|
// 6. Export the new seed phrase + a fresh shielded receive address (the future sweep target).
|
||||||
try {
|
try {
|
||||||
auto m = cli.call("z_exportmnemonic");
|
auto m = cli.callSecret("z_exportmnemonic"); // zero the raw body too (B7)
|
||||||
if (m.contains("mnemonic") && m["mnemonic"].is_string())
|
if (m.contains("mnemonic") && m["mnemonic"].is_string()) {
|
||||||
r.seedPhrase = m["mnemonic"].get<std::string>();
|
// Take our copy, then scrub the json node's own copy so it isn't freed in the clear (B7).
|
||||||
|
auto& mn = m["mnemonic"].get_ref<std::string&>();
|
||||||
|
r.seedPhrase = mn;
|
||||||
|
if (!mn.empty()) sodium_memzero(&mn[0], mn.size());
|
||||||
|
}
|
||||||
r.destAddress = cli.call("z_getnewaddress").get<std::string>();
|
r.destAddress = cli.call("z_getnewaddress").get<std::string>();
|
||||||
r.ok = !r.seedPhrase.empty() && !r.destAddress.empty();
|
r.ok = !r.seedPhrase.empty() && !r.destAddress.empty();
|
||||||
if (!r.ok) r.error = "The isolated node returned an empty seed or address.";
|
if (!r.ok) r.error = "The isolated node returned an empty seed or address.";
|
||||||
@@ -132,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);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Copyright 2024-2026 The Hush Developers
|
// Copyright 2024-2026 The Hush Developers
|
||||||
// Released under the GPLv3
|
// Released under the GPLv3
|
||||||
//
|
//
|
||||||
// xmrig_manager.cpp — Pool mining process management via xmrig-hac.
|
// xmrig_manager.cpp — Pool mining process management via drg-xmrig.
|
||||||
// Spawns xmrig, monitors via HTTP API, tracks hashrate and shares.
|
// Spawns xmrig, monitors via HTTP API, tracks hashrate and shares.
|
||||||
|
|
||||||
#include "xmrig_manager.h"
|
#include "xmrig_manager.h"
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ bool AddressBook::load()
|
|||||||
// Legacy entries (no "scope") migrate to "global" so nothing disappears when
|
// Legacy entries (no "scope") migrate to "global" so nothing disappears when
|
||||||
// multi-wallet scoping lands — a contact you already had stays visible everywhere.
|
// multi-wallet scoping lands — a contact you already had stays visible everywhere.
|
||||||
e.scope = entry.value("scope", "global");
|
e.scope = entry.value("scope", "global");
|
||||||
|
e.avatar = entry.value("avatar", "");
|
||||||
|
|
||||||
if (!e.address.empty()) {
|
if (!e.address.empty()) {
|
||||||
entries_.push_back(e);
|
entries_.push_back(e);
|
||||||
@@ -86,6 +87,7 @@ bool AddressBook::save()
|
|||||||
e["address"] = entry.address;
|
e["address"] = entry.address;
|
||||||
e["notes"] = entry.notes;
|
e["notes"] = entry.notes;
|
||||||
e["scope"] = entry.scope.empty() ? std::string("global") : entry.scope;
|
e["scope"] = entry.scope.empty() ? std::string("global") : entry.scope;
|
||||||
|
if (!entry.avatar.empty()) e["avatar"] = entry.avatar;
|
||||||
j["entries"].push_back(e);
|
j["entries"].push_back(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,6 +144,20 @@ bool AddressBook::removeEntry(size_t index)
|
|||||||
return save();
|
return save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int AddressBook::reattachLegacyScopes(const std::string& scopeId)
|
||||||
|
{
|
||||||
|
if (scopeId.empty()) return 0;
|
||||||
|
int rescoped = 0;
|
||||||
|
for (auto& e : entries_) {
|
||||||
|
if (e.isGlobal()) continue; // global stays global
|
||||||
|
if (e.scope.rfind("w:", 0) == 0) continue; // already a stable scope
|
||||||
|
e.scope = scopeId;
|
||||||
|
++rescoped;
|
||||||
|
}
|
||||||
|
if (rescoped > 0) save();
|
||||||
|
return rescoped;
|
||||||
|
}
|
||||||
|
|
||||||
int AddressBook::findByAddress(const std::string& address) const
|
int AddressBook::findByAddress(const std::string& address) const
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < entries_.size(); i++) {
|
for (size_t i = 0; i < entries_.size(); i++) {
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ struct AddressBookEntry {
|
|||||||
// (shown only when that wallet is the active one). Empty is treated as "global" so legacy
|
// (shown only when that wallet is the active one). Empty is treated as "global" so legacy
|
||||||
// entries — written before multi-wallet scoping — keep showing everywhere.
|
// entries — written before multi-wallet scoping — keep showing everywhere.
|
||||||
std::string scope = "global";
|
std::string scope = "global";
|
||||||
|
// Contact avatar: "" = default type badge (Z/T), "icon:<name>" = a Material wallet-icon,
|
||||||
|
// "img:<path>" = a custom image (copied into <config>/contact-avatars/).
|
||||||
|
std::string avatar;
|
||||||
|
|
||||||
AddressBookEntry() = default;
|
AddressBookEntry() = default;
|
||||||
AddressBookEntry(const std::string& l, const std::string& a, const std::string& n = "",
|
AddressBookEntry(const std::string& l, const std::string& a, const std::string& n = "",
|
||||||
@@ -83,6 +86,15 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool removeEntry(size_t index);
|
bool removeEntry(size_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Re-attach contacts stuck on a legacy (drifting address-hash) scope to a stable wallet id.
|
||||||
|
* Rewrites every non-global entry whose scope is NOT already a stable "w:"-prefixed id to
|
||||||
|
* `scopeId`, and saves once if anything changed. Recovery for contacts orphaned when the
|
||||||
|
* old address-set-hash scope shifted (e.g. after creating a new address).
|
||||||
|
* @return number of entries re-scoped.
|
||||||
|
*/
|
||||||
|
int reattachLegacyScopes(const std::string& scopeId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Find entry by address (any scope). Used for contact-label lookups.
|
* @brief Find entry by address (any scope). Used for contact-label lookups.
|
||||||
* @param address Address to search for
|
* @param address Address to search for
|
||||||
@@ -109,6 +121,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
size_t size() const { return entries_.size(); }
|
size_t size() const { return entries_.size(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief UI-sweep ONLY: replace the in-memory entries WITHOUT persisting to disk, so the sweep can
|
||||||
|
* seed demo contacts and restore the real book without a disk write. Do not use outside the sweep.
|
||||||
|
*/
|
||||||
|
void sweepSetEntries(std::vector<AddressBookEntry> e) { entries_ = std::move(e); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check if empty
|
* @brief Check if empty
|
||||||
*/
|
*/
|
||||||
|
|||||||
44
src/data/candle.h
Normal file
44
src/data/candle.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
// A single OHLC candle — the shared shape for per-exchange candlestick charts. Kept in its own tiny,
|
||||||
|
// dependency-free header so both the parser (data/exchange_candles.h, which pulls in nlohmann/json)
|
||||||
|
// and the model (data/wallet_state.h, included widely) can use it without dragging json everywhere.
|
||||||
|
#include <ctime>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace dragonx {
|
||||||
|
namespace data {
|
||||||
|
|
||||||
|
struct Candle {
|
||||||
|
std::time_t time = 0; // epoch SECONDS (the candle's open/bucket time)
|
||||||
|
double open = 0.0;
|
||||||
|
double high = 0.0;
|
||||||
|
double low = 0.0;
|
||||||
|
double close = 0.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Aggregate fine candles into fixed time buckets (e.g. 5-min -> hourly for the 1D view): open = first
|
||||||
|
// open, high = max high, low = min low, close = last close. Input must be ascending by time.
|
||||||
|
inline std::vector<Candle> bucketOHLC(const std::vector<Candle>& src, long windowSec) {
|
||||||
|
std::vector<Candle> out;
|
||||||
|
if (src.empty() || windowSec <= 0) return out;
|
||||||
|
long curBucket = -1;
|
||||||
|
Candle cur;
|
||||||
|
for (const auto& c : src) {
|
||||||
|
const long b = (long)(c.time / windowSec);
|
||||||
|
if (b != curBucket) {
|
||||||
|
if (curBucket >= 0) out.push_back(cur);
|
||||||
|
curBucket = b;
|
||||||
|
cur = c;
|
||||||
|
cur.time = (std::time_t)(b * windowSec);
|
||||||
|
} else {
|
||||||
|
if (c.high > cur.high) cur.high = c.high;
|
||||||
|
if (c.low < cur.low) cur.low = c.low;
|
||||||
|
cur.close = c.close;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (curBucket >= 0) out.push_back(cur);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace data
|
||||||
|
} // namespace dragonx
|
||||||
117
src/data/exchange_candles.h
Normal file
117
src/data/exchange_candles.h
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#pragma once
|
||||||
|
// Per-exchange OHLC candle adapters. CoinGecko tells us WHICH exchanges list DRGX (via the ticker
|
||||||
|
// `market.identifier`, e.g. "ourbit" / "nonkyc_io") but NOT their API — this is the hand-maintained
|
||||||
|
// mapping from that identifier to each venue's public candle endpoint, so the market chart can show the
|
||||||
|
// SELECTED exchange's real price history instead of CoinGecko's cross-exchange aggregate.
|
||||||
|
//
|
||||||
|
// Header-only + pure (no I/O — the actual HTTP fetch happens in app_network.cpp via util::httpGetString)
|
||||||
|
// so the URL builder + parsers are unit-tested against real captured responses. An unmapped exchange
|
||||||
|
// returns an empty URL, so the caller falls back to the CoinGecko aggregate and nothing regresses.
|
||||||
|
#include <algorithm>
|
||||||
|
#include <ctime>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
#include "candle.h"
|
||||||
|
|
||||||
|
namespace dragonx {
|
||||||
|
namespace data {
|
||||||
|
|
||||||
|
using PricePoint = std::pair<std::time_t, double>; // (epoch SECONDS, close price)
|
||||||
|
|
||||||
|
enum class CandleRange {
|
||||||
|
Intraday, // ~2 days of 5-minute candles — backs the Live / 1H / 1D views
|
||||||
|
Daily, // ~1 year of daily candles — backs the 1W / 1M views
|
||||||
|
};
|
||||||
|
|
||||||
|
// True when we have a candle adapter for this CoinGecko exchange identifier.
|
||||||
|
inline bool hasExchangeCandleAdapter(const std::string& identifier) {
|
||||||
|
return identifier == "ourbit" || identifier == "nonkyc_io";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the venue's candle URL for a pair + range. Returns "" when the exchange isn't mapped.
|
||||||
|
// `now` is epoch seconds, passed in (no hidden clock reads) so URLs are deterministic in tests.
|
||||||
|
inline std::string buildExchangeCandleUrl(const std::string& identifier, const std::string& base,
|
||||||
|
const std::string& quote, CandleRange range, std::time_t now) {
|
||||||
|
if (identifier == "ourbit") {
|
||||||
|
// Ourbit = MEXC-style /api/v3/klines: symbol=BASEQUOTE, interval=5m|1d, limit.
|
||||||
|
const char* iv = (range == CandleRange::Intraday) ? "5m" : "1d";
|
||||||
|
const int limit = (range == CandleRange::Intraday) ? 576 : 365;
|
||||||
|
return "https://api.ourbit.com/api/v3/klines?symbol=" + base + quote +
|
||||||
|
"&interval=" + iv + "&limit=" + std::to_string(limit);
|
||||||
|
}
|
||||||
|
if (identifier == "nonkyc_io") {
|
||||||
|
// NonKYC = TradingView-UDF candles: symbol=BASE_QUOTE, resolution in minutes, from/to seconds.
|
||||||
|
const char* res = (range == CandleRange::Intraday) ? "5" : "1440";
|
||||||
|
const std::time_t span = (range == CandleRange::Intraday) ? (std::time_t)2 * 24 * 3600
|
||||||
|
: (std::time_t)365 * 24 * 3600;
|
||||||
|
return "https://api.nonkyc.io/api/v2/market/candles?symbol=" + base + "_" + quote +
|
||||||
|
"&resolution=" + res + "&from=" + std::to_string(now - span) + "&to=" + std::to_string(now);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
// Read a JSON value that may be a number or a numeric string (Ourbit sends prices as strings).
|
||||||
|
inline double jnum(const nlohmann::json& v) {
|
||||||
|
if (v.is_number()) return v.get<double>();
|
||||||
|
if (v.is_string()) { try { return std::stod(v.get<std::string>()); } catch (...) { return 0.0; } }
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
// Parse the venue's candle response into ascending OHLC candles. Empty on failure.
|
||||||
|
inline std::vector<Candle> parseExchangeOHLC(const std::string& identifier, const std::string& body) {
|
||||||
|
std::vector<Candle> out;
|
||||||
|
if (body.empty()) return out;
|
||||||
|
try {
|
||||||
|
const nlohmann::json j = nlohmann::json::parse(body);
|
||||||
|
if (identifier == "ourbit") {
|
||||||
|
// [[openTime_ms, open, high, low, close, volume, closeTime, quoteVol], ...]
|
||||||
|
if (!j.is_array()) return out;
|
||||||
|
out.reserve(j.size());
|
||||||
|
for (const auto& k : j) {
|
||||||
|
if (!k.is_array() || k.size() < 5) continue;
|
||||||
|
Candle c;
|
||||||
|
c.time = (std::time_t)(detail::jnum(k[0]) / 1000.0);
|
||||||
|
c.open = detail::jnum(k[1]);
|
||||||
|
c.high = detail::jnum(k[2]);
|
||||||
|
c.low = detail::jnum(k[3]);
|
||||||
|
c.close = detail::jnum(k[4]);
|
||||||
|
if (c.time > 0 && c.close > 0) out.push_back(c);
|
||||||
|
}
|
||||||
|
} else if (identifier == "nonkyc_io") {
|
||||||
|
// {"bars":[{"time":ms,"open":..,"high":..,"low":..,"close":..,"volume":..}, ...]}
|
||||||
|
if (!j.contains("bars") || !j["bars"].is_array()) return out;
|
||||||
|
out.reserve(j["bars"].size());
|
||||||
|
for (const auto& b : j["bars"]) {
|
||||||
|
if (!b.is_object() || !b.contains("time") || !b.contains("close")) continue;
|
||||||
|
Candle c;
|
||||||
|
c.time = (std::time_t)(detail::jnum(b["time"]) / 1000.0);
|
||||||
|
c.close = detail::jnum(b["close"]);
|
||||||
|
c.open = b.contains("open") ? detail::jnum(b["open"]) : c.close;
|
||||||
|
c.high = b.contains("high") ? detail::jnum(b["high"]) : c.close;
|
||||||
|
c.low = b.contains("low") ? detail::jnum(b["low"]) : c.close;
|
||||||
|
if (c.time > 0 && c.close > 0) out.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::sort(out.begin(), out.end(), [](const Candle& a, const Candle& b) { return a.time < b.time; });
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close-only convenience over parseExchangeOHLC (backs the line chart + change-% fallback). bucketOHLC
|
||||||
|
// for candlestick resampling lives in candle.h (dependency-free, reused by market_series.h).
|
||||||
|
inline std::vector<PricePoint> parseExchangeCandles(const std::string& identifier, const std::string& body) {
|
||||||
|
std::vector<PricePoint> out;
|
||||||
|
for (const auto& c : parseExchangeOHLC(identifier, body)) out.emplace_back(c.time, c.close);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace data
|
||||||
|
} // namespace dragonx
|
||||||
@@ -22,14 +22,14 @@ const std::vector<ExchangeInfo>& getExchangeRegistry()
|
|||||||
"Nonkyc.io",
|
"Nonkyc.io",
|
||||||
"https://nonkyc.io",
|
"https://nonkyc.io",
|
||||||
{
|
{
|
||||||
{"DRGX", "USDT", "DRGX/USDT", "https://nonkyc.io/market/DRGX_USDT"},
|
{"DRGX", "USDT", "DRGX/USDT", "https://nonkyc.io/market/DRGX_USDT", "nonkyc_io"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"OurBit",
|
"OurBit",
|
||||||
"https://www.ourbit.com",
|
"https://www.ourbit.com",
|
||||||
{
|
{
|
||||||
{"DRGX", "USDT", "DRGX/USDT", "https://www.ourbit.com/exchange/DRGX_USDT"},
|
{"DRGX", "USDT", "DRGX/USDT", "https://www.ourbit.com/exchange/DRGX_USDT", "ourbit"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -59,11 +59,19 @@ std::vector<ExchangeInfo> parseCoinGeckoTickers(const std::string& body)
|
|||||||
if (!t.is_object()) continue;
|
if (!t.is_object()) continue;
|
||||||
const std::string base = t.value("base", std::string{});
|
const std::string base = t.value("base", std::string{});
|
||||||
const std::string target = t.value("target", std::string{});
|
const std::string target = t.value("target", std::string{});
|
||||||
std::string market;
|
std::string market, identifier;
|
||||||
if (t.contains("market") && t["market"].is_object())
|
if (t.contains("market") && t["market"].is_object()) {
|
||||||
market = t["market"].value("name", std::string{});
|
market = t["market"].value("name", std::string{});
|
||||||
|
identifier = t["market"].value("identifier", std::string{}); // keys the per-exchange candle adapter
|
||||||
|
}
|
||||||
const std::string tradeUrl = t.value("trade_url", std::string{});
|
const std::string tradeUrl = t.value("trade_url", std::string{});
|
||||||
if (base.empty() || target.empty() || market.empty()) continue;
|
if (base.empty() || target.empty() || market.empty()) continue;
|
||||||
|
double lastUsd = 0.0; // this venue's current USD price + 24h volume — differ per exchange
|
||||||
|
if (t.contains("converted_last") && t["converted_last"].is_object())
|
||||||
|
lastUsd = t["converted_last"].value("usd", 0.0);
|
||||||
|
double volumeUsd = 0.0;
|
||||||
|
if (t.contains("converted_volume") && t["converted_volume"].is_object())
|
||||||
|
volumeUsd = t["converted_volume"].value("usd", 0.0);
|
||||||
|
|
||||||
auto it = byName.find(market);
|
auto it = byName.find(market);
|
||||||
if (it == byName.end()) {
|
if (it == byName.end()) {
|
||||||
@@ -71,7 +79,7 @@ std::vector<ExchangeInfo> parseCoinGeckoTickers(const std::string& body)
|
|||||||
exchanges.push_back(ExchangeInfo{market, originOf(tradeUrl), {}});
|
exchanges.push_back(ExchangeInfo{market, originOf(tradeUrl), {}});
|
||||||
it = byName.find(market);
|
it = byName.find(market);
|
||||||
}
|
}
|
||||||
ExchangePair pair{base, target, base + "/" + target, tradeUrl};
|
ExchangePair pair{base, target, base + "/" + target, tradeUrl, identifier, lastUsd, volumeUsd};
|
||||||
// Skip duplicate pairs on the same exchange.
|
// Skip duplicate pairs on the same exchange.
|
||||||
bool dup = false;
|
bool dup = false;
|
||||||
for (const auto& p : exchanges[it->second].pairs)
|
for (const auto& p : exchanges[it->second].pairs)
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ struct ExchangePair {
|
|||||||
std::string quote; ///< e.g. "BTC"
|
std::string quote; ///< e.g. "BTC"
|
||||||
std::string displayName; ///< e.g. "DRGX/BTC"
|
std::string displayName; ///< e.g. "DRGX/BTC"
|
||||||
std::string tradeUrl; ///< Link to the exchange pair page
|
std::string tradeUrl; ///< Link to the exchange pair page
|
||||||
|
std::string identifier; ///< CoinGecko exchange id (e.g. "ourbit", "nonkyc_io") — keys the candle adapter
|
||||||
|
double lastUsd = 0.0; ///< This venue's current price in USD (CoinGecko converted_last.usd); 0 if unknown
|
||||||
|
double volumeUsd = 0.0; ///< This venue's 24h volume in USD (CoinGecko converted_volume.usd); 0 if unknown
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -81,11 +81,15 @@ inline std::vector<std::pair<std::time_t, double>> chartSeries(const MarketInfo&
|
|||||||
for (const auto& s : src) if (s.first >= cutoff) out.push_back(s);
|
for (const auto& s : src) if (s.first >= cutoff) out.push_back(s);
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
|
// Draw the SELECTED exchange's own candles when active (data/exchange_candles.h), else the CoinGecko
|
||||||
|
// cross-exchange aggregate. Only the main chart switches source; portfolio sparklines stay aggregate.
|
||||||
|
const auto& intraday = m.exchange_chart_active ? m.exchange_chart_intraday : m.price_chart_intraday;
|
||||||
|
const auto& daily = m.exchange_chart_active ? m.exchange_chart_daily : m.price_chart_daily;
|
||||||
switch (interval) {
|
switch (interval) {
|
||||||
case 1: { auto v = lastWindow(m.price_chart_intraday, 3600); if (v.size() >= 2) return v; break; } // 1H
|
case 1: { auto v = lastWindow(intraday, 3600); if (v.size() >= 2) return v; break; } // 1H
|
||||||
case 2: { auto v = lastWindow(m.price_chart_intraday, kDay); if (v.size() >= 2) return v; break; } // 1D
|
case 2: { auto v = lastWindow(intraday, kDay); if (v.size() >= 2) return v; break; } // 1D
|
||||||
case 3: { auto v = lastWindow(m.price_chart_daily, 7 * kDay); if (v.size() >= 2) return v; break; } // 1W
|
case 3: { auto v = lastWindow(daily, 7 * kDay); if (v.size() >= 2) return v; break; } // 1W
|
||||||
case 4: { auto v = lastWindow(m.price_chart_daily, 30 * kDay); if (v.size() >= 2) return v; break; } // 1M
|
case 4: { auto v = lastWindow(daily, 30 * kDay); if (v.size() >= 2) return v; break; } // 1M
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
std::vector<std::pair<std::time_t, double>> out;
|
std::vector<std::pair<std::time_t, double>> out;
|
||||||
@@ -95,5 +99,28 @@ inline std::vector<std::pair<std::time_t, double>> chartSeries(const MarketInfo&
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OHLC candles for the main chart at the selected RANGE — only when the per-exchange series is active
|
||||||
|
// (the CoinGecko aggregate is close-only, so this returns empty and the chart draws a line). The 1D
|
||||||
|
// view buckets the 5-minute intraday to hourly so it isn't ~288 hair-thin candles; other ranges use
|
||||||
|
// the raw candles. Empty for the Live range (uses the in-session line).
|
||||||
|
inline std::vector<Candle> chartCandles(const MarketInfo& m, int interval, std::time_t now)
|
||||||
|
{
|
||||||
|
if (!m.exchange_chart_active) return {};
|
||||||
|
const long kDay = 86400;
|
||||||
|
auto window = [now](const std::vector<Candle>& src, long rangeSec) {
|
||||||
|
std::vector<Candle> out;
|
||||||
|
const std::time_t cutoff = now - (std::time_t)rangeSec;
|
||||||
|
for (const auto& c : src) if (c.time >= cutoff) out.push_back(c);
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
switch (interval) {
|
||||||
|
case 1: return window(m.exchange_ohlc_intraday, 3600); // 1H: 5-min candles (~12)
|
||||||
|
case 2: return bucketOHLC(window(m.exchange_ohlc_intraday, kDay), 3600); // 1D: hourly candles (~24)
|
||||||
|
case 3: return window(m.exchange_ohlc_daily, 7 * kDay); // 1W: daily (~7)
|
||||||
|
case 4: return window(m.exchange_ohlc_daily, 30 * kDay); // 1M: daily (~30)
|
||||||
|
default: return {}; // Live -> line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace data
|
} // namespace data
|
||||||
} // namespace dragonx
|
} // namespace dragonx
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ bool sameEntry(const WalletIndexEntry& a, const WalletIndexEntry& b)
|
|||||||
return a.fileName == b.fileName
|
return a.fileName == b.fileName
|
||||||
&& a.displayName == b.displayName
|
&& a.displayName == b.displayName
|
||||||
&& a.walletIdentityHash == b.walletIdentityHash
|
&& a.walletIdentityHash == b.walletIdentityHash
|
||||||
|
&& a.scopeId == b.scopeId
|
||||||
&& a.cachedBalance == b.cachedBalance
|
&& a.cachedBalance == b.cachedBalance
|
||||||
&& a.cachedAddressCount == b.cachedAddressCount
|
&& a.cachedAddressCount == b.cachedAddressCount
|
||||||
&& a.lastOpenedEpoch == b.lastOpenedEpoch
|
&& a.lastOpenedEpoch == b.lastOpenedEpoch
|
||||||
@@ -61,6 +62,7 @@ bool WalletIndex::load()
|
|||||||
if (w.fileName.empty()) continue;
|
if (w.fileName.empty()) continue;
|
||||||
w.displayName = e.value("name", w.fileName);
|
w.displayName = e.value("name", w.fileName);
|
||||||
w.walletIdentityHash = e.value("identity", "");
|
w.walletIdentityHash = e.value("identity", "");
|
||||||
|
w.scopeId = e.value("scopeId", "");
|
||||||
w.cachedBalance = e.value("balance", -1.0);
|
w.cachedBalance = e.value("balance", -1.0);
|
||||||
w.cachedAddressCount = e.value("addresses", (long long)-1);
|
w.cachedAddressCount = e.value("addresses", (long long)-1);
|
||||||
w.lastOpenedEpoch = e.value("lastOpened", (long long)0);
|
w.lastOpenedEpoch = e.value("lastOpened", (long long)0);
|
||||||
@@ -96,6 +98,7 @@ bool WalletIndex::save()
|
|||||||
e["file"] = w.fileName;
|
e["file"] = w.fileName;
|
||||||
e["name"] = w.displayName;
|
e["name"] = w.displayName;
|
||||||
e["identity"] = w.walletIdentityHash;
|
e["identity"] = w.walletIdentityHash;
|
||||||
|
e["scopeId"] = w.scopeId;
|
||||||
e["balance"] = w.cachedBalance;
|
e["balance"] = w.cachedBalance;
|
||||||
e["addresses"] = w.cachedAddressCount;
|
e["addresses"] = w.cachedAddressCount;
|
||||||
e["lastOpened"] = w.lastOpenedEpoch;
|
e["lastOpened"] = w.lastOpenedEpoch;
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ struct WalletIndexEntry {
|
|||||||
std::string fileName; // plain wallet filename in the datadir (e.g. "wallet.dat")
|
std::string fileName; // plain wallet filename in the datadir (e.g. "wallet.dat")
|
||||||
std::string displayName; // user-facing name (defaults to fileName)
|
std::string displayName; // user-facing name (defaults to fileName)
|
||||||
std::string walletIdentityHash; // address-derived identity of the last load; "" = unknown
|
std::string walletIdentityHash; // address-derived identity of the last load; "" = unknown
|
||||||
|
std::string scopeId; // stable per-wallet id ("w:"+hex) for scoping contacts etc.;
|
||||||
|
// generated once, never recomputed from the (mutable) address set
|
||||||
double cachedBalance = -1.0; // last-known total balance; < 0 = unknown (never opened)
|
double cachedBalance = -1.0; // last-known total balance; < 0 = unknown (never opened)
|
||||||
long long cachedAddressCount = -1;// < 0 = unknown
|
long long cachedAddressCount = -1;// < 0 = unknown
|
||||||
long long lastOpenedEpoch = 0; // unix seconds of last open; 0 = never opened
|
long long lastOpenedEpoch = 0; // unix seconds of last open; 0 = never opened
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Released under the GPLv3
|
// Released under the GPLv3
|
||||||
|
|
||||||
#include "wallet_state.h"
|
#include "wallet_state.h"
|
||||||
|
#include "../util/text_format.h" // util::formatClockDateTime (app-wide 24h/12h clock)
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -44,13 +45,7 @@ int bestSpendableAddressIndex(const std::vector<AddressInfo>& addresses)
|
|||||||
std::string TransactionInfo::getTimeString() const
|
std::string TransactionInfo::getTimeString() const
|
||||||
{
|
{
|
||||||
if (timestamp == 0) return "Unknown";
|
if (timestamp == 0) return "Unknown";
|
||||||
|
return util::formatClockDateTime(timestamp);
|
||||||
std::time_t t = static_cast<std::time_t>(timestamp);
|
|
||||||
std::tm* tm = std::localtime(&t);
|
|
||||||
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << std::put_time(tm, "%Y-%m-%d %H:%M");
|
|
||||||
return ss.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TransactionInfo::getTypeDisplay() const
|
std::string TransactionInfo::getTypeDisplay() const
|
||||||
@@ -77,13 +72,7 @@ std::string PeerInfo::getConnectionTime() const
|
|||||||
std::string BannedPeer::getBannedUntilString() const
|
std::string BannedPeer::getBannedUntilString() const
|
||||||
{
|
{
|
||||||
if (banned_until == 0) return "Never";
|
if (banned_until == 0) return "Never";
|
||||||
|
return util::formatClockDateTime(banned_until);
|
||||||
std::time_t t = static_cast<std::time_t>(banned_until);
|
|
||||||
std::tm* tm = std::localtime(&t);
|
|
||||||
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << std::put_time(tm, "%Y-%m-%d %H:%M");
|
|
||||||
return ss.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dragonx
|
} // namespace dragonx
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "exchange_info.h"
|
#include "exchange_info.h"
|
||||||
|
#include "candle.h"
|
||||||
|
|
||||||
namespace dragonx {
|
namespace dragonx {
|
||||||
|
|
||||||
@@ -175,6 +176,18 @@ struct MarketInfo {
|
|||||||
std::chrono::steady_clock::time_point chart_last_fetch_time{};
|
std::chrono::steady_clock::time_point chart_last_fetch_time{};
|
||||||
bool chart_loaded = false;
|
bool chart_loaded = false;
|
||||||
|
|
||||||
|
// Per-EXCHANGE candle series for the SELECTED pair, fetched from that venue's own API (see
|
||||||
|
// data/exchange_candles.h). When exchange_chart_active is true the Market chart draws these instead
|
||||||
|
// of the CoinGecko cross-exchange aggregate above; it's set false while switching pairs or when the
|
||||||
|
// selected venue has no adapter / its fetch failed, so the chart gracefully falls back to aggregate.
|
||||||
|
std::vector<std::pair<std::time_t, double>> exchange_chart_intraday;
|
||||||
|
std::vector<std::pair<std::time_t, double>> exchange_chart_daily;
|
||||||
|
bool exchange_chart_active = false;
|
||||||
|
// Full OHLC for the same per-exchange candles, backing the candlestick rendering (the aggregate
|
||||||
|
// CoinGecko series is close-only, so it stays a line). Parallel to exchange_chart_* above.
|
||||||
|
std::vector<data::Candle> exchange_ohlc_intraday;
|
||||||
|
std::vector<data::Candle> exchange_ohlc_daily;
|
||||||
|
|
||||||
// Exchanges/pairs fetched live from CoinGecko (empty until fetched; the Market tab
|
// Exchanges/pairs fetched live from CoinGecko (empty until fetched; the Market tab
|
||||||
// falls back to data::getExchangeRegistry() while empty).
|
// falls back to data::getExchangeRegistry() while empty).
|
||||||
std::vector<data::ExchangeInfo> exchanges;
|
std::vector<data::ExchangeInfo> exchanges;
|
||||||
|
|||||||
@@ -15,3 +15,5 @@ INCBIN(ubuntu_mono, "@CMAKE_SOURCE_DIR@/res/fonts/UbuntuMono-R.ttf");
|
|||||||
INCBIN(material_icons, "@CMAKE_SOURCE_DIR@/res/fonts/MaterialIcons-Regular.ttf");
|
INCBIN(material_icons, "@CMAKE_SOURCE_DIR@/res/fonts/MaterialIcons-Regular.ttf");
|
||||||
INCBIN(mdi_pickaxe_subset, "@CMAKE_SOURCE_DIR@/res/fonts/MaterialDesignIcons-Pickaxe-Subset.ttf");
|
INCBIN(mdi_pickaxe_subset, "@CMAKE_SOURCE_DIR@/res/fonts/MaterialDesignIcons-Pickaxe-Subset.ttf");
|
||||||
INCBIN(noto_cjk_subset, "@CMAKE_SOURCE_DIR@/res/fonts/NotoSansCJK-Subset.ttf");
|
INCBIN(noto_cjk_subset, "@CMAKE_SOURCE_DIR@/res/fonts/NotoSansCJK-Subset.ttf");
|
||||||
|
INCBIN(noto_emoji_subset, "@CMAKE_SOURCE_DIR@/res/fonts/NotoEmoji-Subset.ttf");
|
||||||
|
INCBIN(twemoji_color, "@CMAKE_SOURCE_DIR@/res/fonts/TwemojiMozilla-Color.ttf");
|
||||||
|
|||||||
@@ -35,4 +35,11 @@ extern "C" {
|
|||||||
|
|
||||||
extern const unsigned char g_noto_cjk_subset_data[];
|
extern const unsigned char g_noto_cjk_subset_data[];
|
||||||
extern const unsigned int g_noto_cjk_subset_size;
|
extern const unsigned int g_noto_cjk_subset_size;
|
||||||
|
|
||||||
|
extern const unsigned char g_noto_emoji_subset_data[];
|
||||||
|
extern const unsigned int g_noto_emoji_subset_size;
|
||||||
|
|
||||||
|
// Twemoji COLR/CPAL color-emoji font (used only when color emoji is enabled + FreeType is available).
|
||||||
|
extern const unsigned char g_twemoji_color_data[];
|
||||||
|
extern const unsigned int g_twemoji_color_size;
|
||||||
}
|
}
|
||||||
|
|||||||
39
src/embedded/logo_dragonx_svg.h
Normal file
39
src/embedded/logo_dragonx_svg.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// DragonX Wallet - ImGui Edition
|
||||||
|
// Copyright 2024-2026 The Hush Developers
|
||||||
|
// Released under the GPLv3
|
||||||
|
//
|
||||||
|
// Embedded source of the DragonX mark (res/img/logos/logo_dragonx.svg). Kept as a string so the logo
|
||||||
|
// is available in every build (dev + portable single-file) with no resource-pipeline or file dependency.
|
||||||
|
// It is rasterized + recolored per theme at runtime (see util/svg_texture.*). Two fills: the crimson
|
||||||
|
// body (.cls-2 #d82652) becomes the theme accent; the white detail (.cls-1 #fff) becomes the light tone.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Global `embedded` namespace to match the generated embedded resources (embedded::ui_toml_data, etc.).
|
||||||
|
namespace embedded {
|
||||||
|
|
||||||
|
inline constexpr const char* kLogoDragonXSvg = R"SVG(<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-2 {
|
||||||
|
fill: #d82652;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="cls-2" d="M103.98,128s-6.29-24.7-18.73-34.43c-8.53-8.03-15.63-16.49-21.25-24.16-5.62,7.68-12.72,16.17-21.25,24.16-12.4,9.74-18.73,34.43-18.73,34.43-2.38-24.34,7.85-35.82,12.87-41.29,7.82-8.5,15.31-16.56,21.75-25.02-7.64-11.44-11.41-19.72-11.41-19.72-.89-3.27-3.84-6.64-3.84-6.64,6.08-8.1-1.6-16.98-1.6-16.98,5.79-5.62,6.71-10.02,8.32-18.34-1.96,22.35,4.02,39.09,13.93,54.12,9.84-15.03,15.81-31.77,13.86-54.12,1.6,8.35,2.52,12.72,8.32,18.34,0,0-7.68,8.88-1.6,16.98,0,0-2.95,3.38-3.84,6.64,0,0-3.77,8.28-11.37,19.72,6.43,8.45,13.97,16.56,21.75,25.02,4.97,5.47,15.21,16.95,12.83,41.29h0Z"/>
|
||||||
|
<g>
|
||||||
|
<path class="cls-1" d="M55.33,61.62c-3.55,4.55-7.39,8.99-11.44,13.47-5.29-4.48-11.23-5.33-11.23-5.33,22.92-7.82,2.81-15.17.28-16.31C9.28,42.78,9.1,14.78,9.1,14.78c11.51,34.15,36.42,32.3,36.42,32.3.35-.21.67-.46.92-.71,1.64,3.27,4.58,8.67,8.88,15.24h0Z"/>
|
||||||
|
<g>
|
||||||
|
<path class="cls-1" d="M68.62,40.41c-1.35,2.98-2.91,5.83-4.62,8.63-1.71-2.81-3.23-5.69-4.62-8.63,1.74-3.45,4.62-20.58,4.62-20.58,0,0,2.88,17.13,4.62,20.58Z"/>
|
||||||
|
<path class="cls-1" d="M76.01,97.93l-3.48,2.34s-.1-4.44-3.52-1.84c-.42.32-2.38,2.21-.03,4.27,0,0-4.05,4.08-4.97,8.21-.92-4.12-4.97-8.21-4.97-8.21,2.34-2.06.39-3.95-.03-4.27-3.41-2.59-3.52,1.84-3.52,1.84l-3.48-2.34c.28-3.55.1-6.68-.46-9.42,4.69-4.94,8.85-9.88,12.47-14.61,3.66,4.72,7.78,9.67,12.47,14.61-.57,2.74-.75,5.86-.46,9.42Z"/>
|
||||||
|
<path class="cls-1" d="M95.34,69.76s-5.94.85-11.23,5.33c-4.02-4.48-7.89-8.92-11.44-13.47,4.3-6.57,7.25-11.98,8.88-15.24.25.25.57.5.92.71,0,0,24.91,1.84,36.42-32.3,0,0-.18,28-23.84,38.66-2.52,1.14-22.64,8.5.28,16.31h0Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>)SVG";
|
||||||
|
|
||||||
|
} // namespace embedded
|
||||||
22
src/main.cpp
22
src/main.cpp
@@ -9,6 +9,7 @@
|
|||||||
#include "ui/schema/ui_schema.h"
|
#include "ui/schema/ui_schema.h"
|
||||||
#include "ui/effects/low_spec.h"
|
#include "ui/effects/low_spec.h"
|
||||||
#include "ui/notifications.h"
|
#include "ui/notifications.h"
|
||||||
|
#include "ui/windows/contacts_tab.h"
|
||||||
#include "ui/theme.h"
|
#include "ui/theme.h"
|
||||||
#include "ui/material/color_theme.h"
|
#include "ui/material/color_theme.h"
|
||||||
#include "ui/material/typography.h"
|
#include "ui/material/typography.h"
|
||||||
@@ -438,8 +439,16 @@ static bool InitImGui(SDL_Window* window, SDL_GLContext gl_context);
|
|||||||
static void Shutdown(SDL_Window* window, SDL_GLContext gl_context);
|
static void Shutdown(SDL_Window* window, SDL_GLContext gl_context);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Global single instance lock
|
// Global single instance lock. Keyed PER VARIANT so the full node and Lite can run side by side —
|
||||||
|
// their config dirs are already separate (DRAGONX_APP_NAME), so only this lock kept them apart.
|
||||||
|
// NB: DRAGONX_LITE_BUILD is ALWAYS defined (0 for the full node, 1 for Lite) via $<BOOL:...>, so this
|
||||||
|
// must be #if (value), not #ifdef (existence) — #ifdef is true for both and made the full node grab
|
||||||
|
// the Lite lock.
|
||||||
|
#if DRAGONX_LITE_BUILD
|
||||||
|
static dragonx::util::SingleInstance g_single_instance("obsidiandragonlite");
|
||||||
|
#else
|
||||||
static dragonx::util::SingleInstance g_single_instance("obsidiandragon");
|
static dragonx::util::SingleInstance g_single_instance("obsidiandragon");
|
||||||
|
#endif
|
||||||
|
|
||||||
// Check for payment URI in command line args
|
// Check for payment URI in command line args
|
||||||
static std::string findPaymentURI(int argc, char* argv[])
|
static std::string findPaymentURI(int argc, char* argv[])
|
||||||
@@ -763,11 +772,12 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
// Check for existing instance
|
// Check for existing instance
|
||||||
if (!g_single_instance.tryLock()) {
|
if (!g_single_instance.tryLock()) {
|
||||||
fprintf(stderr, "Another instance of ObsidianDragon is already running.\n");
|
fprintf(stderr, "Another instance of %s is already running.\n", DRAGONX_APP_NAME);
|
||||||
DEBUG_LOGF("Please close the existing instance first.\n");
|
DEBUG_LOGF("Please close the existing instance first.\n");
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
MessageBoxW(nullptr, L"Another instance of ObsidianDragon is already running.\nPlease close it first.",
|
const std::string msg = std::string("Another instance of ") + DRAGONX_APP_NAME +
|
||||||
L"ObsidianDragon", MB_OK | MB_ICONINFORMATION);
|
" is already running.\nPlease close it first.";
|
||||||
|
MessageBoxA(nullptr, msg.c_str(), DRAGONX_APP_NAME, MB_OK | MB_ICONINFORMATION);
|
||||||
#endif
|
#endif
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -1965,12 +1975,14 @@ int main(int argc, char* argv[])
|
|||||||
bool backdropNeedsFrames = (backdrop_active || app.getGradientTexture() != 0)
|
bool backdropNeedsFrames = (backdrop_active || app.getGradientTexture() != 0)
|
||||||
&& !opaqueBackground;
|
&& !opaqueBackground;
|
||||||
bool animating = app.isShuttingDown()
|
bool animating = app.isShuttingDown()
|
||||||
|
|| app.isWalletSwitchInProgress()
|
||||||
|| backdropNeedsFrames
|
|| backdropNeedsFrames
|
||||||
|| app.hasTransactionSendProgress()
|
|| app.hasTransactionSendProgress()
|
||||||
|| app.isTransactionRefreshInProgress()
|
|| app.isTransactionRefreshInProgress()
|
||||||
|| dragonx::ui::effects::ThemeEffects::instance().hasActiveAnimation()
|
|| dragonx::ui::effects::ThemeEffects::instance().hasActiveAnimation()
|
||||||
|| dragonx::ui::Notifications::instance().hasActive()
|
|| dragonx::ui::Notifications::instance().hasActive()
|
||||||
|| dragonx::ui::material::SmoothScrollAnimating();
|
|| dragonx::ui::material::SmoothScrollAnimating()
|
||||||
|
|| dragonx::ui::ConsumeContactsAvatarAnimation();
|
||||||
// If nothing is happening, allow the next iteration to idle
|
// If nothing is happening, allow the next iteration to idle
|
||||||
needsRedraw = uiActive || animating;
|
needsRedraw = uiActive || animating;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,19 @@ namespace rpc {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// 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()) {
|
||||||
|
auto& s = j.template get_ref<std::string&>();
|
||||||
|
if (!s.empty()) sodium_memzero(&s[0], s.size());
|
||||||
|
} else if (j.is_object() || j.is_array()) {
|
||||||
|
for (auto& el : j) scrubJsonSecrets(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::mutex g_trace_mutex;
|
std::mutex g_trace_mutex;
|
||||||
RPCClient::TraceCallback g_trace_callback;
|
RPCClient::TraceCallback g_trace_callback;
|
||||||
std::atomic_bool g_trace_enabled{false};
|
std::atomic_bool g_trace_enabled{false};
|
||||||
@@ -85,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;
|
||||||
}
|
}
|
||||||
@@ -191,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
|
||||||
@@ -317,7 +338,7 @@ std::string RPCClient::performCall(const std::string& method, const json& params
|
|||||||
return response_data;
|
return response_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
json RPCClient::parseRpcResult(long httpCode, const std::string& body)
|
json RPCClient::parseRpcResult(long httpCode, const std::string& body, bool scrubSource)
|
||||||
{
|
{
|
||||||
// Bitcoin/Hush RPC returns HTTP 500 for application-level errors
|
// Bitcoin/Hush RPC returns HTTP 500 for application-level errors
|
||||||
// (insufficient funds, bad params, etc.) with a valid JSON body.
|
// (insufficient funds, bad params, etc.) with a valid JSON body.
|
||||||
@@ -355,7 +376,9 @@ json RPCClient::parseRpcResult(long httpCode, const std::string& body)
|
|||||||
throw RpcError(errCode, "RPC error: " + err_msg);
|
throw RpcError(errCode, "RPC error: " + err_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response["result"];
|
json result = response["result"]; // a COPY of the subobject (operator[] yields an lvalue ref)
|
||||||
|
if (scrubSource) scrubJsonSecrets(response); // zero the discarded tree's secret before it frees (B7)
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
json RPCClient::call(const std::string& method, const json& params)
|
json RPCClient::call(const std::string& method, const json& params)
|
||||||
@@ -370,6 +393,56 @@ json RPCClient::call(const std::string& method, const json& params)
|
|||||||
return parseRpcResult(http_code, response_data);
|
return parseRpcResult(http_code, response_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
json RPCClient::callSecret(const std::string& method, const json& params)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lk(curl_mutex_);
|
||||||
|
if (!impl_->curl) {
|
||||||
|
throw std::runtime_error("Not connected");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero the raw response body whether parsing succeeds or throws — it holds the secret in the
|
||||||
|
// clear (the curl write buffer performCall returns), and would otherwise be freed un-wiped (B7).
|
||||||
|
long http_code = 0;
|
||||||
|
std::string response_data = performCall(method, params, http_code);
|
||||||
|
try {
|
||||||
|
json result = parseRpcResult(http_code, response_data, /*scrubSource=*/true);
|
||||||
|
if (!response_data.empty()) sodium_memzero(&response_data[0], response_data.size());
|
||||||
|
return result;
|
||||||
|
} catch (...) {
|
||||||
|
if (!response_data.empty()) sodium_memzero(&response_data[0], response_data.size());
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RPCClient::callSecretString(const std::string& method, const json& params)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lk(curl_mutex_);
|
||||||
|
if (!impl_->curl) {
|
||||||
|
throw std::runtime_error("Not connected");
|
||||||
|
}
|
||||||
|
|
||||||
|
long http_code = 0;
|
||||||
|
std::string response_data = performCall(method, params, http_code);
|
||||||
|
try {
|
||||||
|
json result = parseRpcResult(http_code, response_data, /*scrubSource=*/true);
|
||||||
|
std::string out;
|
||||||
|
if (result.is_string()) {
|
||||||
|
// Copy the secret out, then zero the json node's OWN heap buffer — parseRpcResult's
|
||||||
|
// json::parse allocates this independently of response_data, so scrubbing the raw body
|
||||||
|
// alone would leave it behind (B7). `out` is now the only live copy; the caller wipes it.
|
||||||
|
auto& s = result.get_ref<std::string&>();
|
||||||
|
out = s;
|
||||||
|
if (!s.empty()) sodium_memzero(&s[0], s.size());
|
||||||
|
}
|
||||||
|
if (!response_data.empty()) sodium_memzero(&response_data[0], response_data.size());
|
||||||
|
if (!result.is_string()) throw std::runtime_error("RPC result is not a string");
|
||||||
|
return out;
|
||||||
|
} catch (...) {
|
||||||
|
if (!response_data.empty()) sodium_memzero(&response_data[0], response_data.size());
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
json RPCClient::call(const std::string& method, const json& params, long timeoutSec)
|
json RPCClient::call(const std::string& method, const json& params, long timeoutSec)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lk(curl_mutex_);
|
std::lock_guard<std::recursive_mutex> lk(curl_mutex_);
|
||||||
@@ -445,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)
|
||||||
|
|||||||
@@ -133,6 +133,26 @@ public:
|
|||||||
*/
|
*/
|
||||||
json call(const std::string& method, const json& params = json::array());
|
json call(const std::string& method, const json& params = json::array());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Like call(), but zeroes the raw HTTP response body after parsing (B7).
|
||||||
|
*
|
||||||
|
* For RPCs whose response carries a secret (z_exportmnemonic / z_exportkey), the parsed
|
||||||
|
* value is scrubbed by the caller — but the raw response string this method builds also
|
||||||
|
* holds that secret and is otherwise freed without zeroing. Use this variant so the largest,
|
||||||
|
* longest-lived plaintext copy doesn't linger in freed heap. Slightly slower (an extra wipe);
|
||||||
|
* only worth it for secret-bearing calls.
|
||||||
|
*/
|
||||||
|
json callSecret(const std::string& method, const json& params = json::array());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief callSecret() for RPCs whose result is a bare secret string (z_exportkey / dumpprivkey /
|
||||||
|
* z_exportviewingkey). Returns the result string with BOTH the raw response body AND the
|
||||||
|
* parsed json's own copy of the secret zeroed — so no un-scrubbed heap copy survives the
|
||||||
|
* call. The returned std::string is the only remaining copy; the caller owns it and must
|
||||||
|
* wipe it (sodium_memzero) when done. Throws if the result is not a JSON string.
|
||||||
|
*/
|
||||||
|
std::string callSecretString(const std::string& method, const json& params = json::array());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Make a raw RPC call with a custom timeout
|
* @brief Make a raw RPC call with a custom timeout
|
||||||
* @param method RPC method name
|
* @param method RPC method name
|
||||||
@@ -250,8 +270,10 @@ private:
|
|||||||
// hold curl_mutex_ and have verified impl_->curl.
|
// hold curl_mutex_ and have verified impl_->curl.
|
||||||
std::string performCall(const std::string& method, const json& params, long& httpCodeOut);
|
std::string performCall(const std::string& method, const json& params, long& httpCodeOut);
|
||||||
// Centralizes the HTTP-code check and JSON error->RpcError extraction, returning
|
// Centralizes the HTTP-code check and JSON error->RpcError extraction, returning
|
||||||
// response["result"] on success.
|
// response["result"] on success. When scrubSource is true (secret-bearing calls), the intermediate
|
||||||
static json parseRpcResult(long httpCode, const std::string& body);
|
// parse tree's string values are zeroed before it is discarded — parseRpcResult returns a COPY of
|
||||||
|
// the result node, so its own tree would otherwise free the secret un-wiped (B7).
|
||||||
|
static json parseRpcResult(long httpCode, const std::string& body, bool scrubSource = false);
|
||||||
|
|
||||||
// Splits a UnifiedCallback into the (Callback, ErrorCallback) pair used by doRPC.
|
// Splits a UnifiedCallback into the (Callback, ErrorCallback) pair used by doRPC.
|
||||||
static std::pair<Callback, ErrorCallback> splitUnified(UnifiedCallback cb);
|
static std::pair<Callback, ErrorCallback> splitUnified(UnifiedCallback cb);
|
||||||
|
|||||||
@@ -168,20 +168,6 @@ void appendExtractedHushChatMetadata(std::vector<chat::HushChatTransactionMetada
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void appendExtractedHushChatMetadata(std::vector<chat::HushChatTransactionMetadata>& destination,
|
|
||||||
const std::string& txid,
|
|
||||||
const NetworkRefreshService::TransactionViewCacheEntry& entry)
|
|
||||||
{
|
|
||||||
if (!chat::hushChatFeatureEnabledAtBuild()) return;
|
|
||||||
|
|
||||||
std::vector<chat::HushChatMemoOutput> outputs;
|
|
||||||
outputs.reserve(entry.outgoing_outputs.size());
|
|
||||||
for (const auto& output : entry.outgoing_outputs) {
|
|
||||||
if (!output.memo.empty()) outputs.push_back(chat::HushChatMemoOutput{output.position, output.memo});
|
|
||||||
}
|
|
||||||
appendExtractedHushChatMetadata(destination, txid, outputs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void appendExtractedHushChatMetadata(std::vector<chat::HushChatTransactionMetadata>& destination,
|
void appendExtractedHushChatMetadata(std::vector<chat::HushChatTransactionMetadata>& destination,
|
||||||
const HushChatMemoOutputMap& outputsByTxid)
|
const HushChatMemoOutputMap& outputsByTxid)
|
||||||
{
|
{
|
||||||
@@ -448,8 +434,15 @@ std::optional<NetworkRefreshService::PriceRefreshResult> NetworkRefreshService::
|
|||||||
result.market.market_cap = data.value("usd_market_cap", 0.0);
|
result.market.market_cap = data.value("usd_market_cap", 0.0);
|
||||||
|
|
||||||
char buf[64];
|
char buf[64];
|
||||||
std::tm* tm = std::localtime(&fetchedAt);
|
// Runs on the RPC worker thread — std::localtime shares a process-wide static tm, so use the
|
||||||
if (tm && std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm) > 0) {
|
// reentrant variant into a local tm (matches the rest of the codebase).
|
||||||
|
std::tm tmv{};
|
||||||
|
#ifdef _WIN32
|
||||||
|
localtime_s(&tmv, &fetchedAt);
|
||||||
|
#else
|
||||||
|
localtime_r(&fetchedAt, &tmv);
|
||||||
|
#endif
|
||||||
|
if (std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tmv) > 0) {
|
||||||
result.market.last_updated = buf;
|
result.market.last_updated = buf;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -920,7 +913,10 @@ NetworkRefreshService::TransactionRefreshResult NetworkRefreshService::collectTr
|
|||||||
if (cached != snapshot.viewTxCache.end()) {
|
if (cached != snapshot.viewTxCache.end()) {
|
||||||
if (!trackedSend || !cached->second.outgoing_outputs.empty()) {
|
if (!trackedSend || !cached->second.outgoing_outputs.empty()) {
|
||||||
appendViewTransactionOutputs(result.transactions, txid, cached->second);
|
appendViewTransactionOutputs(result.transactions, txid, cached->second);
|
||||||
appendExtractedHushChatMetadata(result.hushChatMetadata, txid, cached->second);
|
// NB: do NOT extract chat metadata from our OWN outgoing memos here — ingest marks
|
||||||
|
// everything Incoming, so that duplicates every sent message as a phantom "from peer"
|
||||||
|
// entry. Genuine incoming comes from the z_listreceivedbyaddress path; our sends are
|
||||||
|
// recorded by the local echo. (The recent-refresh variant already omits this.)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -945,7 +941,8 @@ NetworkRefreshService::TransactionRefreshResult NetworkRefreshService::collectTr
|
|||||||
|
|
||||||
auto entry = parseViewTransactionCacheEntry(viewTransaction);
|
auto entry = parseViewTransactionCacheEntry(viewTransaction);
|
||||||
appendViewTransactionOutputs(result.transactions, txid, entry);
|
appendViewTransactionOutputs(result.transactions, txid, entry);
|
||||||
appendExtractedHushChatMetadata(result.hushChatMetadata, txid, entry);
|
// (Chat metadata is harvested only from RECEIVED notes, not our own outgoing memos — see
|
||||||
|
// the cached-view branch above.)
|
||||||
|
|
||||||
json rawTransaction;
|
json rawTransaction;
|
||||||
bool hasRawTransaction = false;
|
bool hasRawTransaction = false;
|
||||||
|
|||||||
@@ -101,10 +101,18 @@ WalletSecurityController::KeyKind WalletSecurityController::classifyPrivateKey(c
|
|||||||
// (The old `key[0]=='s'` test misrouted an uppercase "SK..." shielded key to the transparent RPC.)
|
// (The old `key[0]=='s'` test misrouted an uppercase "SK..." shielded key to the transparent RPC.)
|
||||||
if (key.rfind("secret-extended-key-", 0) == 0) return KeyKind::Shielded;
|
if (key.rfind("secret-extended-key-", 0) == 0) return KeyKind::Shielded;
|
||||||
if (key.size() >= 2 && key[0] == 'S' && key[1] == 'K') return KeyKind::Shielded;
|
if (key.size() >= 2 && key[0] == 'S' && key[1] == 'K') return KeyKind::Shielded;
|
||||||
if (!key.empty() && key[0] == 's') return KeyKind::Shielded;
|
// A "z"-prefixed key is a shielded viewing key (zxview…); "s"-prefixed is a legacy Sprout key.
|
||||||
|
if (!key.empty() && (key[0] == 's' || key[0] == 'z')) return KeyKind::Shielded;
|
||||||
return KeyKind::Transparent;
|
return KeyKind::Transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WalletSecurityController::isViewingKey(const std::string& key)
|
||||||
|
{
|
||||||
|
// Sapling extended full viewing key (mainnet HRP "zxviews"; "zxview" also matches the prefix the
|
||||||
|
// lite backend recognizes). Watch-only: reveals the address's funds but cannot spend them.
|
||||||
|
return key.rfind("zxview", 0) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool WalletSecurityController::isRecognizedPrivateKey(const std::string& key)
|
bool WalletSecurityController::isRecognizedPrivateKey(const std::string& key)
|
||||||
{
|
{
|
||||||
if (key.rfind("secret-extended-key-", 0) == 0) return true; // Sapling z spending key
|
if (key.rfind("secret-extended-key-", 0) == 0) return true; // Sapling z spending key
|
||||||
@@ -115,6 +123,11 @@ bool WalletSecurityController::isRecognizedPrivateKey(const std::string& key)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WalletSecurityController::isRecognizedImportKey(const std::string& key)
|
||||||
|
{
|
||||||
|
return isRecognizedPrivateKey(key) || isViewingKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
const char* WalletSecurityController::importSuccessMessage(KeyKind kind)
|
const char* WalletSecurityController::importSuccessMessage(KeyKind kind)
|
||||||
{
|
{
|
||||||
return kind == KeyKind::Shielded
|
return kind == KeyKind::Shielded
|
||||||
|
|||||||
@@ -74,9 +74,13 @@ public:
|
|||||||
std::size_t minLength = 4);
|
std::size_t minLength = 4);
|
||||||
static KeyKind classifyAddress(const std::string& address);
|
static KeyKind classifyAddress(const std::string& address);
|
||||||
static KeyKind classifyPrivateKey(const std::string& key);
|
static KeyKind classifyPrivateKey(const std::string& key);
|
||||||
|
// True if `key` is a shielded viewing key (extended full viewing key, "zxview…" — watch-only).
|
||||||
|
static bool isViewingKey(const std::string& key);
|
||||||
// True if `key` looks like a recognized Z (Sapling/Sprout spending) or T (WIF) private key.
|
// True if `key` looks like a recognized Z (Sapling/Sprout spending) or T (WIF) private key.
|
||||||
// Single source of truth for the import dialog's indicator AND its submit guard.
|
|
||||||
static bool isRecognizedPrivateKey(const std::string& key);
|
static bool isRecognizedPrivateKey(const std::string& key);
|
||||||
|
// As above, but also accepts a recognized shielded viewing key — the import dialog auto-detects
|
||||||
|
// both, so this is the single source of truth for its indicator AND its submit guard.
|
||||||
|
static bool isRecognizedImportKey(const std::string& key);
|
||||||
static const char* importSuccessMessage(KeyKind kind);
|
static const char* importSuccessMessage(KeyKind kind);
|
||||||
static std::string decryptExportFileName(std::uint64_t timestampSeconds);
|
static std::string decryptExportFileName(std::uint64_t timestampSeconds);
|
||||||
static void secureClear(std::string& value);
|
static void secureClear(std::string& value);
|
||||||
|
|||||||
@@ -388,14 +388,14 @@ void AcrylicMaterial::setQuality(AcrylicQuality quality)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AcrylicMaterial::applyBlur(float radius)
|
void AcrylicMaterial::applyBlur(float radius, bool ignoreMultiplier)
|
||||||
{
|
{
|
||||||
if (!blurBuffers_.isValid() || !blurShader_.isValid()) {
|
if (!blurBuffers_.isValid() || !blurShader_.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply blur radius multiplier from settings
|
// Apply the user's blur-strength multiplier — unless the caller wants an absolute radius.
|
||||||
float scaledRadius = radius * settings_.blurRadiusMultiplier;
|
float scaledRadius = ignoreMultiplier ? radius : radius * settings_.blurRadiusMultiplier;
|
||||||
|
|
||||||
// Skip blur entirely when multiplier is at or near zero — show sharp background
|
// Skip blur entirely when multiplier is at or near zero — show sharp background
|
||||||
if (scaledRadius < 0.5f) {
|
if (scaledRadius < 0.5f) {
|
||||||
@@ -566,7 +566,7 @@ void AcrylicMaterial::drawRect(ImDrawList* drawList, const ImVec2& pMin, const I
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply blur to captured background
|
// Apply blur to captured background
|
||||||
applyBlur(params.blurRadius);
|
applyBlur(params.blurRadius, params.absoluteBlurRadius);
|
||||||
|
|
||||||
// Calculate UV coordinates for sampling the blur FBO texture.
|
// Calculate UV coordinates for sampling the blur FBO texture.
|
||||||
// With multi-viewport enabled, ImGui draw coordinates are in
|
// With multi-viewport enabled, ImGui draw coordinates are in
|
||||||
@@ -1274,11 +1274,11 @@ void AcrylicMaterial::captureLiveFramebuffer()
|
|||||||
blurCacheValid_ = false;
|
blurCacheValid_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AcrylicMaterial::applyBlur(float radius)
|
void AcrylicMaterial::applyBlur(float radius, bool ignoreMultiplier)
|
||||||
{
|
{
|
||||||
if (!dx_blurSRV_[0] || !dx_blurPS_ || !dx_context_) return;
|
if (!dx_blurSRV_[0] || !dx_blurPS_ || !dx_context_) return;
|
||||||
|
|
||||||
float scaledRadius = radius * settings_.blurRadiusMultiplier;
|
float scaledRadius = ignoreMultiplier ? radius : radius * settings_.blurRadiusMultiplier;
|
||||||
if (blurCacheValid_ && std::abs(scaledRadius - lastBlurRadius_) < 0.1f) return;
|
if (blurCacheValid_ && std::abs(scaledRadius - lastBlurRadius_) < 0.1f) return;
|
||||||
|
|
||||||
int passes = 1;
|
int passes = 1;
|
||||||
@@ -1540,7 +1540,7 @@ void AcrylicMaterial::drawRect(ImDrawList* drawList, const ImVec2& pMin, const I
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run DX11 blur pipeline
|
// Run DX11 blur pipeline
|
||||||
applyBlur(params.blurRadius);
|
applyBlur(params.blurRadius, params.absoluteBlurRadius);
|
||||||
|
|
||||||
// DX11 UV: top-left = (0,0), bottom-right = (1,1) — same as ImGui
|
// DX11 UV: top-left = (0,0), bottom-right = (1,1) — same as ImGui
|
||||||
// With multi-viewport, pMin/pMax are in OS screen space; subtract
|
// With multi-viewport, pMin/pMax are in OS screen space; subtract
|
||||||
@@ -1704,7 +1704,7 @@ void AcrylicMaterial::resize(int, int) {}
|
|||||||
void AcrylicMaterial::captureBackground() {}
|
void AcrylicMaterial::captureBackground() {}
|
||||||
void AcrylicMaterial::captureBackgroundDirect() {}
|
void AcrylicMaterial::captureBackgroundDirect() {}
|
||||||
void AcrylicMaterial::captureLiveFramebuffer() {}
|
void AcrylicMaterial::captureLiveFramebuffer() {}
|
||||||
void AcrylicMaterial::applyBlur(float) {}
|
void AcrylicMaterial::applyBlur(float, bool) {}
|
||||||
ImTextureID AcrylicMaterial::getBlurredTexture() const { return 0; }
|
ImTextureID AcrylicMaterial::getBlurredTexture() const { return 0; }
|
||||||
ImTextureID AcrylicMaterial::getNoiseTexture() const { return 0; }
|
ImTextureID AcrylicMaterial::getNoiseTexture() const { return 0; }
|
||||||
void AcrylicMaterial::refreshCapabilities() {}
|
void AcrylicMaterial::refreshCapabilities() {}
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ struct AcrylicParams {
|
|||||||
// Blur radius in pixels (typical: 20-60)
|
// Blur radius in pixels (typical: 20-60)
|
||||||
float blurRadius = 30.0f;
|
float blurRadius = 30.0f;
|
||||||
|
|
||||||
|
// When true, blurRadius is used AS-IS — NOT scaled by the user's blur-strength slider
|
||||||
|
// (blurRadiusMultiplier). The modal backdrop sets this so its blur is a fixed value, independent
|
||||||
|
// of the global acrylic slider.
|
||||||
|
bool absoluteBlurRadius = false;
|
||||||
|
|
||||||
// Noise texture opacity (typical: 0.02-0.04)
|
// Noise texture opacity (typical: 0.02-0.04)
|
||||||
float noiseOpacity = 0.02f;
|
float noiseOpacity = 0.02f;
|
||||||
|
|
||||||
@@ -341,7 +346,7 @@ private:
|
|||||||
/**
|
/**
|
||||||
* @brief Apply blur passes to captured content
|
* @brief Apply blur passes to captured content
|
||||||
*/
|
*/
|
||||||
void applyBlur(float radius);
|
void applyBlur(float radius, bool ignoreMultiplier = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Composite final acrylic appearance
|
* @brief Composite final acrylic appearance
|
||||||
|
|||||||
@@ -160,6 +160,10 @@ void ThemeEffects::loadFromTheme() {
|
|||||||
|
|
||||||
// ---- Gradient Border Shift ----
|
// ---- Gradient Border Shift ----
|
||||||
gradient_border_.enabled = eff("gradient-border-enabled").sizeOr(0.0f) > 0.5f;
|
gradient_border_.enabled = eff("gradient-border-enabled").sizeOr(0.0f) > 0.5f;
|
||||||
|
// Opt-in: also draw the shifting border on every glass panel (not just the
|
||||||
|
// active nav button). Off by default so themes that only want the button
|
||||||
|
// accent (e.g. Obsidian) are unaffected; Jade turns it on as its hero.
|
||||||
|
gradient_border_.panels = eff("gradient-border-panels").sizeOr(0.0f) > 0.5f;
|
||||||
gradient_border_.speed = eff("gradient-border-speed").sizeOr(0.15f);
|
gradient_border_.speed = eff("gradient-border-speed").sizeOr(0.15f);
|
||||||
gradient_border_.thickness = eff("gradient-border-thickness").sizeOr(1.5f);
|
gradient_border_.thickness = eff("gradient-border-thickness").sizeOr(1.5f);
|
||||||
gradient_border_.alpha = eff("gradient-border-alpha").sizeOr(0.6f);
|
gradient_border_.alpha = eff("gradient-border-alpha").sizeOr(0.6f);
|
||||||
@@ -512,11 +516,15 @@ void ThemeEffects::drawGlowPulse(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
void ThemeEffects::drawGradientBorderShift(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
void ThemeEffects::drawGradientBorderShift(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
||||||
float rounding) const {
|
float rounding, float phaseOffset,
|
||||||
|
float alphaMul) const {
|
||||||
if (!enabled_ || !gradient_border_.enabled) return;
|
if (!enabled_ || !gradient_border_.enabled) return;
|
||||||
|
|
||||||
// Smooth sinusoidal oscillation between color A and color B
|
// Smooth sinusoidal oscillation between color A and color B.
|
||||||
float phase = std::sin(time_ * gradient_border_.speed * 2.0f * 3.14159265f) * 0.5f + 0.5f;
|
// phaseOffset shifts where in the cycle this element sits so a wall of
|
||||||
|
// panels reads like veins at different depths rather than one pulse.
|
||||||
|
float phase = std::sin((time_ * gradient_border_.speed + phaseOffset)
|
||||||
|
* 2.0f * 3.14159265f) * 0.5f + 0.5f;
|
||||||
|
|
||||||
// Extract RGBA from both colors and lerp
|
// Extract RGBA from both colors and lerp
|
||||||
RGB ca = unpackRGB(gradient_border_.colorA);
|
RGB ca = unpackRGB(gradient_border_.colorA);
|
||||||
@@ -525,7 +533,7 @@ void ThemeEffects::drawGradientBorderShift(ImDrawList* dl, ImVec2 pMin, ImVec2 p
|
|||||||
int r = ca.r + (int)((cb.r - ca.r) * phase);
|
int r = ca.r + (int)((cb.r - ca.r) * phase);
|
||||||
int g = ca.g + (int)((cb.g - ca.g) * phase);
|
int g = ca.g + (int)((cb.g - ca.g) * phase);
|
||||||
int b = ca.b + (int)((cb.b - ca.b) * phase);
|
int b = ca.b + (int)((cb.b - ca.b) * phase);
|
||||||
int a = scaledAlpha(gradient_border_.alpha, bgOpacity_);
|
int a = scaledAlpha(gradient_border_.alpha * alphaMul, bgOpacity_);
|
||||||
|
|
||||||
// Draw the shifting border
|
// Draw the shifting border
|
||||||
dl->AddRect(pMin, pMax, IM_COL32(r, g, b, a),
|
dl->AddRect(pMin, pMax, IM_COL32(r, g, b, a),
|
||||||
@@ -896,6 +904,22 @@ void ThemeEffects::drawPanelEffects(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
|||||||
float rounding) const {
|
float rounding) const {
|
||||||
if (!enabled_ || effects::isLowSpecMode()) return;
|
if (!enabled_ || effects::isLowSpecMode()) return;
|
||||||
|
|
||||||
|
// Gradient border on panels — a slow color-shifting outline that hugs the
|
||||||
|
// panel's rounded corners (drawn via AddRect, so it follows the rounding
|
||||||
|
// exactly — no polygonal corners). Position-based phase offset makes each
|
||||||
|
// panel drift like a vein at a different depth; softer than the active
|
||||||
|
// nav button (alphaMul 0.6) so a screenful of panels stays calm.
|
||||||
|
if (gradient_border_.enabled && gradient_border_.panels) {
|
||||||
|
float w = pMax.x - pMin.x;
|
||||||
|
float h = pMax.y - pMin.y;
|
||||||
|
if (w > 80 && h > 40) { // skip small panels
|
||||||
|
float posKey = (pMin.x * 0.0073f + pMin.y * 0.0137f);
|
||||||
|
posKey = posKey - (int)posKey; // fractional 0..1
|
||||||
|
if (posKey < 0) posKey += 1.0f;
|
||||||
|
drawGradientBorderShift(dl, pMin, pMax, rounding, posKey, 0.6f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Edge trace on panels — use position-based phase offset so each
|
// Edge trace on panels — use position-based phase offset so each
|
||||||
// panel's tracer is at a different position around the border
|
// panel's tracer is at a different position around the border
|
||||||
if (edge_trace_.enabled) {
|
if (edge_trace_.enabled) {
|
||||||
|
|||||||
@@ -69,9 +69,12 @@ public:
|
|||||||
void drawEdgeTrace(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
void drawEdgeTrace(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
||||||
float rounding) const;
|
float rounding) const;
|
||||||
|
|
||||||
/// Draw a border that shifts between two colors over time (gem-like)
|
/// Draw a border that shifts between two colors over time (gem-like).
|
||||||
|
/// phaseOffset (0..1) shifts this element's point in the color cycle so
|
||||||
|
/// many panels don't pulse in unison; alphaMul scales the whole effect.
|
||||||
void drawGradientBorderShift(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
void drawGradientBorderShift(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax,
|
||||||
float rounding) const;
|
float rounding, float phaseOffset = 0.0f,
|
||||||
|
float alphaMul = 1.0f) const;
|
||||||
|
|
||||||
/// Draw ember particles that rise from an element (fire theme)
|
/// Draw ember particles that rise from an element (fire theme)
|
||||||
void drawEmberRise(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax) const;
|
void drawEmberRise(ImDrawList* dl, ImVec2 pMin, ImVec2 pMax) const;
|
||||||
@@ -186,6 +189,7 @@ private:
|
|||||||
|
|
||||||
struct GradientBorderConfig {
|
struct GradientBorderConfig {
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
|
bool panels = false; ///< also draw on glass panels, not just the active nav button
|
||||||
float speed = 0.15f; ///< full color shift cycles per second
|
float speed = 0.15f; ///< full color shift cycles per second
|
||||||
float thickness = 1.5f; ///< border line thickness in pixels
|
float thickness = 1.5f; ///< border line thickness in pixels
|
||||||
float alpha = 0.6f; ///< peak alpha
|
float alpha = 0.6f; ///< peak alpha
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -742,18 +757,19 @@ inline void DrawFullWindowBlurBackdrop(ImDrawList* dl, const ImVec2& pMin, const
|
|||||||
// panel skips acrylic (IsFullWindowBlurOverlayActive), so the backdrop is the SOLE applyBlur
|
// panel skips acrylic (IsFullWindowBlurOverlayActive), so the backdrop is the SOLE applyBlur
|
||||||
// caller — no other radius contends for the shared, radius-keyed blur cache.
|
// caller — no other radius contends for the shared, radius-keyed blur cache.
|
||||||
auto params = GetCurrentAcrylicTheme().card;
|
auto params = GetCurrentAcrylicTheme().card;
|
||||||
params.blurRadius = 96.0f; // strong full-window blur (deeper than panels)
|
params.blurRadius = 120.0f; // strong full-window blur (deeper than panels)
|
||||||
|
params.absoluteBlurRadius = true; // fixed modal blur — independent of the user's acrylic slider
|
||||||
params.fallbackColor.w = 1.0f; // full-strength blur so the live content reads
|
params.fallbackColor.w = 1.0f; // full-strength blur so the live content reads
|
||||||
effects::ImGuiAcrylic::DrawAcrylicRect(dl, pMin, pMax, params, 0.0f);
|
effects::ImGuiAcrylic::DrawAcrylicRect(dl, pMin, pMax, params, 0.0f);
|
||||||
// Dim over the blur so the floating (card-less) modal reads as foreground. On DARK themes the
|
// Veil over the blur so the floating (card-less) modal reads as foreground. On DARK themes a
|
||||||
// theme-tinted frost darkens the backdrop and the light content pops. On LIGHT themes that same
|
// theme-tinted frost darkens the backdrop and the light content pops. On LIGHT themes the modal's
|
||||||
// tint whitens the backdrop and leaves card-less content with no figure/ground separation — so
|
// own controls carry a dropshadow for figure/ground, so the backdrop stays LIGHT — a theme-tinted
|
||||||
// light themes get a neutral dark veil instead, which also makes the modal's fields/buttons stand
|
// light frost — rather than a dark veil (which made a "light" theme read dark and sank the
|
||||||
// out from the (now slightly greyed) surround. Both a touch stronger than before (was alpha 70).
|
// dark-on-light chrome into it).
|
||||||
if (IsLightTheme())
|
if (IsLightTheme())
|
||||||
dl->AddRectFilled(pMin, pMax, IM_COL32(16, 18, 24, 56)); // neutral dark veil anchors on white
|
dl->AddRectFilled(pMin, pMax, WithAlpha(Background(), 205)); // light frost — dropshadows give shape
|
||||||
else
|
else
|
||||||
dl->AddRectFilled(pMin, pMax, WithAlpha(Background(), 90)); // theme-tinted darken, stronger
|
dl->AddRectFilled(pMin, pMax, WithAlpha(Background(), 120)); // theme-tinted darken, stronger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1346,10 +1362,17 @@ inline int SegmentedControl(ImDrawList* dl, ImVec2 origin, float totalW, float h
|
|||||||
ImVec2(cMax.x - 2.0f * dp, cMax.y - 2.0f * dp),
|
ImVec2(cMax.x - 2.0f * dp, cMax.y - 2.0f * dp),
|
||||||
WithAlpha(Primary(), 210), (height - 4.0f * dp) * 0.5f);
|
WithAlpha(Primary(), 210), (height - 4.0f * dp) * 0.5f);
|
||||||
ImVec2 ts = font->CalcTextSizeA(font->LegacySize, FLT_MAX, 0, labels[i]);
|
ImVec2 ts = font->CalcTextSizeA(font->LegacySize, FLT_MAX, 0, labels[i]);
|
||||||
|
// Center when the label fits; otherwise left-align with a small pad (so a long translation clips
|
||||||
|
// on the right, not on BOTH sides). Clip to the cell so no label can bleed into a neighbouring
|
||||||
|
// segment or past the rounded track — English fits, but de/es/fr/pt/ru labels can overrun.
|
||||||
|
const float lpad = 4.0f * dp;
|
||||||
|
const float tx = (ts.x <= cellW - 2.0f * lpad) ? (cellW - ts.x) * 0.5f : lpad;
|
||||||
|
dl->PushClipRect(cMin, cMax, true);
|
||||||
dl->AddText(font, font->LegacySize,
|
dl->AddText(font, font->LegacySize,
|
||||||
ImVec2(cMin.x + (cellW - ts.x) * 0.5f, cMin.y + (height - ts.y) * 0.5f),
|
ImVec2(cMin.x + tx, cMin.y + (height - ts.y) * 0.5f),
|
||||||
active ? IM_COL32(255, 255, 255, 255) : (hov ? OnSurface() : OnSurfaceMedium()),
|
active ? IM_COL32(255, 255, 255, 255) : (hov ? OnSurface() : OnSurfaceMedium()),
|
||||||
labels[i]);
|
labels[i]);
|
||||||
|
dl->PopClipRect();
|
||||||
ImGui::PushID(i);
|
ImGui::PushID(i);
|
||||||
ImGui::SetCursorScreenPos(cMin);
|
ImGui::SetCursorScreenPos(cMin);
|
||||||
if (ImGui::InvisibleButton(idBase, ImVec2(cellW, height))) clicked = i;
|
if (ImGui::InvisibleButton(idBase, ImVec2(cellW, height))) clicked = i;
|
||||||
@@ -1665,11 +1688,21 @@ inline void BeginOverlayDialogFooter(float totalActionWidth, bool drawSeparator
|
|||||||
// TextColored(label). The label text is passed in (already translated).
|
// TextColored(label). The label text is passed in (already translated).
|
||||||
inline void DialogWarningHeader(const char* warningLabel, const ImVec4& col = WarningVec4())
|
inline void DialogWarningHeader(const char* warningLabel, const ImVec4& col = WarningVec4())
|
||||||
{
|
{
|
||||||
ImGui::PushFont(Type().iconLarge());
|
ImFont* iconF = Type().iconLarge();
|
||||||
|
const float rowTop = ImGui::GetCursorPosY();
|
||||||
|
const float iconH = iconF->LegacySize; // already dp-scaled
|
||||||
|
const float textH = ImGui::GetFontSize();
|
||||||
|
ImGui::PushFont(iconF);
|
||||||
ImGui::TextColored(col, ICON_MD_WARNING);
|
ImGui::TextColored(col, ICON_MD_WARNING);
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
// Vertically center the label with the taller warning glyph (stays within the icon's row height).
|
||||||
|
if (iconH > textH) ImGui::SetCursorPosY(rowTop + (iconH - textH) * 0.5f);
|
||||||
|
// Wrap so a long warning flows to a second line instead of clipping at the card edge; short
|
||||||
|
// warnings (which fit) are unaffected.
|
||||||
|
ImGui::PushTextWrapPos(0.0f);
|
||||||
ImGui::TextColored(col, "%s", warningLabel);
|
ImGui::TextColored(col, "%s", warningLabel);
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 50/50 Cancel / confirm footer for overlay dialogs. Sets `outCancel` /
|
// 50/50 Cancel / confirm footer for overlay dialogs. Sets `outCancel` /
|
||||||
@@ -1699,6 +1732,70 @@ inline void DialogConfirmFooter(const char* cancelId, const char* confirmLabel,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reference dialog footer: a centered primary + Close (or Cancel) TactileButton pair, fixed width,
|
||||||
|
// NO divider above (the reference dropped the separator). Sets outPrimary/outClose when the respective
|
||||||
|
// button is clicked — the caller runs the bodies (so the primary's action can be arbitrary). The
|
||||||
|
// primary is BeginDisabled'd when !primaryEnabled. btnW<=0 → 130·dp default.
|
||||||
|
inline void DialogActionFooter(const char* primaryLabel, bool primaryEnabled,
|
||||||
|
const char* closeLabel, bool& outPrimary, bool& outClose,
|
||||||
|
float btnW = 0.0f)
|
||||||
|
{
|
||||||
|
const float dp = Layout::dpiScale();
|
||||||
|
if (btnW <= 0.0f) btnW = 130.0f * dp;
|
||||||
|
const float total = btnW * 2.0f + ImGui::GetStyle().ItemSpacing.x;
|
||||||
|
const float off = (ImGui::GetContentRegionAvail().x - total) * 0.5f;
|
||||||
|
if (off > 0.0f) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + off);
|
||||||
|
ImGui::BeginDisabled(!primaryEnabled);
|
||||||
|
if (TactileButton(primaryLabel, ImVec2(btnW, 0))) outPrimary = true;
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (TactileButton(closeLabel, ImVec2(btnW, 0))) outClose = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RAII glass sub-section for grouping an optional/advanced block inside a dialog (the reference's
|
||||||
|
// scan-height panel). Auto-sizes to its content and paints a subtle glass panel behind it. Uses a
|
||||||
|
// LOCAL ImDrawListSplitter — safe with BeginCombo popups, unlike the shared ChannelsSplit that
|
||||||
|
// GlassCardScope uses. padX/padY are the interior insets (in logical px, dp-scaled here). Render the
|
||||||
|
// section body between construction and destruction; use interiorWidth() for full-width children.
|
||||||
|
struct GlassSectionScope {
|
||||||
|
ImDrawListSplitter splitter;
|
||||||
|
ImDrawList* dl;
|
||||||
|
ImVec2 cardMin;
|
||||||
|
float panelW, padX, padY;
|
||||||
|
GlassPanelSpec spec;
|
||||||
|
|
||||||
|
explicit GlassSectionScope(float padXLogical = 12.0f, float padYLogical = 10.0f)
|
||||||
|
{
|
||||||
|
const float dp = Layout::dpiScale();
|
||||||
|
padX = padXLogical * dp;
|
||||||
|
padY = padYLogical * dp;
|
||||||
|
spec.rounding = 8.0f * dp;
|
||||||
|
spec.fillAlpha = 20;
|
||||||
|
spec.borderAlpha = 30;
|
||||||
|
dl = ImGui::GetWindowDrawList();
|
||||||
|
panelW = ImGui::GetContentRegionAvail().x;
|
||||||
|
cardMin = ImGui::GetCursorScreenPos();
|
||||||
|
splitter.Split(dl, 2);
|
||||||
|
splitter.SetCurrentChannel(dl, 1); // content above the panel background
|
||||||
|
ImGui::Dummy(ImVec2(0, padY));
|
||||||
|
ImGui::Indent(padX);
|
||||||
|
}
|
||||||
|
~GlassSectionScope()
|
||||||
|
{
|
||||||
|
ImGui::Unindent(padX);
|
||||||
|
ImGui::Dummy(ImVec2(0, padY));
|
||||||
|
const ImVec2 cardMax(cardMin.x + panelW, ImGui::GetCursorScreenPos().y);
|
||||||
|
splitter.SetCurrentChannel(dl, 0);
|
||||||
|
DrawGlassPanel(dl, cardMin, cardMax, spec);
|
||||||
|
splitter.Merge(dl);
|
||||||
|
}
|
||||||
|
// Interior content width (panel width minus both side insets) for full-width children.
|
||||||
|
float interiorWidth() const { return panelW - 2.0f * padX; }
|
||||||
|
|
||||||
|
GlassSectionScope(const GlassSectionScope&) = delete;
|
||||||
|
GlassSectionScope& operator=(const GlassSectionScope&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace material
|
} // namespace material
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace dragonx
|
} // namespace dragonx
|
||||||
|
|||||||
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
|
||||||
@@ -15,6 +15,11 @@
|
|||||||
#include "../embedded/IconsMaterialDesign.h" // Icon codepoint defines
|
#include "../embedded/IconsMaterialDesign.h" // Icon codepoint defines
|
||||||
#include "../../util/logger.h"
|
#include "../../util/logger.h"
|
||||||
|
|
||||||
|
#ifdef DRAGONX_HAVE_FREETYPE
|
||||||
|
#include "imgui_internal.h" // ImFontAtlasGetFontLoaderForStbTruetype
|
||||||
|
#include "misc/freetype/imgui_freetype.h" // ImGuiFreeType::GetFontLoader + LoadColor flag
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace dragonx {
|
namespace dragonx {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
namespace material {
|
namespace material {
|
||||||
@@ -126,6 +131,15 @@ bool Typography::load(ImGuiIO& io, float dpiScale)
|
|||||||
DEBUG_LOGF("Typography: Loading Material Design type scale (DPI: %.2f, fontScale: %.2f, userFontScale: %.2f, combined: %.2f)\n",
|
DEBUG_LOGF("Typography: Loading Material Design type scale (DPI: %.2f, fontScale: %.2f, userFontScale: %.2f, combined: %.2f)\n",
|
||||||
dpiScale, Layout::kFontScale(), Layout::userFontScale(), scale);
|
dpiScale, Layout::kFontScale(), Layout::userFontScale(), scale);
|
||||||
|
|
||||||
|
#ifdef DRAGONX_HAVE_FREETYPE
|
||||||
|
// Choose the atlas font loader BEFORE any font is added: FreeType (required to rasterize COLR
|
||||||
|
// color-emoji glyphs) when color emoji is enabled, else the default stb_truetype loader. Toggling
|
||||||
|
// the setting + reload() flips this cleanly.
|
||||||
|
io.Fonts->SetFontLoader(color_emoji_ ? ImGuiFreeType::GetFontLoader()
|
||||||
|
: ImFontAtlasGetFontLoaderForStbTruetype());
|
||||||
|
DEBUG_LOGF("Typography: font loader = %s\n", color_emoji_ ? "FreeType (color emoji)" : "stb_truetype");
|
||||||
|
#endif
|
||||||
|
|
||||||
// For ImGui, we need to load fonts at specific pixel sizes.
|
// For ImGui, we need to load fonts at specific pixel sizes.
|
||||||
// Font sizes come from Layout:: accessors (backed by UISchema JSON)
|
// Font sizes come from Layout:: accessors (backed by UISchema JSON)
|
||||||
|
|
||||||
@@ -325,6 +339,63 @@ ImFont* Typography::loadFont(ImGuiIO& io, int weight, float size, const char* na
|
|||||||
name, g_noto_cjk_subset_size);
|
name, g_noto_cjk_subset_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge monochrome emoji for chat + user text (Q12). Only into the small text fonts — baking
|
||||||
|
// ~1400 emoji at heading sizes would bloat the atlas for glyphs no heading needs. ImGui does no
|
||||||
|
// shaping, so single-codepoint emoji render but ZWJ sequences / flags won't compose. The
|
||||||
|
// >0xFFFF ranges require IMGUI_USE_WCHAR32 (imconfig.h).
|
||||||
|
static const char* const kEmojiFonts[] = {
|
||||||
|
"Body1", "Body2", "Subtitle1", "Subtitle2", "Caption", "Overline", "Button", "ButtonSm"
|
||||||
|
};
|
||||||
|
bool wantEmoji = false;
|
||||||
|
for (const char* n : kEmojiFonts) if (strcmp(name, n) == 0) { wantEmoji = true; break; }
|
||||||
|
|
||||||
|
// Emoji blob: the COLR/CPAL color font (FreeType-rendered) when color emoji is enabled and this
|
||||||
|
// is a FreeType build, else the monochrome subset (default / non-FreeType path).
|
||||||
|
const unsigned char* emojiData = g_noto_emoji_subset_data;
|
||||||
|
unsigned int emojiSize = g_noto_emoji_subset_size;
|
||||||
|
bool colorGlyphs = false;
|
||||||
|
#ifdef DRAGONX_HAVE_FREETYPE
|
||||||
|
if (color_emoji_ && g_twemoji_color_size > 0) {
|
||||||
|
emojiData = g_twemoji_color_data;
|
||||||
|
emojiSize = g_twemoji_color_size;
|
||||||
|
colorGlyphs = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (wantEmoji && emojiSize > 0) {
|
||||||
|
void* emojiCopy = IM_ALLOC(emojiSize);
|
||||||
|
memcpy(emojiCopy, emojiData, emojiSize);
|
||||||
|
|
||||||
|
ImFontConfig emojiCfg;
|
||||||
|
emojiCfg.FontDataOwnedByAtlas = true;
|
||||||
|
emojiCfg.MergeMode = true; // merge into the text font just loaded
|
||||||
|
emojiCfg.OversampleH = 1;
|
||||||
|
emojiCfg.OversampleV = 1;
|
||||||
|
emojiCfg.PixelSnapH = true;
|
||||||
|
emojiCfg.GlyphMinAdvanceX = 0;
|
||||||
|
#ifdef DRAGONX_HAVE_FREETYPE
|
||||||
|
if (colorGlyphs) emojiCfg.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_LoadColor; // render COLR in color
|
||||||
|
#endif
|
||||||
|
// The base Ubuntu font already owns U+2600–26FF etc.; MergeMode keeps the first-loaded glyph,
|
||||||
|
// so its text-style symbols win and only the codepoints it lacks fall through to emoji.
|
||||||
|
static const ImWchar emojiRanges[] = {
|
||||||
|
0x2600, 0x27BF, // Misc Symbols + Dingbats
|
||||||
|
0x2B00, 0x2BFF, // stars (⭐) + arrows
|
||||||
|
0x1F000, 0x1FAFF, // emoji planes (emoticons, pictographs, transport, supplement, extended)
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
emojiCfg.GlyphRanges = emojiRanges;
|
||||||
|
snprintf(emojiCfg.Name, sizeof(emojiCfg.Name), "%s %.0fpx (merge)",
|
||||||
|
colorGlyphs ? "Twemoji" : "NotoEmoji", size);
|
||||||
|
|
||||||
|
ImFont* emojiMerge = io.Fonts->AddFontFromMemoryTTF(emojiCopy, emojiSize, size, &emojiCfg);
|
||||||
|
if (emojiMerge) {
|
||||||
|
DEBUG_LOGF("Typography: Merged %s emoji (%u bytes) into %s OK\n",
|
||||||
|
colorGlyphs ? "color" : "mono", emojiSize, name);
|
||||||
|
} else {
|
||||||
|
DEBUG_LOGF("Typography: WARNING — emoji merge FAILED for %s (size=%u)\n", name, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
DEBUG_LOGF("Typography: Failed to load %s\n", name);
|
DEBUG_LOGF("Typography: Failed to load %s\n", name);
|
||||||
IM_FREE(fontDataCopy);
|
IM_FREE(fontDataCopy);
|
||||||
|
|||||||
@@ -113,6 +113,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
float getDpiScale() const { return dpiScale_; }
|
float getDpiScale() const { return dpiScale_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Select color vs monochrome emoji for the next (re)load. Color needs a FreeType-enabled
|
||||||
|
* build (DRAGONX_HAVE_FREETYPE); otherwise this is inert and monochrome is always used.
|
||||||
|
* Set before load()/reload() (App does this from the chat_emoji_color setting).
|
||||||
|
*/
|
||||||
|
void setColorEmoji(bool enabled) { color_emoji_ = enabled; }
|
||||||
|
bool colorEmoji() const { return color_emoji_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get font for a type style
|
* @brief Get font for a type style
|
||||||
*
|
*
|
||||||
@@ -261,6 +269,7 @@ private:
|
|||||||
|
|
||||||
bool loaded_ = false;
|
bool loaded_ = false;
|
||||||
float dpiScale_ = 1.0f;
|
float dpiScale_ = 1.0f;
|
||||||
|
bool color_emoji_ = false; // when true + FreeType present, merge the COLR color-emoji font
|
||||||
|
|
||||||
// Fonts for each type style
|
// Fonts for each type style
|
||||||
ImFont* fonts_[15] = {};
|
ImFont* fonts_[15] = {};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -149,6 +149,7 @@ struct SidebarStatus {
|
|||||||
int unconfirmedTxCount = 0; // badge on History
|
int unconfirmedTxCount = 0; // badge on History
|
||||||
bool miningActive = false; // green dot on Mining
|
bool miningActive = false; // green dot on Mining
|
||||||
int peerCount = 0; // badge on Peers
|
int peerCount = 0; // badge on Peers
|
||||||
|
int chatUnreadCount = 0; // badge on Chat (unread incoming messages)
|
||||||
// Exit
|
// Exit
|
||||||
bool exitClicked = false;
|
bool exitClicked = false;
|
||||||
// Branding logo (optional — loaded at startup)
|
// Branding logo (optional — loaded at startup)
|
||||||
@@ -715,6 +716,8 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
|
|||||||
dotOnly = true; badgeCol = Success();
|
dotOnly = true; badgeCol = Success();
|
||||||
} else if (item.page == NavPage::Peers && status.peerCount > 0) {
|
} else if (item.page == NavPage::Peers && status.peerCount > 0) {
|
||||||
badgeCount = status.peerCount;
|
badgeCount = status.peerCount;
|
||||||
|
} else if (item.page == NavPage::Chat && status.chatUnreadCount > 0) {
|
||||||
|
badgeCount = status.chatUnreadCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (badgeCount > 0 || dotOnly) {
|
if (badgeCount > 0 || dotOnly) {
|
||||||
|
|||||||
@@ -61,7 +61,12 @@ public:
|
|||||||
|
|
||||||
constexpr float cardTopViewportRatio = 0.15f;
|
constexpr float cardTopViewportRatio = 0.15f;
|
||||||
constexpr float cardBottomViewportRatio = 0.80f;
|
constexpr float cardBottomViewportRatio = 0.80f;
|
||||||
if (BeginOverlayDialog(TR("set_label"), &s_open, 660.0f, 0.92f, cardBottomViewportRatio)) {
|
OverlayDialogSpec ov;
|
||||||
|
ov.title = TR("set_label"); ov.p_open = &s_open;
|
||||||
|
ov.style = OverlayStyle::BlurFloat;
|
||||||
|
ov.cardWidth = 660.0f; ov.idSuffix = "addrlabel";
|
||||||
|
ov.cardBottomViewportRatio = cardBottomViewportRatio;
|
||||||
|
if (BeginOverlayDialog(ov)) {
|
||||||
float dp = Layout::dpiScale();
|
float dp = Layout::dpiScale();
|
||||||
|
|
||||||
// Address preview
|
// Address preview
|
||||||
@@ -108,7 +113,7 @@ public:
|
|||||||
const bool showClearIcon = (s_selectedIcon >= 0);
|
const bool showClearIcon = (s_selectedIcon >= 0);
|
||||||
const float clearRowH = showClearIcon ? ImGui::GetTextLineHeight() : 0.0f;
|
const float clearRowH = showClearIcon ? ImGui::GetTextLineHeight() : 0.0f;
|
||||||
const float buttonRowH = ImGui::GetFrameHeight();
|
const float buttonRowH = ImGui::GetFrameHeight();
|
||||||
const float separatorH = 1.0f;
|
const float separatorH = 0.0f; // footer divider removed (reference design)
|
||||||
const float preButtonReserve =
|
const float preButtonReserve =
|
||||||
(showClearIcon ? (style.ItemSpacing.y + clearRowH) : 0.0f) +
|
(showClearIcon ? (style.ItemSpacing.y + clearRowH) : 0.0f) +
|
||||||
style.ItemSpacing.y * 3.0f + separatorH;
|
style.ItemSpacing.y * 3.0f + separatorH;
|
||||||
@@ -196,7 +201,6 @@ public:
|
|||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
if (ImGui::GetCursorPosY() < buttonY) {
|
if (ImGui::GetCursorPosY() < buttonY) {
|
||||||
@@ -247,6 +251,9 @@ public:
|
|||||||
|
|
||||||
static bool isOpen() { return s_open; }
|
static bool isOpen() { return s_open; }
|
||||||
|
|
||||||
|
// Close the dialog (used by the UI sweep teardown).
|
||||||
|
static void hide() { s_open = false; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static inline bool s_open = false;
|
static inline bool s_open = false;
|
||||||
static inline App* s_app = nullptr;
|
static inline App* s_app = nullptr;
|
||||||
|
|||||||
@@ -59,7 +59,11 @@ public:
|
|||||||
|
|
||||||
using namespace material;
|
using namespace material;
|
||||||
|
|
||||||
if (BeginOverlayDialog(TR("transfer_funds"), &s_open, 620.0f, 0.94f)) {
|
OverlayDialogSpec ov;
|
||||||
|
ov.title = TR("transfer_funds"); ov.p_open = &s_open;
|
||||||
|
ov.style = OverlayStyle::BlurFloat;
|
||||||
|
ov.cardWidth = 620.0f; ov.idSuffix = "addrtransfer";
|
||||||
|
if (BeginOverlayDialog(ov)) {
|
||||||
float dp = Layout::dpiScale();
|
float dp = Layout::dpiScale();
|
||||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||||
|
|
||||||
@@ -72,9 +76,7 @@ public:
|
|||||||
|
|
||||||
// Shielding / deshielding warning
|
// Shielding / deshielding warning
|
||||||
if (s_info.fromIsZ && !s_info.toIsZ) {
|
if (s_info.fromIsZ && !s_info.toIsZ) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Warning()));
|
DialogWarningHeader(TR("deshielding_warning"));
|
||||||
ImGui::TextWrapped("%s", TR("deshielding_warning"));
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
} else if (!s_info.fromIsZ && s_info.toIsZ) {
|
} else if (!s_info.fromIsZ && s_info.toIsZ) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Success()));
|
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Success()));
|
||||||
@@ -188,7 +190,7 @@ public:
|
|||||||
buttonFont->CalcTextSizeA(buttonFontSize, 1000.0f, 0.0f, sendingLabel).x);
|
buttonFont->CalcTextSizeA(buttonFontSize, 1000.0f, 0.0f, sendingLabel).x);
|
||||||
float confirmW = std::max(confirmMinW, confirmTextW + buttonPadW);
|
float confirmW = std::max(confirmMinW, confirmTextW + buttonPadW);
|
||||||
float totalW = cancelW + confirmW + Layout::spacingMd();
|
float totalW = cancelW + confirmW + Layout::spacingMd();
|
||||||
float footerH = ImGui::GetFrameHeight() + ImGui::GetStyle().ItemSpacing.y * 3.0f + 1.0f;
|
float footerH = ImGui::GetFrameHeight() + ImGui::GetStyle().ItemSpacing.y * 3.0f; // footer divider removed
|
||||||
ImGuiViewport* vp = ImGui::GetMainViewport();
|
ImGuiViewport* vp = ImGui::GetMainViewport();
|
||||||
float cardBottomY = vp->Pos.y + vp->Size.y * 0.85f;
|
float cardBottomY = vp->Pos.y + vp->Size.y * 0.85f;
|
||||||
float footerTopY = cardBottomY - 24.0f * dp - footerH;
|
float footerTopY = cardBottomY - 24.0f * dp - footerH;
|
||||||
@@ -199,7 +201,6 @@ public:
|
|||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
float rowStartX = ImGui::GetCursorPosX();
|
float rowStartX = ImGui::GetCursorPosX();
|
||||||
@@ -300,7 +301,7 @@ private:
|
|||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
{
|
{
|
||||||
char buf[128];
|
char buf[128];
|
||||||
snprintf(buf, sizeof(buf), "Operation ID: %s",
|
snprintf(buf, sizeof(buf), TR("shield_operation_id"),
|
||||||
s_resultMsg.size() > 40
|
s_resultMsg.size() > 40
|
||||||
? (s_resultMsg.substr(0, 20) + "..." + s_resultMsg.substr(s_resultMsg.size() - 10)).c_str()
|
? (s_resultMsg.substr(0, 20) + "..." + s_resultMsg.substr(s_resultMsg.size() - 10)).c_str()
|
||||||
: s_resultMsg.c_str());
|
: s_resultMsg.c_str());
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -680,19 +680,31 @@ void RenderSharedAddressList(App* app, float listH, float availW,
|
|||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if (ImGui::BeginMenu(TR("portfolio_add_to"))) {
|
if (ImGui::BeginMenu(TR("portfolio_add_to"))) {
|
||||||
const auto& pf = app->settings()->getPortfolioEntries();
|
const auto& pf = app->settings()->getPortfolioEntries();
|
||||||
if (pf.empty()) {
|
const std::string activeHash = app->activeWalletIdentityHash();
|
||||||
ImGui::MenuItem(TR("portfolio_no_entries"), nullptr, false, false);
|
int shown = 0;
|
||||||
}
|
|
||||||
for (int pi = 0; pi < (int)pf.size(); pi++) {
|
for (int pi = 0; pi < (int)pf.size(); pi++) {
|
||||||
|
// Only this wallet's groups (or legacy/unscoped), matching the Market summary —
|
||||||
|
// don't offer to add this address to another wallet's group.
|
||||||
|
if (!(pf[pi].scope.empty() || activeHash.empty() || pf[pi].scope == activeHash))
|
||||||
|
continue;
|
||||||
|
// PushID keeps the item ID unique even when two groups share a label (a
|
||||||
|
// label-derived MenuItem ID would collide); a blank label shows a placeholder
|
||||||
|
// instead of rendering as an invisible row.
|
||||||
|
ImGui::PushID(pi);
|
||||||
bool inSet = dragonx::data::PortfolioEntryContains(pf[pi].addresses, addr.address);
|
bool inSet = dragonx::data::PortfolioEntryContains(pf[pi].addresses, addr.address);
|
||||||
if (ImGui::MenuItem(pf[pi].label.c_str(), nullptr, inSet)) {
|
const char* lbl = pf[pi].label.empty() ? TR("portfolio_new_entry") : pf[pi].label.c_str();
|
||||||
|
if (ImGui::MenuItem(lbl, nullptr, inSet)) {
|
||||||
auto entries = pf; // copy, mutate, persist
|
auto entries = pf; // copy, mutate, persist
|
||||||
if (inSet) dragonx::data::PortfolioEntryRemove(entries[pi].addresses, addr.address);
|
if (inSet) dragonx::data::PortfolioEntryRemove(entries[pi].addresses, addr.address);
|
||||||
else dragonx::data::PortfolioEntryAdd(entries[pi].addresses, addr.address);
|
else dragonx::data::PortfolioEntryAdd(entries[pi].addresses, addr.address);
|
||||||
app->settings()->setPortfolioEntries(entries);
|
app->settings()->setPortfolioEntries(entries);
|
||||||
app->settings()->save();
|
app->settings()->save();
|
||||||
}
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
shown++;
|
||||||
}
|
}
|
||||||
|
if (shown == 0)
|
||||||
|
ImGui::MenuItem(TR("portfolio_no_entries"), nullptr, false, false);
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
effects::ImGuiAcrylic::EndAcrylicPopup();
|
effects::ImGuiAcrylic::EndAcrylicPopup();
|
||||||
|
|||||||
@@ -203,10 +203,7 @@ void BlockInfoDialog::render(App* app)
|
|||||||
ImGui::Text("%s", TR("block_timestamp"));
|
ImGui::Text("%s", TR("block_timestamp"));
|
||||||
ImGui::SameLine(lbl.position);
|
ImGui::SameLine(lbl.position);
|
||||||
if (s_block_time > 0) {
|
if (s_block_time > 0) {
|
||||||
std::time_t t = static_cast<std::time_t>(s_block_time);
|
ImGui::Text("%s", dragonx::util::formatClockDateTime(s_block_time, /*withSeconds=*/true).c_str());
|
||||||
char time_buf[64];
|
|
||||||
std::strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", std::localtime(&t));
|
|
||||||
ImGui::Text("%s", time_buf);
|
|
||||||
} else {
|
} else {
|
||||||
ImGui::TextDisabled("%s", TR("unknown"));
|
ImGui::TextDisabled("%s", TR("unknown"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ public:
|
|||||||
|
|
||||||
static bool isOpen() { return s_open; }
|
static bool isOpen() { return s_open; }
|
||||||
|
|
||||||
|
// Close the dialog (used by the UI sweep teardown).
|
||||||
|
static void hide() { s_open = false; s_state = State::Confirm; }
|
||||||
|
|
||||||
static void render() {
|
static void render() {
|
||||||
if (!s_app) return;
|
if (!s_app) return;
|
||||||
if (!s_open) {
|
if (!s_open) {
|
||||||
@@ -91,21 +94,8 @@ private:
|
|||||||
ImGui::TextWrapped("%s", TR("bootstrap_desc"));
|
ImGui::TextWrapped("%s", TR("bootstrap_desc"));
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
// Warning card
|
// Care header (reference style: ⚠ + Warning color, wraps if long).
|
||||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.9f, 0.6f, 0.0f, 0.08f));
|
DialogWarningHeader(TR("bootstrap_warning"));
|
||||||
ImGui::BeginChild("##bsWarn", ImVec2(0, 0),
|
|
||||||
ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysUseWindowPadding,
|
|
||||||
ImGuiWindowFlags_NoScrollbar);
|
|
||||||
{
|
|
||||||
ImFont* iconFont = Type().iconSmall();
|
|
||||||
ImGui::PushFont(iconFont);
|
|
||||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(Warning()), "%s", ICON_MD_WARNING);
|
|
||||||
ImGui::PopFont();
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::TextWrapped("%s", TR("bootstrap_warning"));
|
|
||||||
}
|
|
||||||
ImGui::EndChild();
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
@@ -124,9 +114,12 @@ private:
|
|||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
// Buttons: Download | Mirror | Cancel
|
// Buttons: Download | Mirror | Cancel — centered as a group.
|
||||||
float btnW = 140.0f * dp;
|
float btnW = 140.0f * dp;
|
||||||
float btnSm = 90.0f * dp;
|
float btnSm = 90.0f * dp;
|
||||||
|
const float rowTotal = btnW * 2.0f + btnSm + ImGui::GetStyle().ItemSpacing.x * 2.0f;
|
||||||
|
const float rowOff = (ImGui::GetContentRegionAvail().x - rowTotal) * 0.5f;
|
||||||
|
if (rowOff > 0.0f) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + rowOff);
|
||||||
|
|
||||||
if (TactileButton(TR("download"), ImVec2(btnW, 0))) {
|
if (TactileButton(TR("download"), ImVec2(btnW, 0))) {
|
||||||
startDownload("");
|
startDownload("");
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -11,17 +11,38 @@ class App;
|
|||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Render the Chat tab (read-only HushChat conversation view — Phase 3).
|
* @brief Render the Chat tab (HushChat conversation view).
|
||||||
*
|
*
|
||||||
* A two-pane view: a conversation list on the left and the selected thread on
|
* A two-pane view: a conversation list on the left and the selected thread on the
|
||||||
* the right, both read from the App-owned ChatService store (App::chatService()).
|
* right, both read from the App-owned ChatService store (App::chatService()). Peer
|
||||||
* Peer z-addresses are resolved to contact names via the address book when known.
|
* z-addresses are resolved to contact names via the address book when known.
|
||||||
* Read-only for now — composing/sending arrives in a later phase. The tab is only
|
* Composing/sending and starting new conversations are wired (App::sendChatMessage /
|
||||||
* reachable when built with DRAGONX_ENABLE_CHAT (gated via WalletUiSurface::Chat).
|
* startChatConversation). Reachable when built with DRAGONX_ENABLE_CHAT
|
||||||
|
* (gated via WalletUiSurface::Chat).
|
||||||
*
|
*
|
||||||
* @param app Pointer to the app instance.
|
* @param app Pointer to the app instance.
|
||||||
*/
|
*/
|
||||||
void RenderChatTab(App* app);
|
void RenderChatTab(App* app);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Render the chat-customization controls (emoji style, poll rate, bubble style/color,
|
||||||
|
* density, text size, chat timestamps, Enter-to-send). Shared by the Chat tab's settings
|
||||||
|
* "notch" modal and the Settings tab's Chat & Contacts section. The app-wide clock format
|
||||||
|
* (which chat timestamps fall back to) lives separately in Settings → General.
|
||||||
|
* @param app Pointer to the app instance.
|
||||||
|
* @param contentWidth Explicit row width for right-aligning controls; pass the card's inner content
|
||||||
|
* width from the Settings tab (whose GlassCard doesn't narrow the content region). 0 = auto
|
||||||
|
* (use the current content region, correct inside the chat modal's dialog).
|
||||||
|
*/
|
||||||
|
void RenderChatSettingsControls(App* app, float contentWidth = 0.0f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Securely wipe the Chat tab's UI-local state (composer / new-conversation
|
||||||
|
* plaintext buffers + the selected-conversation ids). Called by
|
||||||
|
* App::resetChatSession() on a wallet switch/lock so one wallet's typed
|
||||||
|
* plaintext can't resurface (or linger unwiped in RAM) under the next wallet.
|
||||||
|
*/
|
||||||
|
void ResetChatTab();
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace dragonx
|
} // namespace dragonx
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "console_command_executor.h"
|
#include "console_command_executor.h"
|
||||||
#include "console_channel.h"
|
#include "console_channel.h"
|
||||||
|
#include "console_command_reference.h" // consoleCommandCategories / liteConsoleCommandCategories
|
||||||
#include "console_input_model.h" // BuildConsoleRpcCall
|
#include "console_input_model.h" // BuildConsoleRpcCall
|
||||||
|
|
||||||
#include "../../app.h"
|
#include "../../app.h"
|
||||||
@@ -28,6 +29,10 @@ namespace ui {
|
|||||||
|
|
||||||
using namespace material;
|
using namespace material;
|
||||||
|
|
||||||
|
// Classifies a diagnostics line into a console channel (defined below in an anonymous namespace); both
|
||||||
|
// executors drain the same LiteDiagnostics ring, so declare it up front for the full-node drain.
|
||||||
|
namespace { ConsoleChannel liteLogChannel(const std::string& line); }
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// FullNodeConsoleExecutor
|
// FullNodeConsoleExecutor
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -172,6 +177,25 @@ void FullNodeConsoleExecutor::pollLogLines(const ConsoleAddLineFn& add)
|
|||||||
} else {
|
} else {
|
||||||
last_xmrig_output_size_ = 0; // reset so we get fresh output when it restarts
|
last_xmrig_output_size_ = 0; // reset so we get fresh output when it restarts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Chat diagnostics ring: chat code logs via wallet::liteLog on both variants (send lifecycle, the
|
||||||
|
// 0-conf harvest, the note buffer). Surface those lines here as App messages so the full-node console
|
||||||
|
// shows chat activity too. Same generation-cursor drain the lite console uses.
|
||||||
|
{
|
||||||
|
auto& diag = wallet::LiteDiagnostics::instance();
|
||||||
|
const std::uint64_t gen = diag.generation();
|
||||||
|
if (gen != diag_gen_) {
|
||||||
|
const auto snap = diag.snapshot();
|
||||||
|
std::size_t startIdx = 0;
|
||||||
|
if (diag_gen_ != static_cast<std::uint64_t>(-1)) {
|
||||||
|
const std::uint64_t added = gen - diag_gen_;
|
||||||
|
startIdx = (added >= snap.size()) ? 0 : snap.size() - static_cast<std::size_t>(added);
|
||||||
|
}
|
||||||
|
for (std::size_t i = startIdx; i < snap.size(); ++i)
|
||||||
|
add(snap[i], liteLogChannel(snap[i]));
|
||||||
|
diag_gen_ = gen;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FullNodeConsoleExecutor::printHelp(const ConsoleAddLineFn& add)
|
void FullNodeConsoleExecutor::printHelp(const ConsoleAddLineFn& add)
|
||||||
@@ -194,6 +218,11 @@ void FullNodeConsoleExecutor::printHelp(const ConsoleAddLineFn& add)
|
|||||||
add(TR("console_tab_completion"), ConsoleChannel::Info);
|
add(TR("console_tab_completion"), ConsoleChannel::Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<ConsoleCommandCategory>* FullNodeConsoleExecutor::commandReference() const
|
||||||
|
{
|
||||||
|
return &consoleCommandCategories();
|
||||||
|
}
|
||||||
|
|
||||||
ConsoleStatusLine FullNodeConsoleExecutor::toolbarStatus() const
|
ConsoleStatusLine FullNodeConsoleExecutor::toolbarStatus() const
|
||||||
{
|
{
|
||||||
ConsoleStatusLine s;
|
ConsoleStatusLine s;
|
||||||
@@ -258,7 +287,7 @@ bool LiteConsoleExecutor::pollResult(std::string& result, bool& isError)
|
|||||||
if (!lw) return false;
|
if (!lw) return false;
|
||||||
wallet::LiteConsoleResult res;
|
wallet::LiteConsoleResult res;
|
||||||
if (!lw->takeConsoleResult(res)) return false;
|
if (!lw->takeConsoleResult(res)) return false;
|
||||||
result = res.response.empty() ? std::string("(no output)") : res.response;
|
result = res.response.empty() ? std::string(TR("console_no_output")) : res.response;
|
||||||
isError = !res.ok;
|
isError = !res.ok;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -286,6 +315,19 @@ void LiteConsoleExecutor::printHelp(const ConsoleAddLineFn& add)
|
|||||||
add(TR("console_help_clear"), ConsoleChannel::None);
|
add(TR("console_help_clear"), ConsoleChannel::None);
|
||||||
add(TR("console_help_help"), ConsoleChannel::None);
|
add(TR("console_help_help"), ConsoleChannel::None);
|
||||||
add(TR("lite_console_help_passthrough"), ConsoleChannel::Info);
|
add(TR("lite_console_help_passthrough"), ConsoleChannel::Info);
|
||||||
|
// The lite backend's own command verbs (literal command tokens, not RPC methods — mirrors the
|
||||||
|
// backend's get_commands() registry). Listed for discoverability: the C++ tab intercepts `help`
|
||||||
|
// before it can reach the backend's own HelpCommand, so its "type help" advice would dead-end.
|
||||||
|
add(TR("lite_console_backend_commands"), ConsoleChannel::Info);
|
||||||
|
add(" sync syncstatus balance addresses height info list notes encryptionstatus", ConsoleChannel::None);
|
||||||
|
add(" send shield new seed import timport export", ConsoleChannel::None);
|
||||||
|
add(" encrypt decrypt lock unlock rescan save sietch saplingtree coinsupply", ConsoleChannel::None);
|
||||||
|
add(TR("console_click_commands"), ConsoleChannel::Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<ConsoleCommandCategory>* LiteConsoleExecutor::commandReference() const
|
||||||
|
{
|
||||||
|
return &liteConsoleCommandCategories();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ConsoleStatusLine> LiteConsoleExecutor::statusLines() const
|
std::vector<ConsoleStatusLine> LiteConsoleExecutor::statusLines() const
|
||||||
@@ -294,20 +336,20 @@ std::vector<ConsoleStatusLine> LiteConsoleExecutor::statusLines() const
|
|||||||
wallet::LiteWalletController* lw = app_->liteWallet();
|
wallet::LiteWalletController* lw = app_->liteWallet();
|
||||||
if (lw && lw->walletOpen()) {
|
if (lw && lw->walletOpen()) {
|
||||||
const SyncInfo& sync = app_->state().sync;
|
const SyncInfo& sync = app_->state().sync;
|
||||||
char buf[96];
|
char buf[128];
|
||||||
if (sync.syncing && !sync.isSynced()) {
|
if (sync.syncing && !sync.isSynced()) {
|
||||||
double vp = sync.verification_progress;
|
double vp = sync.verification_progress;
|
||||||
if (vp < 0.0) vp = 0.0; else if (vp > 1.0) vp = 1.0;
|
if (vp < 0.0) vp = 0.0; else if (vp > 1.0) vp = 1.0;
|
||||||
std::snprintf(buf, sizeof(buf), "Syncing %.1f%% (block %d / %d)",
|
std::snprintf(buf, sizeof(buf), "%s %.1f%% (block %d / %d)",
|
||||||
vp * 100.0, sync.blocks, sync.headers);
|
TR("lite_net_syncing"), vp * 100.0, sync.blocks, sync.headers);
|
||||||
} else {
|
} else {
|
||||||
std::snprintf(buf, sizeof(buf), "Synced (block %d)", sync.blocks);
|
std::snprintf(buf, sizeof(buf), "%s (block %d)", TR("lite_net_synced"), sync.blocks);
|
||||||
}
|
}
|
||||||
out.push_back({std::string(buf), OnSurfaceMedium(), false});
|
out.push_back({std::string(buf), OnSurfaceMedium(), false});
|
||||||
}
|
}
|
||||||
const std::string& err = app_->liteOpenError();
|
const std::string& err = app_->liteOpenError();
|
||||||
if (!err.empty() && (!lw || !lw->walletOpen()))
|
if (!err.empty() && (!lw || !lw->walletOpen()))
|
||||||
out.push_back({std::string("Last error: ") + err, Error(), false});
|
out.push_back({std::string(TR("console_last_error")) + " " + err, Error(), false});
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,10 +357,10 @@ ConsoleStatusLine LiteConsoleExecutor::toolbarStatus() const
|
|||||||
{
|
{
|
||||||
ConsoleStatusLine s;
|
ConsoleStatusLine s;
|
||||||
wallet::LiteWalletController* lw = app_->liteWallet();
|
wallet::LiteWalletController* lw = app_->liteWallet();
|
||||||
if (!lw) { s.text = "No backend"; s.color = Error(); return s; }
|
if (!lw) { s.text = TR("console_backend_unavailable"); s.color = Error(); return s; }
|
||||||
if (lw->walletOpen()) { s.text = "Connected"; s.color = Success(); }
|
if (lw->walletOpen()) { s.text = TR("lite_net_connected"); s.color = Success(); }
|
||||||
else if (lw->openInProgress()) { s.text = "Connecting"; s.color = Warning(); s.pulse = true; }
|
else if (lw->openInProgress()) { s.text = TR("lite_net_connecting"); s.color = Warning(); s.pulse = true; }
|
||||||
else { s.text = "Disconnected"; s.color = Error(); }
|
else { s.text = TR("lite_net_disconnected"); s.color = Error(); }
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ namespace dragonx {
|
|||||||
class App;
|
class App;
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
|
struct ConsoleCommandCategory; // console_command_reference.h — command-reference table
|
||||||
|
|
||||||
using ConsoleAddLineFn = std::function<void(const std::string&, ConsoleChannel)>;
|
using ConsoleAddLineFn = std::function<void(const std::string&, ConsoleChannel)>;
|
||||||
|
|
||||||
struct ConsoleStatusLine {
|
struct ConsoleStatusLine {
|
||||||
@@ -64,7 +66,13 @@ public:
|
|||||||
virtual void pollLogLines(const ConsoleAddLineFn& add) { (void)add; }
|
virtual void pollLogLines(const ConsoleAddLineFn& add) { (void)add; }
|
||||||
|
|
||||||
// UI-chrome capabilities.
|
// UI-chrome capabilities.
|
||||||
virtual bool hasRpcReference() const { return false; } // show the RPC command-reference popup
|
// True when this backend speaks JSON-RPC to a daemon (the full node). Gates daemon-specific
|
||||||
|
// console behavior (the 'stop' shutdown confirm, "not connected to daemon" wording) and the
|
||||||
|
// command-reference modal's JSON string-arg quoting — the lite backend takes bare tokens.
|
||||||
|
virtual bool hasRpcReference() const { return false; }
|
||||||
|
// The command-reference table this backend offers (browsed by the console's reference modal),
|
||||||
|
// or nullptr for none. Full node = the JSON-RPC reference; lite = its own backend verbs.
|
||||||
|
virtual const std::vector<ConsoleCommandCategory>* commandReference() const { return nullptr; }
|
||||||
// Which log-filter toggles the toolbar should show (default: none).
|
// Which log-filter toggles the toolbar should show (default: none).
|
||||||
virtual ConsoleLogFilterCaps logFilterCaps() const { return {}; }
|
virtual ConsoleLogFilterCaps logFilterCaps() const { return {}; }
|
||||||
|
|
||||||
@@ -88,6 +96,7 @@ public:
|
|||||||
bool pollResult(std::string& result, bool& isError) override;
|
bool pollResult(std::string& result, bool& isError) override;
|
||||||
void pollLogLines(const ConsoleAddLineFn& add) override;
|
void pollLogLines(const ConsoleAddLineFn& add) override;
|
||||||
bool hasRpcReference() const override { return true; }
|
bool hasRpcReference() const override { return true; }
|
||||||
|
const std::vector<ConsoleCommandCategory>* commandReference() const override;
|
||||||
// Full node: daemon/xmrig log, errors-only, RPC trace, and app messages.
|
// Full node: daemon/xmrig log, errors-only, RPC trace, and app messages.
|
||||||
ConsoleLogFilterCaps logFilterCaps() const override { return {true, true, true, true}; }
|
ConsoleLogFilterCaps logFilterCaps() const override { return {true, true, true, true}; }
|
||||||
void printHelp(const ConsoleAddLineFn& add) override;
|
void printHelp(const ConsoleAddLineFn& add) override;
|
||||||
@@ -104,6 +113,9 @@ private:
|
|||||||
bool last_rpc_connected_ = false;
|
bool last_rpc_connected_ = false;
|
||||||
std::deque<std::pair<std::string, bool>> results_; // {text, isError}
|
std::deque<std::pair<std::string, bool>> results_; // {text, isError}
|
||||||
std::mutex results_mutex_;
|
std::mutex results_mutex_;
|
||||||
|
// Chat diagnostics ring cursor: chat code logs via wallet::liteLog on BOTH variants, so the full-node
|
||||||
|
// console surfaces those lines (as App messages) too — mirrors LiteConsoleExecutor's diag drain.
|
||||||
|
std::uint64_t diag_gen_ = static_cast<std::uint64_t>(-1);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── Lite: diagnostics ring + backend console command ─────────────────────────
|
// ── Lite: diagnostics ring + backend console command ─────────────────────────
|
||||||
@@ -119,6 +131,7 @@ public:
|
|||||||
// Lite: no daemon log / RPC trace — its diagnostics ring maps to the App + Error
|
// Lite: no daemon log / RPC trace — its diagnostics ring maps to the App + Error
|
||||||
// channels, so offer errors-only + app-messages (plus the always-shown text filter).
|
// channels, so offer errors-only + app-messages (plus the always-shown text filter).
|
||||||
ConsoleLogFilterCaps logFilterCaps() const override { return {false, true, false, true}; }
|
ConsoleLogFilterCaps logFilterCaps() const override { return {false, true, false, true}; }
|
||||||
|
const std::vector<ConsoleCommandCategory>* commandReference() const override;
|
||||||
void printHelp(const ConsoleAddLineFn& add) override;
|
void printHelp(const ConsoleAddLineFn& add) override;
|
||||||
std::vector<ConsoleStatusLine> statusLines() const override;
|
std::vector<ConsoleStatusLine> statusLines() const override;
|
||||||
ConsoleStatusLine toolbarStatus() const override;
|
ConsoleStatusLine toolbarStatus() const override;
|
||||||
|
|||||||
@@ -14,106 +14,369 @@ constexpr int CountOf(const ConsoleCommandEntry (&)[N])
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ConsoleCommandEntry kControlCommands[] = {
|
const ConsoleCommandEntry kControlCommands[] = {
|
||||||
{"help", "List all commands, or get help for a specified command", "[\"command\"]"},
|
{"help", "List all commands, or get help for a specified command", "[\"command\"]",
|
||||||
{"getinfo", "Get general info about the node", ""},
|
"Lists every RPC command. Pass a command name to see the node's detailed help for just that one.",
|
||||||
{"stop", "Stop the daemon", ""},
|
"help \"getinfo\"", "commands list documentation what available"},
|
||||||
|
{"getinfo", "Get general info about the node", "",
|
||||||
|
"A quick snapshot of the node: version, block height, connection count, difficulty and wallet balance.",
|
||||||
|
"getinfo", "status version summary overview node health"},
|
||||||
|
{"stop", "Stop the daemon", "",
|
||||||
|
"Shuts the DragonX node down cleanly. The wallet stays disconnected until the node is started again.",
|
||||||
|
"stop", "quit shutdown exit close halt turn off", true},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConsoleCommandEntry kNetworkCommands[] = {
|
const ConsoleCommandEntry kNetworkCommands[] = {
|
||||||
{"getnetworkinfo", "Return P2P network state info", ""},
|
{"getnetworkinfo", "Return P2P network state info", "",
|
||||||
{"getpeerinfo", "Get data about each connected peer", ""},
|
"The node's networking state: protocol version, which networks are active, listening addresses and relay fee.",
|
||||||
{"getconnectioncount", "Get number of peer connections", ""},
|
"getnetworkinfo", "network p2p connection status internet"},
|
||||||
{"getnettotals", "Get network traffic statistics", ""},
|
{"getpeerinfo", "Get data about each connected peer", "",
|
||||||
{"addnode", "Add, remove, or connect to a node", "\"node\" \"add|remove|onetry\""},
|
"Detailed data for every peer you are connected to \xE2\x80\x94 address, version, ping time and bytes exchanged.",
|
||||||
{"setban", "Add or remove an IP/subnet from the ban list", "\"ip\" \"add|remove\" [bantime] [absolute]"},
|
"getpeerinfo", "peers connections nodes who connected"},
|
||||||
{"listbanned", "List all banned IPs/subnets", ""},
|
{"getconnectioncount", "Get number of peer connections", "",
|
||||||
{"clearbanned", "Clear all banned IPs", ""},
|
"How many peers the node is currently connected to. Zero usually means the node is still starting or offline.",
|
||||||
{"ping", "Ping all peers to measure round-trip time", ""},
|
"getconnectioncount", "peers connections count how many"},
|
||||||
|
{"getnettotals", "Get network traffic statistics", "",
|
||||||
|
"Shows the total number of bytes your node has sent and received over the peer-to-peer network since it started. Useful for monitoring how much bandwidth the wallet is using.",
|
||||||
|
"getnettotals", "bandwidth usage data traffic upload download network stats"},
|
||||||
|
{"addnode", "Add, remove, or connect to a node", "\"node\" \"add|remove|onetry\"",
|
||||||
|
"Manually connects your node to a specific peer by host or IP. Use 'add' to keep it in your list permanently, 'onetry' for a single connection attempt, or 'remove' to drop it. Handy when auto-discovery is not finding peers.",
|
||||||
|
"addnode \"node.dragonx.is\" \"add\"", "connect peer manual node ip host disconnect no peers not connecting"},
|
||||||
|
{"setban", "Add or remove an IP/subnet from the ban list", "\"ip\" \"add|remove\" [bantime] [absolute]",
|
||||||
|
"Bans or unbans an IP or subnet from connecting. bantime is in seconds (0 = the default 24h); absolute treats it as a Unix timestamp.",
|
||||||
|
"setban \"192.168.0.6\" \"add\" 86400", "ban block ip peer firewall reject", true},
|
||||||
|
{"listbanned", "List all banned IPs/subnets", "",
|
||||||
|
"Lists the IP addresses and subnets your node has banned, usually for misbehaving. Check it to see who you have blocked or to confirm before unbanning a peer.",
|
||||||
|
"listbanned", "banned blocked ip ban list blacklist peers rejected"},
|
||||||
|
{"clearbanned", "Clear all banned IPs", "",
|
||||||
|
"Removes every entry from the ban list, letting all previously-banned peers connect again.",
|
||||||
|
"clearbanned", "unban reset bans allow clear", true},
|
||||||
|
{"ping", "Ping all peers to measure round-trip time", "",
|
||||||
|
"Queues a ping to every connected peer to measure round-trip latency. Results are not returned here but show up in getpeerinfo; use it to check how responsive your connections are.",
|
||||||
|
"ping", "latency ping peers round trip connection speed responsive test"},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConsoleCommandEntry kBlockchainCommands[] = {
|
const ConsoleCommandEntry kBlockchainCommands[] = {
|
||||||
{"getblockchaininfo", "Get current blockchain state", ""},
|
{"getblockchaininfo", "Get current blockchain state", "",
|
||||||
{"getblockcount", "Get number of blocks in longest chain", ""},
|
"State of the chain: height, best block hash, difficulty, verification progress and how far the node has synced.",
|
||||||
{"getbestblockhash", "Get hash of the tip block", ""},
|
"getblockchaininfo", "chain sync height status blockchain progress"},
|
||||||
{"getblock", "Get block data for a given hash or height", "\"hash|height\" [verbosity]"},
|
{"getblockcount", "Get number of blocks in longest chain", "",
|
||||||
{"getblockhash", "Get block hash at a given height", "height"},
|
"The height of the longest chain \xE2\x80\x94 how many blocks the node has. Compare with the network to check sync.",
|
||||||
{"getblockheader", "Get block header for a given hash", "\"hash\" [verbose]"},
|
"getblockcount", "height blocks how many chain length synced"},
|
||||||
{"getdifficulty", "Get proof-of-work difficulty", ""},
|
{"getbestblockhash", "Get hash of the tip block", "",
|
||||||
{"getrawmempool", "Get all txids in mempool", "[verbose]"},
|
"Shows the ID (hash) of the newest block at the very top of the blockchain. Handy to confirm your node is synced to the current chain tip.",
|
||||||
{"getmempoolinfo", "Get mempool state info", ""},
|
"getbestblockhash", "latest block tip newest current top synced chain head"},
|
||||||
{"gettxout", "Get details about an unspent output", "\"txid\" n [includemempool]"},
|
{"getblock", "Get block data for a given hash or height", "\"hash|height\" [verbosity]",
|
||||||
{"coinsupply", "Get coin supply information", "[height]"},
|
"Returns a block by its hash or height. verbosity 0 = raw hex, 1 = decoded header + txids, 2 = full transactions.",
|
||||||
{"getchaintips", "Get all known chain tips", ""},
|
"getblock \"0000000000abc123\" 1", "block details transactions header"},
|
||||||
{"getchaintxstats", "Get chain transaction statistics", "[nblocks] [\"blockhash\"]"},
|
{"getblockhash", "Get block hash at a given height", "height",
|
||||||
{"verifychain", "Verify the blockchain database", "[checklevel] [numblocks]"},
|
"Looks up the unique ID (hash) of the block at a specific height (block number). Use it to find a block by its position, then feed the hash to other commands.",
|
||||||
{"kvsearch", "Search the blockchain key-value store", "\"key\""},
|
"getblockhash 1000000", "block by height number find block id lookup height hash"},
|
||||||
{"kvupdate", "Update a key-value pair on-chain", "\"key\" \"value\" days"},
|
{"getblockheader", "Get block header for a given hash", "\"hash\" [verbose]",
|
||||||
|
"Returns the header (summary info) of a block, such as its time, difficulty, and links to neighboring blocks. Add true for readable fields instead of raw hex.",
|
||||||
|
"getblockheader \"0000000000abc123\" true", "block summary header time difficulty inspect block metadata"},
|
||||||
|
{"getdifficulty", "Get proof-of-work difficulty", "",
|
||||||
|
"Shows how hard it currently is to mine a new block. A higher number means more mining power is competing on the network.",
|
||||||
|
"getdifficulty", "mining difficulty how hard hashrate network pow proof of work"},
|
||||||
|
{"getrawmempool", "Get all txids in mempool", "[verbose]",
|
||||||
|
"Lists the transaction IDs waiting in the mempool (unconfirmed, not yet in a block). Pass true for extra details like fee and size per transaction.",
|
||||||
|
"getrawmempool true", "pending unconfirmed transactions waiting mempool queue not confirmed"},
|
||||||
|
{"getmempoolinfo", "Get mempool state info", "",
|
||||||
|
"Reports the overall state of the waiting-transaction pool: how many transactions are pending and how much memory they use.",
|
||||||
|
"getmempoolinfo", "mempool size pending count unconfirmed pool stats memory"},
|
||||||
|
{"gettxout", "Get details about an unspent output", "\"txid\" n [includemempool]",
|
||||||
|
"Checks a specific transaction output to see if it is still unspent and how many coins it holds. The last flag controls whether the mempool is also searched.",
|
||||||
|
"gettxout \"0000000000abc123\" 0 true", "unspent output utxo check coins available spendable balance"},
|
||||||
|
{"coinsupply", "Get coin supply information", "[height]",
|
||||||
|
"Reports the coin supply at a block height (transparent, shielded, and total), or at the current tip if no height is given. Useful for checking total supply.",
|
||||||
|
"coinsupply 1000000", "total supply circulating coins how many emission money supply shielded"},
|
||||||
|
{"getchaintips", "Get all known chain tips", "",
|
||||||
|
"Lists all block-chain branch tips the node knows about, including the main chain and any stale forks. Mostly useful for diagnosing chain forks or reorgs.",
|
||||||
|
"getchaintips", "forks branches chain tips reorg orphan stale diagnose"},
|
||||||
|
{"getchaintxstats", "Get chain transaction statistics", "[nblocks] [\"blockhash\"]",
|
||||||
|
"Gives statistics about transactions over a window of recent blocks, like total count and average transactions per second. Useful for gauging network activity.",
|
||||||
|
"getchaintxstats 2016", "transaction stats network activity tx rate throughput volume history"},
|
||||||
|
{"verifychain", "Verify the blockchain database", "[checklevel] [numblocks]",
|
||||||
|
"Runs an integrity check on your local blockchain database to confirm it is not corrupted. A higher checklevel and more blocks mean a deeper, slower check.",
|
||||||
|
"verifychain 3 288", "check database integrity verify blockchain corruption validate"},
|
||||||
|
{"kvsearch", "Search the blockchain key-value store", "\"key\"",
|
||||||
|
"Advanced/developer command. Looks up a value previously stored on-chain under a given key in DragonX's key-value store.",
|
||||||
|
"kvsearch \"mykey\"", "lookup key value store read on-chain data retrieve kv developer"},
|
||||||
|
{"kvupdate", "Update a key-value pair on-chain", "\"key\" \"value\" days",
|
||||||
|
"Advanced/developer command. Stores or updates a key-value entry on the blockchain for a number of days (costs a fee); an optional passphrase can protect the key. Rarely needed by wallet users.",
|
||||||
|
"kvupdate \"mykey\" \"myvalue\" 30", "write key value store on-chain data set update kv developer publish"},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConsoleCommandEntry kMiningCommands[] = {
|
const ConsoleCommandEntry kMiningCommands[] = {
|
||||||
{"getmininginfo", "Get mining-related information", ""},
|
{"getmininginfo", "Get mining-related information", "",
|
||||||
{"setgenerate", "Turn mining on or off (true/false [threads])", "generate [genproclimit]"},
|
"Mining status: current difficulty, estimated network hash rate and whether this node is generating blocks.",
|
||||||
{"getgenerate", "Check if the node is mining", ""},
|
"getmininginfo", "mining hashrate difficulty generate"},
|
||||||
{"getnetworkhashps", "Get estimated network hash rate", "[blocks] [height]"},
|
{"setgenerate", "Turn mining on or off (true/false [threads])", "generate [genproclimit]",
|
||||||
{"getblocksubsidy", "Get block reward at a given height", "[height]"},
|
"Turns built-in CPU mining on or off. Pass true or false, and optionally the number of threads (-1 = all cores).",
|
||||||
{"getblocktemplate", "Get block template for mining", "[\"jsonrequestobject\"]"},
|
"setgenerate true 4", "mine mining generate cpu start stop", true},
|
||||||
{"submitblock", "Submit a mined block to the network", "\"hexdata\""},
|
{"getgenerate", "Check if the node is mining", "",
|
||||||
|
"Tells you whether the node's built-in CPU miner is currently turned on and trying to mine blocks. Handy to confirm mining is running or stopped.",
|
||||||
|
"getgenerate", "mining on off status check am i mining is mining running generate"},
|
||||||
|
{"getnetworkhashps", "Get estimated network hash rate", "[blocks] [height]",
|
||||||
|
"Estimates the whole network's total mining power (solutions per second) over recent blocks. A higher number means more miners competing to find each block.",
|
||||||
|
"getnetworkhashps 120 -1", "network hashrate mining power total speed solutions per second competition"},
|
||||||
|
{"getblocksubsidy", "Get block reward at a given height", "[height]",
|
||||||
|
"Shows the block reward (newly minted DRGX) paid for mining the block at a given height. Useful for checking current or future rewards; omit the height to use the current tip.",
|
||||||
|
"getblocksubsidy 250000", "block reward mining payout coins subsidy how much earn per block"},
|
||||||
|
{"getblocktemplate", "Get block template for mining", "[\"jsonrequestobject\"]",
|
||||||
|
"Advanced/developer command: returns the raw data a mining program needs to assemble and work on a candidate block. Normal users mine via a pool or the built-in miner instead.",
|
||||||
|
"getblocktemplate {\"capabilities\":[\"coinbasetxn\",\"workid\"]}", "mining template block work developer pool solo advanced candidate"},
|
||||||
|
{"submitblock", "Submit a mined block to the network", "\"hexdata\"",
|
||||||
|
"Advanced/developer command: submits a fully mined block (as raw hex) to the network. Only used by external mining software that built the block itself.",
|
||||||
|
"submitblock \"0000000000abc123\"", "submit block mined broadcast solo mining developer advanced send block"},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConsoleCommandEntry kWalletCommands[] = {
|
const ConsoleCommandEntry kWalletCommands[] = {
|
||||||
{"getbalance", "Get wallet transparent balance", "[\"account\"] [minconf]"},
|
{"getbalance", "Get wallet transparent balance", "[\"account\"] [minconf]",
|
||||||
{"z_gettotalbalance", "Get total transparent + shielded balance", "[minconf]"},
|
"Your confirmed transparent (t-address) balance in DRGX. Shielded funds are NOT included \xE2\x80\x94 use z_gettotalbalance for everything.",
|
||||||
{"z_getbalances", "Get all balances (transparent + shielded)", ""},
|
"getbalance", "balance money funds how much transparent"},
|
||||||
{"getnewaddress", "Generate a new transparent address", ""},
|
{"z_gettotalbalance", "Get total transparent + shielded balance", "[minconf]",
|
||||||
{"z_getnewaddress", "Generate a new shielded address", "[\"type\"]"},
|
"Your complete balance in DRGX \xE2\x80\x94 transparent plus shielded (private) funds together.",
|
||||||
{"listaddresses", "List all transparent addresses", ""},
|
"z_gettotalbalance", "balance total money funds shielded private how much"},
|
||||||
{"z_listaddresses", "List all z-addresses", ""},
|
{"z_getbalances", "Get all balances (transparent + shielded)", "",
|
||||||
{"sendtoaddress", "Send to a specific address", "\"address\" amount"},
|
"Breaks your balance down across each transparent and shielded address the wallet holds.",
|
||||||
{"z_sendmany", "Send to multiple z/t-addresses with shielded support", "\"fromaddress\" [{\"address\":\"...\",\"amount\":...}]"},
|
"z_getbalances", "balance breakdown addresses funds per address"},
|
||||||
{"z_shieldcoinbase", "Shield transparent coinbase funds to a z-address", "\"fromaddress\" \"tozaddress\" [fee] [limit]"},
|
{"getnewaddress", "Generate a new transparent address", "",
|
||||||
{"z_mergetoaddress", "Merge multiple UTXOs/notes to one address", "[\"fromaddress\",...] \"toaddress\" [fee] [limit]"},
|
"Creates a fresh transparent (t-) address you can share to receive DRGX.",
|
||||||
{"listtransactions", "List recent wallet transactions", "[\"account\"] [count] [from]"},
|
"getnewaddress", "receive new address deposit transparent create get paid"},
|
||||||
{"listunspent", "List unspent transaction outputs", "[minconf] [maxconf]"},
|
{"z_getnewaddress", "Generate a new shielded address", "[\"type\"]",
|
||||||
{"z_listunspent", "List unspent shielded notes", "[minconf] [maxconf]"},
|
"Creates a fresh shielded (z-) address for receiving DRGX privately.",
|
||||||
{"z_getoperationstatus", "Get status of async z operations", "[\"operationid\",...]"},
|
"z_getnewaddress", "receive new address private shielded create"},
|
||||||
{"z_getoperationresult", "Get result of completed z operations", "[\"operationid\",...]"},
|
{"listaddresses", "List all transparent addresses", "",
|
||||||
{"z_listoperationids", "List all async z operation IDs", ""},
|
"Lists the transparent (t-) addresses in your wallet.",
|
||||||
{"getwalletinfo", "Get wallet state info", ""},
|
"listaddresses", "addresses list my accounts transparent"},
|
||||||
{"backupwallet", "Back up wallet to a file", "\"destination\""},
|
{"z_listaddresses", "List all z-addresses", "",
|
||||||
{"dumpprivkey", "Dump private key for an address", "\"address\""},
|
"Lists the shielded (z-) addresses in your wallet.",
|
||||||
{"importprivkey", "Import a private key into the wallet", "\"privkey\" [\"label\"] [rescan]"},
|
"z_listaddresses", "addresses list my shielded private"},
|
||||||
{"dumpwallet", "Dump all wallet keys to a file", "\"filename\""},
|
{"sendtoaddress", "Send to a specific address", "\"address\" amount",
|
||||||
{"importwallet", "Import wallet from a dump file", "\"filename\""},
|
"Sends DRGX to a transparent address. amount is in DRGX. Double-check the address \xE2\x80\x94 sent transactions cannot be reversed.",
|
||||||
{"z_exportkey", "Export spending key for a z-address", "\"zaddr\""},
|
"sendtoaddress \"RyourRecipientAddr\" 1.5", "send pay transfer money spend", true},
|
||||||
{"z_importkey", "Import a z-address spending key", "\"zkey\" [rescan] [startheight]"},
|
{"z_sendmany", "Send to multiple z/t-addresses with shielded support", "\"fromaddress\" [{\"address\":\"...\",\"amount\":...}]",
|
||||||
{"z_exportviewingkey", "Export viewing key for a z-address", "\"zaddr\""},
|
"Sends DRGX from one address to one or more recipients, with shielded (private) support. Amounts are in DRGX.",
|
||||||
{"z_importviewingkey", "Import a z-address viewing key", "\"vkey\" [rescan] [startheight]"},
|
"z_sendmany \"RfromAddr\" [{\"address\":\"zs1toAddr\",\"amount\":1.0}]", "send pay private shielded transfer money", true},
|
||||||
{"z_exportwallet", "Export all wallet keys (including z-keys) to file", "\"filename\""},
|
{"z_shieldcoinbase", "Shield transparent coinbase funds to a z-address", "\"fromaddress\" \"tozaddress\" [fee] [limit]",
|
||||||
{"signmessage", "Sign a message with an address key", "\"address\" \"message\""},
|
"Moves newly mined (coinbase) transparent funds into a private shielded z-address, since mined rewards must be shielded before they can be spent normally. Runs in the background and returns an operation id.",
|
||||||
{"settxfee", "Set the transaction fee per kB", "amount"},
|
"z_shieldcoinbase \"RyourMiningAddr\" \"zs1yourShieldedAddr\"", "shield mining rewards coinbase private hide mined funds move to shielded"},
|
||||||
{"walletpassphrase", "Unlock the wallet with passphrase", "\"passphrase\" timeout"},
|
{"z_mergetoaddress", "Merge multiple UTXOs/notes to one address", "[\"fromaddress\",...] \"toaddress\" [fee] [limit]",
|
||||||
{"walletlock", "Lock the wallet", ""},
|
"Combines many small balances (from transparent and/or shielded addresses) into a single destination address in one transaction, to consolidate funds. Runs in the background and returns an operation id.",
|
||||||
{"encryptwallet", "Encrypt the wallet with a passphrase", "\"passphrase\""},
|
"z_mergetoaddress [\"RyourAddr\",\"zs1yourShieldedAddr\"] \"zs1destShieldedAddr\"", "merge combine consolidate funds sweep small balances into one address"},
|
||||||
|
{"listtransactions", "List recent wallet transactions", "[\"account\"] [count] [from]",
|
||||||
|
"Your most recent wallet transactions, newest first \xE2\x80\x94 amounts, addresses and confirmations.",
|
||||||
|
"listtransactions", "transactions history recent payments received sent"},
|
||||||
|
{"listunspent", "List unspent transaction outputs", "[minconf] [maxconf]",
|
||||||
|
"Lists your spendable transparent coins (unspent outputs), showing which addresses hold funds and how many confirmations each has. Useful to see exactly what's available to spend.",
|
||||||
|
"listunspent 1", "unspent coins utxo available balance spendable transparent outputs"},
|
||||||
|
{"z_listunspent", "List unspent shielded notes", "[minconf] [maxconf]",
|
||||||
|
"Lists your spendable shielded notes (private z-address funds), showing amounts and confirmations. The private-address counterpart to listunspent.",
|
||||||
|
"z_listunspent 1", "unspent shielded notes private balance available zaddr spendable notes"},
|
||||||
|
{"z_getoperationstatus", "Get status of async z operations", "[\"operationid\",...]",
|
||||||
|
"Checks whether a background shielded operation (like sending or shielding from a z-address) is still running, has succeeded, or has failed. Use the operation id returned when you started it.",
|
||||||
|
"z_getoperationstatus [\"opid-00000000-abc1-2345-6789-000000000abc\"]", "check status pending operation progress is my send done async job"},
|
||||||
|
{"z_getoperationresult", "Get result of completed z operations", "[\"operationid\",...]",
|
||||||
|
"Fetches the final result (transaction id or any error) of a finished background shielded operation and then removes it from memory. Use after the status shows it completed.",
|
||||||
|
"z_getoperationresult [\"opid-00000000-abc1-2345-6789-000000000abc\"]", "operation result txid outcome finished completed error async send"},
|
||||||
|
{"z_listoperationids", "List all async z operation IDs", "",
|
||||||
|
"Lists the ids of all your background shielded operations known to the node, so you can look up their status or results. Takes no arguments.",
|
||||||
|
"z_listoperationids", "list operations pending jobs opids background async queue"},
|
||||||
|
{"getwalletinfo", "Get wallet state info", "",
|
||||||
|
"Wallet summary: balances, transaction count, key-pool size and whether the wallet is encrypted and locked.",
|
||||||
|
"getwalletinfo", "wallet status info balance encrypted locked"},
|
||||||
|
{"backupwallet", "Back up wallet to a file", "\"destination\"",
|
||||||
|
"Saves a copy of your wallet.dat to the given path. Keep the backup somewhere safe and private.",
|
||||||
|
"backupwallet \"/home/you/drgx-backup.dat\"", "backup save wallet copy protect"},
|
||||||
|
{"dumpprivkey", "Dump private key for an address", "\"address\"",
|
||||||
|
"Reveals the private key for an address. Anyone with this key can spend from the address \xE2\x80\x94 never share it.",
|
||||||
|
"dumpprivkey \"RyourAddr\"", "private key export reveal secret spend", true},
|
||||||
|
{"importprivkey", "Import a private key into the wallet", "\"privkey\" [\"label\"] [rescan]",
|
||||||
|
"Imports a private key so the wallet can spend from its address, then rescans the chain for its history (can be slow).",
|
||||||
|
"importprivkey \"Uxxxxxxxxxxxx\"", "import private key restore recover add", true},
|
||||||
|
{"dumpwallet", "Dump all wallet keys to a file", "\"filename\"",
|
||||||
|
"Writes a human-readable text file of your wallet's transparent private keys for backup. The file is saved into the daemon's configured -exportdir folder. Anyone with it can spend your funds, so store it securely.",
|
||||||
|
"dumpwallet \"wallet-backup\"", "backup export private keys save wallet dump keys to file", true},
|
||||||
|
{"importwallet", "Import wallet from a dump file", "\"filename\"",
|
||||||
|
"Loads keys from a dumpwallet text file back into your wallet and rescans the chain for their funds. Use to restore a backup or add keys from another wallet. Advanced/recovery use.",
|
||||||
|
"importwallet \"/home/you/wallet-backup.txt\"", "restore import keys recover load wallet backup from file", true},
|
||||||
|
{"z_exportkey", "Export spending key for a z-address", "\"zaddr\"",
|
||||||
|
"Reveals the spending key for a shielded address. Anyone with it can spend your private funds \xE2\x80\x94 keep it secret.",
|
||||||
|
"z_exportkey \"zs1yourAddr\"", "private key export shielded secret spend", true},
|
||||||
|
{"z_importkey", "Import a z-address spending key", "\"zkey\" [rescan] [startheight]",
|
||||||
|
"Adds a shielded (z-address) spending key from another wallet so you can see and spend its private funds here. By default it rescans the chain, which can take a while. Advanced/recovery use.",
|
||||||
|
"z_importkey \"secret-extended-key-main1yourZKeyHere\"", "import shielded key restore zaddr spending key recover private funds", true},
|
||||||
|
{"z_exportviewingkey", "Export viewing key for a z-address", "\"zaddr\"",
|
||||||
|
"Exports a view-only key for a shielded address, letting someone see its incoming funds and balance without being able to spend. Safe to share for auditing/monitoring.",
|
||||||
|
"z_exportviewingkey \"zs1yourShieldedAddr\"", "export viewing key watch only audit read only shielded balance"},
|
||||||
|
{"z_importviewingkey", "Import a z-address viewing key", "\"vkey\" [rescan] [startheight]",
|
||||||
|
"Imports a view-only key so your wallet can watch a shielded address's incoming funds without being able to spend them. By default it rescans the chain. For monitoring/auditing.",
|
||||||
|
"z_importviewingkey \"zviews1yourViewingKeyHere\"", "import viewing key watch only monitor shielded address audit read only", true},
|
||||||
|
{"z_exportwallet", "Export all wallet keys (including z-keys) to file", "\"filename\"",
|
||||||
|
"Writes a text-file backup of all your keys including shielded (z-address) spending keys, unlike dumpwallet which is transparent-only. Saved into the daemon's -exportdir folder; anyone with it can spend your funds.",
|
||||||
|
"z_exportwallet \"full-wallet-backup\"", "backup export all keys shielded zkeys save wallet full to file", true},
|
||||||
|
{"signmessage", "Sign a message with an address key", "\"address\" \"message\"",
|
||||||
|
"Signs a message with the private key of one of your addresses, proving you control that address.",
|
||||||
|
"signmessage \"RyourAddr\" \"hello world\"", "sign message prove ownership"},
|
||||||
|
{"settxfee", "Set the transaction fee per kB", "amount",
|
||||||
|
"Sets the fee you pay per kilobyte on transparent transactions, in DRGX. A higher fee can speed confirmation; set 0 to use the default. Applies until you change it or restart.",
|
||||||
|
"settxfee 0.0001", "set fee transaction cost per kb miner fee change fee rate"},
|
||||||
|
{"walletpassphrase", "Unlock the wallet with passphrase", "\"passphrase\" timeout",
|
||||||
|
"Unlocks an encrypted wallet for the given number of seconds so you can send funds. Your passphrase is typed in plain text \xE2\x80\x94 be sure no one is watching.",
|
||||||
|
"walletpassphrase \"your passphrase\" 60", "unlock passphrase password encrypted open", true},
|
||||||
|
{"walletlock", "Lock the wallet", "",
|
||||||
|
"Removes the encryption key from memory, locking the wallet so its keys can't spend until you unlock it again with your passphrase. Only works on an encrypted wallet.",
|
||||||
|
"walletlock", "lock wallet secure re-lock protect passphrase disable spending"},
|
||||||
|
{"encryptwallet", "Encrypt the wallet with a passphrase", "\"passphrase\"",
|
||||||
|
"Encrypts the wallet with a passphrase. The node shuts down afterward and you will need the passphrase to send. Encryption cannot be undone \xE2\x80\x94 keep the passphrase safe.",
|
||||||
|
"encryptwallet \"a strong passphrase\"", "encrypt password protect secure lock", true},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConsoleCommandEntry kRawTransactionCommands[] = {
|
const ConsoleCommandEntry kRawTransactionCommands[] = {
|
||||||
{"createrawtransaction", "Create a raw transaction spending given inputs", "[{\"txid\":\"...\",\"vout\":n},...] {\"address\":amount,...}"},
|
{"createrawtransaction", "Create a raw transaction spending given inputs", "[{\"txid\":\"...\",\"vout\":n},...] {\"address\":amount,...}",
|
||||||
{"decoderawtransaction", "Decode raw transaction hex string", "\"hexstring\""},
|
"Advanced/developer command. Builds an unsigned raw transaction from transparent inputs (txid+vout) you pick and transparent outputs you specify; returns only hex that you must still sign and broadcast. Cannot spend to or from shielded (zs1) addresses. Most users should just use the Send tab.",
|
||||||
{"decodescript", "Decode a hex-encoded script", "\"hex\""},
|
"createrawtransaction [{\"txid\":\"0000000000abc123\",\"vout\":0}] {\"RyourAddr\":1.0}", "build unsigned transaction manually craft raw tx construct spend inputs outputs advanced"},
|
||||||
{"getrawtransaction", "Get raw transaction data by txid", "\"txid\" [verbose]"},
|
{"decoderawtransaction", "Decode raw transaction hex string", "\"hexstring\"",
|
||||||
{"sendrawtransaction", "Submit raw transaction to the network", "\"hexstring\" [allowhighfees]"},
|
"Developer tool. Takes a raw transaction hex string and shows its contents in readable JSON (inputs, outputs, amounts, addresses, scripts). Useful for inspecting a transaction before you sign or broadcast it.",
|
||||||
{"signrawtransaction", "Sign a raw transaction with private keys", "\"hexstring\""},
|
"decoderawtransaction \"0100000001abc123...\"", "inspect raw transaction hex read decode view contents parse tx examine"},
|
||||||
{"fundrawtransaction", "Add inputs to meet output value", "\"hexstring\""},
|
{"decodescript", "Decode a hex-encoded script", "\"hex\"",
|
||||||
|
"Developer tool. Turns a hex-encoded script into readable form and shows the address or spending condition it represents. Rarely needed unless you are debugging scripts, redeem scripts, or multisig setups.",
|
||||||
|
"decodescript \"76a914abc123...88ac\"", "decode script hex readable inspect multisig p2sh redeemscript debug advanced"},
|
||||||
|
{"getrawtransaction", "Get raw transaction data by txid", "\"txid\" [verbose]",
|
||||||
|
"Look up a transaction by its txid and return its raw hex, or a decoded readable view if you pass 1 for verbose. Only finds a tx that is in the mempool, has an unspent output, or when the node runs with -txindex.",
|
||||||
|
"getrawtransaction \"0000000000abc123\" 1", "lookup transaction by id fetch tx details verbose inspect view raw txindex"},
|
||||||
|
{"sendrawtransaction", "Submit raw transaction to the network", "\"hexstring\" [allowhighfees]",
|
||||||
|
"Advanced command. Broadcasts an already-signed raw transaction hex to the network so miners can include it in a block. This is the final step after building and signing a transaction by hand.",
|
||||||
|
"sendrawtransaction \"0100000001abc123...\"", "broadcast submit signed transaction publish push to network relay send hex"},
|
||||||
|
{"signrawtransaction", "Sign a raw transaction with private keys", "\"hexstring\"",
|
||||||
|
"Advanced command. Signs a raw transaction using the private keys in your wallet so it becomes valid to broadcast. Returns the signed hex plus a 'complete' flag telling you whether it is fully signed and ready to send.",
|
||||||
|
"signrawtransaction \"0100000001abc123...\"", "sign raw transaction authorize private key finalize validate ready to broadcast"},
|
||||||
|
{"fundrawtransaction", "Add inputs to meet output value", "\"hexstring\"",
|
||||||
|
"Advanced wallet command. Adds enough of your wallet's inputs (plus one change output) to a partial raw transaction so it covers its outputs and fee. The added inputs are unsigned, so you still need to sign and send it afterward.",
|
||||||
|
"fundrawtransaction \"0100000000010000000000...\"", "add inputs fund coins cover amount select utxos change fee complete transaction"},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConsoleCommandEntry kUtilityCommands[] = {
|
const ConsoleCommandEntry kUtilityCommands[] = {
|
||||||
{"validateaddress", "Validate a transparent address", "\"address\""},
|
{"validateaddress", "Validate a transparent address", "\"address\"",
|
||||||
{"z_validateaddress", "Validate a z-address", "\"zaddr\""},
|
"Checks whether a transparent address is well-formed and whether it belongs to your wallet.",
|
||||||
{"estimatefee", "Estimate fee for a transaction", "nblocks"},
|
"validateaddress \"RsomeAddr\"", "validate check address valid mine"},
|
||||||
{"verifymessage", "Verify a signed message", "\"address\" \"signature\" \"message\""},
|
{"z_validateaddress", "Validate a z-address", "\"zaddr\"",
|
||||||
{"createmultisig", "Create a multisig address", "nrequired [\"key\",...]"},
|
"Checks whether a shielded (zs1...) address is valid and reports its type (sapling) and, if your wallet is loaded, whether the address belongs to you. Handy for double-checking a private address before sending to it.",
|
||||||
{"invalidateblock", "Mark a block as invalid", "\"hash\""},
|
"z_validateaddress \"zs1someAddr\"", "check shielded address valid private zaddr is this address mine verify z-address sapling"},
|
||||||
{"reconsiderblock", "Reconsider a previously invalidated block", "\"hash\""},
|
{"estimatefee", "Estimate fee for a transaction", "nblocks",
|
||||||
|
"Estimates the fee (per kilobyte) a transaction likely needs to start confirming within the given number of blocks. Returns -1 if the node hasn't seen enough activity to make a guess.",
|
||||||
|
"estimatefee 6", "estimate fee cost transaction how much confirmation speed fee per kb suggested network fee"},
|
||||||
|
{"verifymessage", "Verify a signed message", "\"address\" \"signature\" \"message\"",
|
||||||
|
"Confirms that a signed message really came from the owner of a transparent (R...) address. You supply the address, its base64 signature, and the exact original message; it returns true or false.",
|
||||||
|
"verifymessage \"RyourAddr\" \"H1234base64signature==\" \"the exact signed message\"", "verify signed message check signature prove ownership authenticate confirm sender validate proof"},
|
||||||
|
{"createmultisig", "Create a multisig address", "nrequired [\"key\",...]",
|
||||||
|
"Advanced: builds a multi-signature address that needs several keys to approve spending (e.g. 2-of-3). Returns the address and a redeem script you must keep in order to spend from it.",
|
||||||
|
"createmultisig 2 [\"RfirstAddr\",\"RsecondAddr\",\"RthirdAddr\"]", "multisig shared wallet multiple signatures joint account m of n co-sign approval redeem script"},
|
||||||
|
{"invalidateblock", "Mark a block as invalid", "\"hash\"",
|
||||||
|
"Advanced/developer: forces your node to mark a specific block (and everything built on it) as invalid, rolling your local chain back to before it. Only for troubleshooting or testing; reversible with reconsiderblock.",
|
||||||
|
"invalidateblock \"0000000000abc123\"", "reject block roll back chain reorg force invalid remove block undo block troubleshoot fork", true},
|
||||||
|
{"reconsiderblock", "Reconsider a previously invalidated block", "\"hash\"",
|
||||||
|
"Advanced/developer: clears the invalid mark on a block and its descendants so your node accepts them again and rejoins the normal chain. Used to reverse a previous invalidateblock.",
|
||||||
|
"reconsiderblock \"0000000000abc123\"", "undo invalidate accept block again re-enable block restore chain reconsider fix rollback fork", true},
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Lite backend command set — the verbs the SDXL lite backend accepts (see its
|
||||||
|
// commands.rs get_commands()). Unlike the full node these are NOT JSON-RPC: the
|
||||||
|
// backend takes plain, space-separated tokens (no quoting), so the reference
|
||||||
|
// modal inserts them bare. clear/help/quit are intentionally omitted — the C++
|
||||||
|
// console tab intercepts those before they reach the backend.
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
const ConsoleCommandEntry kLiteWalletCommands[] = {
|
||||||
|
{"balance", "Show your DRGX balance", "",
|
||||||
|
"Shows the DRGX balance held in this wallet across all its shielded and transparent addresses.",
|
||||||
|
"balance", "money funds amount total holdings"},
|
||||||
|
{"addresses", "List all addresses in the wallet", "",
|
||||||
|
"Lists every address this wallet owns \xE2\x80\x94 shielded (zs1...) and transparent (R.../t...) \xE2\x80\x94 so you can pick one to receive to.",
|
||||||
|
"addresses", "receive address list wallet mine deposit"},
|
||||||
|
{"new", "Create a new address in this wallet", "type",
|
||||||
|
"Creates a fresh receive address. Pass zs for a shielded (private) sapling address, or R (a capital R) for a transparent one \xE2\x80\x94 those exact tokens (case-sensitive).",
|
||||||
|
"new zs", "create address receive generate new shielded transparent zs"},
|
||||||
|
{"list", "List all transactions in the wallet", "",
|
||||||
|
"Shows the wallet's transaction history \xE2\x80\x94 sends, receives and shields \xE2\x80\x94 with amounts, addresses and confirmations.",
|
||||||
|
"list", "history transactions txs payments activity sent received"},
|
||||||
|
{"notes", "List sapling notes and UTXOs", "[all]",
|
||||||
|
"Lists the individual shielded notes and transparent UTXOs that make up your balance. Pass all to include spent ones.",
|
||||||
|
"notes", "utxo notes unspent coins inputs sapling"},
|
||||||
|
{"info", "Get the lightwalletd server's info", "",
|
||||||
|
"Reports the lightwalletd server the wallet is connected to \xE2\x80\x94 its version, chain and block height.",
|
||||||
|
"info", "server node lightwalletd version connection status"},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConsoleCommandEntry kLiteSyncCommands[] = {
|
||||||
|
{"sync", "Download compact blocks and sync to the server", "",
|
||||||
|
"Fetches new compact blocks from the lightwalletd server and scans them for transactions belonging to this wallet.",
|
||||||
|
"sync", "update refresh scan blocks download catch up"},
|
||||||
|
{"syncstatus", "Get the sync status of the wallet", "",
|
||||||
|
"Reports how far the wallet has synced \xE2\x80\x94 whether a sync is in progress and the block it has reached.",
|
||||||
|
"syncstatus", "progress status syncing scanning percent blocks"},
|
||||||
|
{"height", "Get the latest block height the wallet is at", "",
|
||||||
|
"Shows the block height the wallet has scanned up to. Compare with the network height to gauge sync.",
|
||||||
|
"height", "block height number chain tip synced"},
|
||||||
|
{"rescan", "Rescan the wallet from scratch", "",
|
||||||
|
"Discards the scanned state and re-downloads/re-scans every block from the wallet's birthday. Slow, but fixes a stuck or incomplete balance.",
|
||||||
|
"rescan", "rescan resync repair fix balance rebuild from scratch"},
|
||||||
|
{"save", "Save the wallet file to disk", "",
|
||||||
|
"Writes the current wallet state to disk. The wallet also saves automatically after a sync or send.",
|
||||||
|
"save", "save persist write disk store"},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConsoleCommandEntry kLiteSendCommands[] = {
|
||||||
|
{"send", "Send DRGX to an address", "[{\"address\":\"zs1...\",\"amount\":0}]",
|
||||||
|
"Sends DRGX from the console using a JSON array of recipients (the Send tab is the easy way; this is the power-user form). amount is in puposhis (the base unit); memo is optional and delivered privately to shielded recipients.",
|
||||||
|
"send [{\"address\":\"zs1exampleaddress\",\"amount\":100000000,\"memo\":\"thanks\"}]",
|
||||||
|
"pay transfer send spend money transaction json", true},
|
||||||
|
{"shield", "Shield transparent DRGX into a sapling address", "[address]",
|
||||||
|
"Moves your transparent (public) DRGX into a shielded sapling address for privacy. With no address it shields to the wallet's own sapling address.",
|
||||||
|
"shield", "shield private sapling transparent move protect", true},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConsoleCommandEntry kLiteKeyCommands[] = {
|
||||||
|
{"seed", "Display the wallet seed phrase", "",
|
||||||
|
"Reveals the seed phrase that backs up this wallet. Anyone who sees it can spend your funds \xE2\x80\x94 keep it secret and offline.",
|
||||||
|
"seed", "seed phrase mnemonic backup recovery words secret", true},
|
||||||
|
{"export", "Export the private key for an address", "[address]",
|
||||||
|
"Prints the private/spending key for a wallet address \xE2\x80\x94 anyone with it controls the funds. With no address it exports every key.",
|
||||||
|
"export zs1exampleaddress", "export private key spending backup secret", true},
|
||||||
|
{"import", "Import a spending or viewing key", "key",
|
||||||
|
"Imports a shielded spending or viewing key (pass just the key). The wallet rescans from the sapling activation height to find the key's transactions.",
|
||||||
|
"import somekey", "import restore key spending viewing add watch"},
|
||||||
|
{"timport", "Import a transparent WIF private key", "wif",
|
||||||
|
"Imports a transparent private key in WIF format (begins with U, 5, K or L) so the wallet can spend its funds.",
|
||||||
|
"timport somewifkey", "import transparent wif private key taddr"},
|
||||||
|
{"encrypt", "Encrypt the wallet with a password", "password",
|
||||||
|
"Encrypts the wallet with a password and locks it immediately. You will need the password to send or reveal keys afterwards. If you forget it, only the seed phrase can recover the wallet.",
|
||||||
|
"encrypt strongpassword", "encrypt password protect lock secure passphrase", true},
|
||||||
|
{"decrypt", "Completely remove wallet encryption", "password",
|
||||||
|
"Permanently removes the wallet's password encryption, leaving it unprotected on disk. Requires the current password.",
|
||||||
|
"decrypt strongpassword", "decrypt remove encryption password unprotect", true},
|
||||||
|
{"unlock", "Unlock the wallet for spending", "password",
|
||||||
|
"Temporarily unlocks an encrypted wallet so it can send or reveal keys. Use lock to re-lock it.",
|
||||||
|
"unlock strongpassword", "unlock password spend open temporarily"},
|
||||||
|
{"lock", "Lock a temporarily-unlocked wallet", "",
|
||||||
|
"Re-locks a wallet that was unlocked for spending, without removing its encryption.",
|
||||||
|
"lock", "lock secure re-lock protect close"},
|
||||||
|
{"encryptionstatus", "Check if the wallet is encrypted and locked", "",
|
||||||
|
"Reports whether the wallet is encrypted and, if so, whether it is currently locked or unlocked.",
|
||||||
|
"encryptionstatus", "encryption status locked unlocked encrypted state"},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConsoleCommandEntry kLiteAdvancedCommands[] = {
|
||||||
|
{"sietch", "Create a Sietch address", "[type]",
|
||||||
|
"Creates a Sietch address, used for enhanced-privacy sends. Pass zs for a sapling Sietch address.",
|
||||||
|
"sietch zs", "sietch privacy address decoy sapling advanced"},
|
||||||
|
{"saplingtree", "Dump the latest Sapling commitment tree (debug)", "",
|
||||||
|
"Prints the latest Sapling commitment tree state \xE2\x80\x94 a debugging aid, not needed for everyday use.",
|
||||||
|
"saplingtree", "sapling tree commitment debug advanced merkle"},
|
||||||
|
{"coinsupply", "Get the coin supply info", "",
|
||||||
|
"Reports coin-supply figures for the chain as seen by the wallet's server.",
|
||||||
|
"coinsupply", "supply coins total emission circulating amount"},
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -132,5 +395,17 @@ const std::vector<ConsoleCommandCategory>& consoleCommandCategories()
|
|||||||
return categories;
|
return categories;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<ConsoleCommandCategory>& liteConsoleCommandCategories()
|
||||||
|
{
|
||||||
|
static const std::vector<ConsoleCommandCategory> categories = {
|
||||||
|
{"Wallet", kLiteWalletCommands, CountOf(kLiteWalletCommands)},
|
||||||
|
{"Sync", kLiteSyncCommands, CountOf(kLiteSyncCommands)},
|
||||||
|
{"Send", kLiteSendCommands, CountOf(kLiteSendCommands)},
|
||||||
|
{"Keys & Security", kLiteKeyCommands, CountOf(kLiteKeyCommands)},
|
||||||
|
{"Advanced", kLiteAdvancedCommands, CountOf(kLiteAdvancedCommands)},
|
||||||
|
};
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace dragonx
|
} // namespace dragonx
|
||||||
|
|||||||
@@ -7,8 +7,14 @@ namespace ui {
|
|||||||
|
|
||||||
struct ConsoleCommandEntry {
|
struct ConsoleCommandEntry {
|
||||||
const char* name;
|
const char* name;
|
||||||
const char* desc;
|
const char* desc; // terse one-line summary (always set)
|
||||||
const char* params;
|
const char* params; // parameter template, e.g. "\"address\" amount [comment]"
|
||||||
|
// Optional novice-facing enrichment (C++17 aggregate defaults — entries that omit these keep
|
||||||
|
// the empty/false fallbacks, and the command explorer falls back to `desc`):
|
||||||
|
const char* details = ""; // longer plain-language explanation
|
||||||
|
const char* example = ""; // one concrete, runnable example line
|
||||||
|
const char* keywords = ""; // space-separated search synonyms ("balance money funds")
|
||||||
|
bool destructive = false; // consequential/sensitive -> safety badge + run confirmation
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ConsoleCommandCategory {
|
struct ConsoleCommandCategory {
|
||||||
@@ -17,7 +23,12 @@ struct ConsoleCommandCategory {
|
|||||||
int count;
|
int count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The full-node daemon's JSON-RPC command reference (browsed by the console's command-reference modal).
|
||||||
const std::vector<ConsoleCommandCategory>& consoleCommandCategories();
|
const std::vector<ConsoleCommandCategory>& consoleCommandCategories();
|
||||||
|
|
||||||
|
// The lite backend's own command set (the ~25 verbs the SDXL backend accepts) — the lite-variant
|
||||||
|
// analog of the RPC reference, shown by the same modal when the executor is the lite one.
|
||||||
|
const std::vector<ConsoleCommandCategory>& liteConsoleCommandCategories();
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
} // namespace dragonx
|
} // namespace dragonx
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include <cmath>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace dragonx {
|
namespace dragonx {
|
||||||
@@ -116,9 +117,9 @@ std::vector<std::string> FormatConsoleCompletionLines(const std::vector<std::str
|
|||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> ParseConsoleCommandArgs(const std::string& command)
|
std::vector<ConsoleArg> ParseConsoleCommandArgsTagged(const std::string& command)
|
||||||
{
|
{
|
||||||
std::vector<std::string> args;
|
std::vector<ConsoleArg> args;
|
||||||
std::size_t index = 0;
|
std::size_t index = 0;
|
||||||
while (index < command.size()) {
|
while (index < command.size()) {
|
||||||
while (index < command.size() && (command[index] == ' ' || command[index] == '\t')) {
|
while (index < command.size() && (command[index] == ' ' || command[index] == '\t')) {
|
||||||
@@ -126,11 +127,12 @@ std::vector<std::string> ParseConsoleCommandArgs(const std::string& command)
|
|||||||
}
|
}
|
||||||
if (index >= command.size()) break;
|
if (index >= command.size()) break;
|
||||||
|
|
||||||
std::string token;
|
ConsoleArg arg;
|
||||||
if (command[index] == '"' || command[index] == '\'') {
|
if (command[index] == '"' || command[index] == '\'') {
|
||||||
|
arg.quoted = true;
|
||||||
char quote = command[index++];
|
char quote = command[index++];
|
||||||
while (index < command.size() && command[index] != quote) {
|
while (index < command.size() && command[index] != quote) {
|
||||||
token += command[index++];
|
arg.text += command[index++];
|
||||||
}
|
}
|
||||||
if (index < command.size()) ++index;
|
if (index < command.size()) ++index;
|
||||||
} else if (command[index] == '[' || command[index] == '{') {
|
} else if (command[index] == '[' || command[index] == '{') {
|
||||||
@@ -140,30 +142,69 @@ std::vector<std::string> ParseConsoleCommandArgs(const std::string& command)
|
|||||||
while (index < command.size()) {
|
while (index < command.size()) {
|
||||||
if (command[index] == open) ++depth;
|
if (command[index] == open) ++depth;
|
||||||
else if (command[index] == close) --depth;
|
else if (command[index] == close) --depth;
|
||||||
token += command[index++];
|
arg.text += command[index++];
|
||||||
if (depth == 0) break;
|
if (depth == 0) break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (index < command.size() && command[index] != ' ' && command[index] != '\t') {
|
while (index < command.size() && command[index] != ' ' && command[index] != '\t') {
|
||||||
token += command[index++];
|
arg.text += command[index++];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!token.empty()) args.push_back(token);
|
// A quoted empty string ("") is a real argument; only skip genuinely-empty unquoted tokens.
|
||||||
|
if (!arg.text.empty() || arg.quoted) args.push_back(std::move(arg));
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> ParseConsoleCommandArgs(const std::string& command)
|
||||||
|
{
|
||||||
|
std::vector<std::string> args;
|
||||||
|
for (auto& a : ParseConsoleCommandArgsTagged(command)) args.push_back(std::move(a.text));
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Interpret a bare token as a JSON number only when the WHOLE token is a valid, finite number;
|
||||||
|
// otherwise return it as a string. Guards against std::stoll silently truncating "1e9"/"123abc"
|
||||||
|
// (it stops at the first non-digit) and std::stod yielding inf/subnormal for out-of-range input.
|
||||||
|
nlohmann::json ParseConsoleNumberOrString(const std::string& s)
|
||||||
|
{
|
||||||
|
if (!s.empty()) {
|
||||||
|
try {
|
||||||
|
std::size_t pos = 0;
|
||||||
|
long long ll = std::stoll(s, &pos);
|
||||||
|
if (pos == s.size()) return ll;
|
||||||
|
} catch (...) {}
|
||||||
|
try {
|
||||||
|
std::size_t pos = 0;
|
||||||
|
double d = std::stod(s, &pos);
|
||||||
|
if (pos == s.size() && std::isfinite(d)) return d;
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
ConsoleRpcCall BuildConsoleRpcCall(const std::string& command)
|
ConsoleRpcCall BuildConsoleRpcCall(const std::string& command)
|
||||||
{
|
{
|
||||||
auto args = ParseConsoleCommandArgs(command);
|
auto args = ParseConsoleCommandArgsTagged(command);
|
||||||
ConsoleRpcCall call;
|
ConsoleRpcCall call;
|
||||||
if (args.empty()) return call;
|
if (args.empty()) return call;
|
||||||
|
|
||||||
call.valid = true;
|
call.valid = true;
|
||||||
call.method = args.front();
|
call.method = args.front().text;
|
||||||
|
|
||||||
for (std::size_t argIndex = 1; argIndex < args.size(); ++argIndex) {
|
for (std::size_t argIndex = 1; argIndex < args.size(); ++argIndex) {
|
||||||
const std::string& arg = args[argIndex];
|
const ConsoleArg& a = args[argIndex];
|
||||||
|
const std::string& arg = a.text;
|
||||||
|
|
||||||
|
// A quoted token is always a string — never coerced — so a genuinely-string argument that
|
||||||
|
// happens to look numeric (e.g. an all-digit label) is sent as a string, not a number.
|
||||||
|
if (a.quoted) {
|
||||||
|
call.params.push_back(arg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!arg.empty() && (arg[0] == '{' || arg[0] == '[')) {
|
if (!arg.empty() && (arg[0] == '{' || arg[0] == '[')) {
|
||||||
auto parsed = nlohmann::json::parse(arg, nullptr, false);
|
auto parsed = nlohmann::json::parse(arg, nullptr, false);
|
||||||
if (!parsed.is_discarded()) {
|
if (!parsed.is_discarded()) {
|
||||||
@@ -181,15 +222,7 @@ ConsoleRpcCall BuildConsoleRpcCall(const std::string& command)
|
|||||||
} else if (arg == "false") {
|
} else if (arg == "false") {
|
||||||
call.params.push_back(false);
|
call.params.push_back(false);
|
||||||
} else {
|
} else {
|
||||||
try {
|
call.params.push_back(ParseConsoleNumberOrString(arg));
|
||||||
if (arg.find('.') != std::string::npos) {
|
|
||||||
call.params.push_back(std::stod(arg));
|
|
||||||
} else {
|
|
||||||
call.params.push_back(std::stoll(arg));
|
|
||||||
}
|
|
||||||
} catch (...) {
|
|
||||||
call.params.push_back(arg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user