feat(ui): settings polish — button retune, daemon card, RPC 2-row, chat preview
Settings tabs brought closer to the approved mockup: - ActionButton/renderCardButton retune (settings-scoped): 7px radius, 9px padX, Primary → accent-outline chip, Secondary/card buttons more defined. - Daemon-binary card: compact status right-aligned on the DAEMON BINARY heading (Up to date / Version differs / Not installed), filled/rounded status box, neutral danger divider (was alarming red), roomier spacing. - RPC Connection: two-row column-aligned layout (Host | Port, then Username | Password) so the password no longer clips off the card edge. - Chat settings tab: live conversation preview below the Appearance / Messaging cards; "Focus input on open" checkbox reflowed onto the console color-toggle row. - Debug Options: "Current theme only" toggle restricts either screenshot sweep to the active theme instead of cycling every skin. - Tabs fill the full content width (content-max-width cap disabled) and the sidebar nav panel centers within the true visible area. - i18n: new keys for the above (untranslated keys fall back to English). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+13
-24
@@ -1795,32 +1795,21 @@ void App::render()
|
||||
sbStatus.unconfirmedTxCount = static_cast<int>(unconfirmedTxids.size());
|
||||
}
|
||||
|
||||
// Sidebar margins from ui.toml schema (DPI-scaled like all sidebar values)
|
||||
const float sbMarginTop = sbde("margin-top", 0.0f);
|
||||
const float sbMarginBottom = sbde("margin-bottom", 0.0f);
|
||||
const float sbMinHeight = sbde("min-height", 360.0f);
|
||||
// Sidebar minimum height from ui.toml schema (DPI-scaled).
|
||||
const float sbMinHeight = sbde("min-height", 360.0f);
|
||||
|
||||
// Ensure sidebar is tall enough to fit all buttons — shrink margins if needed
|
||||
float sidebarH = contentH - sbMarginTop - sbMarginBottom;
|
||||
float effectiveMarginTop = sbMarginTop;
|
||||
if (sidebarH < sbMinHeight) {
|
||||
float available = contentH - sbMinHeight;
|
||||
if (available > 0.0f) {
|
||||
float ratio = available / (sbMarginTop + sbMarginBottom);
|
||||
effectiveMarginTop = sbMarginTop * ratio;
|
||||
} else {
|
||||
effectiveMarginTop = 0.0f;
|
||||
}
|
||||
sidebarH = std::max(contentH - effectiveMarginTop, sbMinHeight);
|
||||
}
|
||||
|
||||
// Sidebar navigation
|
||||
// Save cursor Y before applying sidebar margin so the content area
|
||||
// (placed via SameLine) starts at the original row position, not the
|
||||
// margin-shifted one.
|
||||
// Save cursor Y before the sidebar so the content area (restored below) starts at the original row.
|
||||
float preSidebarCursorY = ImGui::GetCursorPosY();
|
||||
if (effectiveMarginTop > 0.0f)
|
||||
ImGui::SetCursorPosY(preSidebarCursorY + effectiveMarginTop);
|
||||
|
||||
// Size the sidebar to span from its own top down to the status-bar top, so the nav panel centers
|
||||
// within the TRUE visible area (equal top/bottom gaps). Do NOT derive it from contentH (inset by the
|
||||
// content-area's edge-fade margins) and do NOT apply the legacy sidebar margin-top/-bottom (they are
|
||||
// asymmetric, -12 / +40, and pushed the panel upward). Window-local reference (matches how the status
|
||||
// bar is positioned — GetWindowPos/GetWindowSize, not GetMainViewport) so it is correct on every platform.
|
||||
float sbWindowBottom = ImGui::GetWindowPos().y + ImGui::GetWindowSize().y;
|
||||
float sbStatusTopY = sbWindowBottom - statusBarH - mainPadBot;
|
||||
float sbChildTopY = ImGui::GetCursorScreenPos().y;
|
||||
float sidebarH = std::max(sbMinHeight, sbStatusTopY - sbChildTopY);
|
||||
bool prevCollapsed = sidebar_collapsed_;
|
||||
{
|
||||
PERF_SCOPE("Render.Sidebar");
|
||||
|
||||
@@ -408,6 +408,9 @@ public:
|
||||
// each under every skin. Output: <config>/screenshots-full/<surface>/<skin>.png + an index.
|
||||
void startFullUiSweep();
|
||||
std::string screenshotFullDir() const;
|
||||
// Debug option: restrict either sweep to just the currently-active theme instead of cycling all.
|
||||
bool sweepCurrentThemeOnly() const { return sweep_current_theme_only_; }
|
||||
void setSweepCurrentThemeOnly(bool v) { sweep_current_theme_only_ = v; }
|
||||
bool isScreenshotSweeping() const { return screenshot_sweep_active_; }
|
||||
bool wantsScreenshotThisFrame() const { return sweep_capture_this_frame_; }
|
||||
const std::string& screenshotSweepPath() const { return sweep_current_path_; }
|
||||
@@ -1114,6 +1117,7 @@ private:
|
||||
|
||||
// Debug screenshot sweep state.
|
||||
bool screenshot_sweep_active_ = false;
|
||||
bool sweep_current_theme_only_ = false; // Debug Options: sweep only the active theme
|
||||
bool sweep_capture_this_frame_ = false;
|
||||
int sweep_skin_idx_ = 0;
|
||||
int sweep_settle_frames_ = 0; // frames to let a new skin/surface settle before capture
|
||||
|
||||
@@ -704,6 +704,10 @@ void App::startSweepImpl(bool full)
|
||||
if (sk.valid) sweep_skins_.push_back(sk.id);
|
||||
if (sweep_skins_.empty()) return;
|
||||
|
||||
// Debug Options "Current theme only": sweep just the active skin instead of cycling every theme.
|
||||
if (sweep_current_theme_only_)
|
||||
sweep_skins_.assign(1, ui::schema::SkinManager::instance().activeSkinId());
|
||||
|
||||
sweep_full_ = full;
|
||||
if (full) { capture_mode_ = true; installDemoWalletData(); }
|
||||
buildSweepCatalog();
|
||||
|
||||
+7
-6
@@ -173,11 +173,11 @@ inline float kSidePanelMinWidth() { return schema::UI().drawElement("panels",
|
||||
inline float kSidePanelMaxWidth() { return schema::UI().drawElement("panels", "side-panel").getFloat("max-width", 450.0f) * dpiScale(); }
|
||||
inline float kSidePanelWidthRatio() { return schema::UI().drawElement("panels", "side-panel").getFloat("width-ratio", 0.4f); }
|
||||
|
||||
// Overall content-column cap: the max width a tab's content should occupy before it is centered in wider
|
||||
// windows. Prevents cards/forms/tables (which all derive their size from the content child's width) from
|
||||
// stretching edge-to-edge at wide/ultrawide widths. <= 0 disables (fill full width). Tunable via ui.toml
|
||||
// [layout] content-max-width; default is generous so data-dense screens stay comfortable.
|
||||
inline float kContentMaxWidth() { return schema::UI().drawElement("layout", "content-max-width").sizeOr(1600.0f) * dpiScale(); }
|
||||
// Overall content-column cap: the max width a tab's content occupies before it is centered in wider
|
||||
// windows. <= 0 disables the cap so tab content fills ALL available horizontal width (the default —
|
||||
// requested so large windows don't leave a big empty gutter on the right). Set a positive
|
||||
// ui.toml [layout] content-max-width to re-enable a centered readable column.
|
||||
inline float kContentMaxWidth() { return schema::UI().drawElement("layout", "content-max-width").sizeOr(0.0f) * dpiScale(); }
|
||||
|
||||
// Shared compose-card envelope for the Send + Receive tabs (and any tab wanting the same box): fill the
|
||||
// available column up to the content-max-width cap, then center the leftover as margin. Both tabs MUST
|
||||
@@ -186,7 +186,8 @@ inline float kContentMaxWidth() { return schema::UI().drawElement("layout", "con
|
||||
// Receive on any window wider than ~860dp. Returns {width, offsetX} in the same units as availW.
|
||||
struct CardBox { float width; float offsetX; };
|
||||
inline CardBox mainComposeCardBox(float availW) {
|
||||
float w = std::min(availW, kContentMaxWidth());
|
||||
float cap = kContentMaxWidth();
|
||||
float w = (cap > 0.0f) ? std::min(availW, cap) : availW; // cap <= 0 -> fill full width
|
||||
return CardBox{ w, std::max(0.0f, (availW - w) * 0.5f) };
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ inline float ActionButtonWidth(const char* label, const char* icon, float minWid
|
||||
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 padX = 9.0f * dp, gap = 6.0f * dp; // mockup .btn padding: 9px horizontal
|
||||
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;
|
||||
@@ -133,19 +133,21 @@ inline bool ActionButton(const char* id, const char* label, const char* icon, Ac
|
||||
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 round = 7.0f * dp; // mockup .btn radius: 7px (softer than the global 4px frame)
|
||||
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);
|
||||
// Mockup .btn.acc: a dark accent-tinted chip with accent TEXT — not a bright filled button.
|
||||
bg = WithAlpha(Primary(), hov ? 52 : 38);
|
||||
border = WithAlpha(Primary(), hov ? 150 : 110);
|
||||
fg = Primary();
|
||||
break;
|
||||
case ActionTier::Secondary:
|
||||
bg = WithAlpha(OnSurface(), hov ? 26 : 16);
|
||||
border = WithAlpha(OnSurface(), 40);
|
||||
bg = WithAlpha(OnSurface(), hov ? 30 : 20);
|
||||
border = WithAlpha(OnSurface(), 48);
|
||||
glass = true;
|
||||
fg = OnSurface();
|
||||
break;
|
||||
|
||||
+762
-792
File diff suppressed because it is too large
Load Diff
+7
-5
@@ -538,11 +538,6 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
|
||||
float exitRelY = curY + bottomPadding;
|
||||
float panelH = exitRelY + stripH;
|
||||
|
||||
// Vertical centering — offset so panel is centered in the child window
|
||||
float centerOffset = std::max(glassMarginY, (contentHeight - panelH) * 0.5f);
|
||||
if (centerOffset + panelH > contentHeight)
|
||||
centerOffset = std::max(0.0f, contentHeight - panelH);
|
||||
|
||||
// ===================================================================
|
||||
// PASS 2: Render using computed positions
|
||||
// ===================================================================
|
||||
@@ -552,6 +547,13 @@ inline bool RenderSidebar(NavPage& current, float sidebarWidth, float contentHei
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
ImVec2 wp = ImGui::GetWindowPos();
|
||||
|
||||
// Vertical centering — center the panel within the child. app.cpp sizes the child (contentHeight)
|
||||
// to the visible area (child top -> status-bar top) using window-local geometry, so this yields
|
||||
// equal top/bottom gaps at any height on every platform, no viewport dependency.
|
||||
float centerOffset = std::max(glassMarginY, (contentHeight - panelH) * 0.5f);
|
||||
if (centerOffset + panelH > contentHeight)
|
||||
centerOffset = std::max(0.0f, contentHeight - panelH);
|
||||
|
||||
float panelLeft = wp.x + glassMarginL;
|
||||
float panelRight = wp.x + sidebarWidth - glassMarginR;
|
||||
float panelTopY = wp.y + centerOffset;
|
||||
|
||||
@@ -1885,20 +1885,37 @@ void ResetChatTab()
|
||||
s_show_chat_settings = false;
|
||||
}
|
||||
|
||||
void RenderChatSettingsControls(App* app, float contentWidth)
|
||||
void RenderChatSettingsControls(App* app, float contentWidth, bool drawCards)
|
||||
{
|
||||
auto* st = app ? app->settings() : nullptr;
|
||||
if (!st) return;
|
||||
const float dp = Layout::dpiScale();
|
||||
const float ctrlW = 250.0f * dp; // control column width (fits a 3-segment control comfortably)
|
||||
const float rowGap = 5.0f * dp;
|
||||
const float rowGap = 10.0f * dp;
|
||||
|
||||
// Optionally paint two glass cards (Appearance | Messaging) around our own two columns so the
|
||||
// Settings tab matches the mockup's card-per-group layout. The chat modal passes drawCards=false
|
||||
// and keeps its plain single-surface layout — the controls themselves are identical either way.
|
||||
ImDrawList* cardDL = ImGui::GetWindowDrawList();
|
||||
const float cardPad = drawCards ? Layout::cardInnerPadding() : 0.0f;
|
||||
material::GlassPanelSpec cardSpec; cardSpec.rounding = Layout::glassRounding();
|
||||
float cardTopScr = 0.0f, cardBaseXScr = 0.0f, cardLeftBotScr = 0.0f;
|
||||
if (drawCards) {
|
||||
cardTopScr = ImGui::GetCursorScreenPos().y;
|
||||
cardBaseXScr = ImGui::GetCursorScreenPos().x;
|
||||
cardDL->ChannelsSplit(2);
|
||||
cardDL->ChannelsSetCurrent(1);
|
||||
ImGui::SetCursorScreenPos(ImVec2(cardBaseXScr, cardTopScr + cardPad));
|
||||
ImGui::Indent(cardPad);
|
||||
}
|
||||
// Right-align controls to the row's true right edge. The Settings tab renders us inside a GlassCard
|
||||
// whose content region isn't narrowed to the card padding, so it passes an explicit contentWidth;
|
||||
// the chat modal's dialog content region is correct, so it passes 0 (auto).
|
||||
// leftX/rowW define the current column the rows lay out in; retargeted below
|
||||
// to split Appearance | Messaging into two columns when the card is wide.
|
||||
float leftX = ImGui::GetCursorPosX();
|
||||
float rowW = (contentWidth > 0.0f) ? contentWidth : ImGui::GetContentRegionAvail().x;
|
||||
float rowW = drawCards ? (contentWidth - 2.0f * cardPad)
|
||||
: ((contentWidth > 0.0f) ? contentWidth : ImGui::GetContentRegionAvail().x);
|
||||
|
||||
// Label left, control right-aligned within [leftX, leftX+rowW]. Leaves the cursor at the control origin.
|
||||
auto beginRow = [&](const char* label) {
|
||||
@@ -1966,7 +1983,7 @@ void RenderChatSettingsControls(App* app, float contentWidth)
|
||||
// Two internal columns when the card is wide enough: Appearance on the left,
|
||||
// Messaging on the right — fills the width and roughly halves the height.
|
||||
// (Mirrors the Node & Security card.) Narrow (the chat modal) stays single-column.
|
||||
const float chatColGap = 24.0f * dp;
|
||||
const float chatColGap = drawCards ? (Layout::cardGap() + 2.0f * cardPad) : (24.0f * dp);
|
||||
const bool chatTwoCol = rowW > 760.0f * dp;
|
||||
const float chatColW = chatTwoCol ? (rowW - chatColGap) * 0.5f : rowW;
|
||||
const float chatBaseLeftX = leftX;
|
||||
@@ -2023,6 +2040,7 @@ void RenderChatSettingsControls(App* app, float contentWidth)
|
||||
// line-start holds the column; retarget leftX so controls right-align in it).
|
||||
if (chatTwoCol) {
|
||||
chatLeftBottomY = ImGui::GetCursorPosY();
|
||||
cardLeftBotScr = ImGui::GetCursorScreenPos().y; // left column bottom (screen), for its card panel
|
||||
ImGui::SetCursorPosY(chatTopY);
|
||||
ImGui::Indent(chatColW + chatColGap);
|
||||
leftX = chatBaseLeftX + chatColW + chatColGap;
|
||||
@@ -2056,12 +2074,41 @@ void RenderChatSettingsControls(App* app, float contentWidth)
|
||||
}
|
||||
|
||||
// Close the two-column band: un-indent and drop below the taller column.
|
||||
const float cardRightBotScr = ImGui::GetCursorScreenPos().y; // right (or only) column bottom, screen
|
||||
if (chatTwoCol) {
|
||||
ImGui::Unindent(chatColW + chatColGap);
|
||||
const float chatRightBottomY = ImGui::GetCursorPosY();
|
||||
ImGui::SetCursorPosX(chatBaseLeftX);
|
||||
ImGui::SetCursorPosY(std::max(chatLeftBottomY, chatRightBottomY));
|
||||
}
|
||||
|
||||
// Paint the glass card(s) behind the content, then merge the channels.
|
||||
if (drawCards) {
|
||||
ImGui::Unindent(cardPad);
|
||||
cardDL->ChannelsSetCurrent(0);
|
||||
const float cardW = (contentWidth - Layout::cardGap()) * 0.5f;
|
||||
if (chatTwoCol) {
|
||||
const float eqBot = std::max(cardLeftBotScr, cardRightBotScr); // equal-height cards (mockup grid stretch)
|
||||
material::DrawGlassPanel(cardDL, ImVec2(cardBaseXScr, cardTopScr),
|
||||
ImVec2(cardBaseXScr + cardW, eqBot + cardPad), cardSpec);
|
||||
material::DrawGlassPanel(cardDL, ImVec2(cardBaseXScr + cardW + Layout::cardGap(), cardTopScr),
|
||||
ImVec2(cardBaseXScr + contentWidth, eqBot + cardPad), cardSpec);
|
||||
} else {
|
||||
material::DrawGlassPanel(cardDL, ImVec2(cardBaseXScr, cardTopScr),
|
||||
ImVec2(cardBaseXScr + contentWidth, cardRightBotScr + cardPad), cardSpec);
|
||||
}
|
||||
cardDL->ChannelsMerge();
|
||||
const float botScr = chatTwoCol ? std::max(cardLeftBotScr, cardRightBotScr) : cardRightBotScr;
|
||||
// Reserve the card footprint with a Dummy so the parent scroll region grows to include it
|
||||
// (a bare SetCursorScreenPos past content warns in ImGui).
|
||||
ImGui::SetCursorScreenPos(ImVec2(cardBaseXScr, cardTopScr));
|
||||
ImGui::Dummy(ImVec2(contentWidth, (botScr - cardTopScr) + cardPad));
|
||||
|
||||
// Live conversation preview below the two cards (Settings tab only — the chat modal renders
|
||||
// its own preview column beside these controls, so it passes drawCards=false and skips this).
|
||||
ImGui::Dummy(ImVec2(0.0f, Layout::spacingMd()));
|
||||
RenderChatSettingsPreview(app, contentWidth);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -34,7 +34,7 @@ void RenderChatTab(App* app);
|
||||
* 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);
|
||||
void RenderChatSettingsControls(App* app, float contentWidth = 0.0f, bool drawCards = false);
|
||||
|
||||
/**
|
||||
* @brief Securely wipe the Chat tab's UI-local state (composer / new-conversation
|
||||
|
||||
+37
-20
@@ -460,15 +460,23 @@ void I18n::loadBuiltinEnglish()
|
||||
// Settings sections
|
||||
strings_["appearance"] = "APPEARANCE";
|
||||
strings_["theme_language"] = "THEME & LANGUAGE";
|
||||
strings_["scale_effects"] = "SCALE & EFFECTS";
|
||||
strings_["advanced_effects"] = "Advanced Effects...";
|
||||
strings_["tools_actions"] = "Tools & Actions...";
|
||||
strings_["tools_actions_hdr"] = "TOOLS & ACTIONS";
|
||||
strings_["wallet_options_hdr"] = "OPTIONS";
|
||||
strings_["wallet_diagnostics_hdr"] = "DIAGNOSTICS";
|
||||
strings_["wallet"] = "WALLET";
|
||||
strings_["node_security"] = "NODE & SECURITY";
|
||||
strings_["node"] = "NODE";
|
||||
strings_["security"] = "SECURITY";
|
||||
strings_["explorer_section"] = "EXPLORER";
|
||||
strings_["explorer_urls_hdr"] = "URLS";
|
||||
strings_["about"] = "About";
|
||||
strings_["backup_data"] = "BACKUP & DATA";
|
||||
strings_["backup_col_import"] = "IMPORT & RESTORE";
|
||||
strings_["backup_col_backup"] = "BACKUP";
|
||||
strings_["backup_col_export"] = "EXPORT";
|
||||
strings_["balance_layout"] = "Balance Layout";
|
||||
strings_["low_spec_mode"] = "Low-spec mode";
|
||||
strings_["simple_background"] = "Simple background";
|
||||
@@ -496,9 +504,11 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["screenshot_sweep"] = "Run screenshot sweep";
|
||||
strings_["screenshot_sweep_full"] = "Full UI sweep";
|
||||
strings_["screenshot_open_dir"] = "Open location";
|
||||
strings_["sweep_current_theme_only"] = "Current theme only";
|
||||
strings_["tt_sweep_current_theme_only"] = "Sweep only the active theme instead of cycling every theme";
|
||||
strings_["screenshot_sweep_desc"] = "Cycles every theme across every tab and saves a screenshot of each into per-tab subfolders under the config directory's screenshots folder (overwriting the previous sweep). Runs for a few seconds.";
|
||||
strings_["mine_when_idle"] = "Mine when idle";
|
||||
strings_["setup_wizard"] = "Run Setup Wizard...";
|
||||
strings_["setup_wizard"] = "Run Setup Wizard…";
|
||||
|
||||
// RPC / Explorer settings
|
||||
strings_["rpc_connection"] = "RPC Connection...";
|
||||
@@ -512,21 +522,21 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["fetch_prices"] = "Fetch price data from CoinGecko";
|
||||
strings_["block_explorer"] = "Block Explorer";
|
||||
strings_["test_connection"] = "Test Connection";
|
||||
strings_["rescan"] = "Rescan Blockchain";
|
||||
strings_["rescan"] = "Rescan";
|
||||
|
||||
// Settings: buttons
|
||||
strings_["settings_address_book"] = "Address Book...";
|
||||
strings_["settings_validate_address"] = "Validate Address...";
|
||||
strings_["settings_request_payment"] = "Request Payment...";
|
||||
strings_["settings_shield_mining"] = "Shield Mining...";
|
||||
strings_["settings_merge_to_address"] = "Merge to Address...";
|
||||
strings_["settings_address_book"] = "Address Book…";
|
||||
strings_["settings_validate_address"] = "Validate Address…";
|
||||
strings_["settings_request_payment"] = "Request Payment…";
|
||||
strings_["settings_shield_mining"] = "Shield Mining…";
|
||||
strings_["settings_merge_to_address"] = "Merge to Address…";
|
||||
strings_["settings_clear_ztx"] = "Clear Z-Tx History";
|
||||
strings_["settings_import_key"] = "Import Private Key...";
|
||||
strings_["settings_import_viewkey"] = "Import Viewing Key...";
|
||||
strings_["settings_export_key"] = "Export Key...";
|
||||
strings_["settings_export_all"] = "Export All...";
|
||||
strings_["settings_backup"] = "Backup...";
|
||||
strings_["settings_export_csv"] = "Export CSV...";
|
||||
strings_["settings_import_key"] = "Import Private Key…";
|
||||
strings_["settings_import_viewkey"] = "Import Viewing Key…";
|
||||
strings_["settings_export_key"] = "Export Key…";
|
||||
strings_["settings_export_all"] = "Export All…";
|
||||
strings_["settings_backup"] = "Backup…";
|
||||
strings_["settings_export_csv"] = "Export CSV…";
|
||||
strings_["settings_encrypt_wallet"] = "Encrypt Wallet";
|
||||
strings_["settings_change_passphrase"] = "Change Passphrase";
|
||||
strings_["settings_lock_now"] = "Lock Now";
|
||||
@@ -655,8 +665,8 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["wiz_pin_confirm"] = "Confirm PIN:";
|
||||
strings_["wiz_pin_invalid"] = "PIN must be 4-8 digits";
|
||||
strings_["wiz_pin_mismatch"] = "PINs do not match";
|
||||
strings_["settings_data_dir"] = "Data Dir:";
|
||||
strings_["settings_wallet_size_label"] = "Wallet Size:";
|
||||
strings_["settings_data_dir"] = "Data Dir";
|
||||
strings_["settings_wallet_size_label"] = "Wallet Size";
|
||||
strings_["settings_debug_changed"] = "Debug categories changed \xe2\x80\x94 restart daemon to apply";
|
||||
strings_["settings_auto_detected"] = "Auto-detected from DRAGONX.conf";
|
||||
strings_["settings_visual_effects"] = "Visual Effects";
|
||||
@@ -670,7 +680,7 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["settings_wallet_info"] = "Wallet Info";
|
||||
strings_["settings_block_explorer_urls"] = "Block Explorer URLs";
|
||||
strings_["settings_configure_explorer"] = "Configure external block explorer links";
|
||||
strings_["settings_auto_lock"] = "AUTO-LOCK";
|
||||
strings_["settings_auto_lock"] = "Auto-lock";
|
||||
strings_["timeout_off"] = "Off";
|
||||
strings_["timeout_1min"] = "1 min";
|
||||
strings_["timeout_5min"] = "5 min";
|
||||
@@ -700,9 +710,9 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["tt_scanline"] = "CRT scanline effect in console";
|
||||
strings_["tt_theme_effects"] = "Shimmer, glow, hue-cycling per theme";
|
||||
strings_["tt_animate_avatars"] = "Play animated (GIF / WebP) contact avatars; off shows the first frame only";
|
||||
strings_["tt_blur"] = "Blur amount (0%% = off, 100%% = maximum)";
|
||||
strings_["tt_noise"] = "Grain texture intensity (0%% = off, 100%% = maximum)";
|
||||
strings_["tt_ui_opacity"] = "Card and sidebar opacity (100%% = fully opaque, lower = more see-through)";
|
||||
strings_["tt_blur"] = "Blur amount (0% = off, 100% = maximum)";
|
||||
strings_["tt_noise"] = "Grain texture intensity (0% = off, 100% = maximum)";
|
||||
strings_["tt_ui_opacity"] = "Card and sidebar opacity (100% = fully opaque, lower = more see-through)";
|
||||
strings_["tt_window_opacity"] = "Background opacity (lower = desktop visible through window)";
|
||||
strings_["tt_font_scale"] = "Scale all text and UI (1.0x = default, up to 1.5x). Hotkey: Alt + Scroll Wheel";
|
||||
strings_["tt_custom_theme"] = "Custom theme active";
|
||||
@@ -805,12 +815,14 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["rescan_detecting"] = "Checking which blocks your node has on disk…";
|
||||
strings_["rescan_bootstrapped_msg"] = "Your node was bootstrapped, so blocks below the snapshot aren't on disk and a rescan from genesis would fail. Rescan from a height your snapshot includes to reconcile your wallet's spent balance. Your wallet.dat and chain data are not deleted.";
|
||||
strings_["rescan_from_height"] = "Rescan from block height:";
|
||||
strings_["repair_wallet"] = "Repair Wallet";
|
||||
strings_["repair_wallet"] = "Repair";
|
||||
strings_["tt_repair_wallet"] = "Wipe and rebuild the wallet's transaction records from the blockchain (fixes notes that fail to send after a rescan)";
|
||||
strings_["confirm_repair_wallet_title"] = "Repair Wallet";
|
||||
strings_["confirm_repair_wallet_msg"] = "This restarts the daemon with -zapwallettxes=2: it deletes all of the wallet's transaction and note records, then rebuilds them from the blockchain. Use this when transactions fail to build (\"Invalid sapling spend proof\" / \"shielded requirements not met\") even after a full rescan. It takes a long time and the wallet stays offline until it finishes.";
|
||||
strings_["confirm_repair_wallet_safe"] = "Your keys, addresses and balance are preserved — only the cached transaction records are rebuilt.";
|
||||
strings_["daemon_binary"] = "Daemon binary";
|
||||
strings_["daemon_updates_label"] = "UPDATES";
|
||||
strings_["daemon_maintenance_label"] = "MAINTENANCE";
|
||||
strings_["daemon_installed"] = "Installed";
|
||||
strings_["daemon_bundled"] = "Bundled";
|
||||
strings_["daemon_not_installed"] = "not installed";
|
||||
@@ -818,6 +830,10 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["daemon_status_match"] = "Installed binary matches the bundled version.";
|
||||
strings_["daemon_status_differ"] = "Installed binary differs from the bundled version.";
|
||||
strings_["daemon_status_missing"] = "No daemon installed — install the bundled version.";
|
||||
// Compact status shown right-aligned on the DAEMON BINARY heading row.
|
||||
strings_["daemon_status_ok"] = "Up to date";
|
||||
strings_["daemon_status_diff"] = "Version differs";
|
||||
strings_["daemon_status_none"] = "Not installed";
|
||||
strings_["daemon_install_bundled"] = "Install bundled";
|
||||
strings_["tt_daemon_install_bundled"] = "Stop the node, overwrite the installed dragonxd with the version bundled in this wallet build, then restart";
|
||||
strings_["confirm_reinstall_daemon_title"] = "Install Bundled Daemon";
|
||||
@@ -1500,6 +1516,7 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["about_chain"] = "Chain:";
|
||||
strings_["about_connections"] = "Connections:";
|
||||
strings_["about_credits"] = "Credits";
|
||||
strings_["about_source"] = "Source";
|
||||
strings_["about_daemon"] = "Daemon:";
|
||||
strings_["about_debug"] = "Debug";
|
||||
strings_["about_edition"] = "ImGui Edition";
|
||||
|
||||
Reference in New Issue
Block a user