fix(console): RPC command-reference modal audit fixes

- Clicking a command now actually closes the modal. It called
  ImGui::CloseCurrentPopup(), a no-op here (BeginOverlayDialog is Begin/
  BeginChild, not an ImGui popup), so the picker stayed open covering the input
  and the insert looked like it did nothing. Set show_commands_popup_=false.
- Search filter is now a member cleared when the modal opens, so it isn't stale
  on reopen after an outside-click / command-click dismiss (was reset only by
  the Close button).
- Esc dismisses the modal (this overlay has no built-in Esc handling), and the
  search box auto-focuses on open so the user can type immediately.
- Scale the 2-vs-3 column breakpoint (cmd-min-width) by dpiScale() — it was a
  logical-px threshold compared against a physical-px available width.
- Give each category's table a unique ImGui id (##cmdsN) instead of sharing
  "##cmds", and translate the 7 category names (Control/Network/... — command
  descriptions stay English) into all 8 languages; CJK subset rebuilt (+2).

(Investigated but rejected: the command-name blue was flagged as low-contrast
on light skins, but the modal card is a dark glass panel on every theme, so the
original color is correct — verified by rendering marble/light/color-pop-light.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-12 19:25:51 -05:00
parent 675d434958
commit ab4dd370c0
12 changed files with 103 additions and 11 deletions

Binary file not shown.

View File

@@ -185,6 +185,13 @@
"console_auto_scroll": "Automatisch scrollen", "console_auto_scroll": "Automatisch scrollen",
"console_available_commands": "Verfügbare Befehle:", "console_available_commands": "Verfügbare Befehle:",
"console_capturing_output": "Erfasse Daemon-Ausgabe...", "console_capturing_output": "Erfasse Daemon-Ausgabe...",
"console_cat_blockchain": "Blockchain",
"console_cat_control": "Steuerung",
"console_cat_mining": "Mining",
"console_cat_network": "Netzwerk",
"console_cat_raw_transactions": "Rohtransaktionen",
"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",

View File

@@ -185,6 +185,13 @@
"console_auto_scroll": "Auto-desplazamiento", "console_auto_scroll": "Auto-desplazamiento",
"console_available_commands": "Comandos disponibles:", "console_available_commands": "Comandos disponibles:",
"console_capturing_output": "Capturando salida del daemon...", "console_capturing_output": "Capturando salida del daemon...",
"console_cat_blockchain": "Blockchain",
"console_cat_control": "Control",
"console_cat_mining": "Minería",
"console_cat_network": "Red",
"console_cat_raw_transactions": "Transacciones sin procesar",
"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",

View File

@@ -185,6 +185,13 @@
"console_auto_scroll": "Défilement auto", "console_auto_scroll": "Défilement auto",
"console_available_commands": "Commandes disponibles :", "console_available_commands": "Commandes disponibles :",
"console_capturing_output": "Capture de la sortie du daemon...", "console_capturing_output": "Capture de la sortie du daemon...",
"console_cat_blockchain": "Blockchain",
"console_cat_control": "Contrôle",
"console_cat_mining": "Minage",
"console_cat_network": "Réseau",
"console_cat_raw_transactions": "Transactions brutes",
"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",

View File

@@ -185,6 +185,13 @@
"console_auto_scroll": "自動スクロール", "console_auto_scroll": "自動スクロール",
"console_available_commands": "利用可能なコマンド:", "console_available_commands": "利用可能なコマンド:",
"console_capturing_output": "デーモン出力をキャプチャ中...", "console_capturing_output": "デーモン出力をキャプチャ中...",
"console_cat_blockchain": "ブロックチェーン",
"console_cat_control": "制御",
"console_cat_mining": "マイニング",
"console_cat_network": "ネットワーク",
"console_cat_raw_transactions": "生トランザクション",
"console_cat_utility": "ユーティリティ",
"console_cat_wallet": "ウォレット",
"console_clear": "クリア", "console_clear": "クリア",
"console_clear_console": "コンソールをクリア", "console_clear_console": "コンソールをクリア",
"console_cleared": "コンソールをクリアしました", "console_cleared": "コンソールをクリアしました",

View File

@@ -185,6 +185,13 @@
"console_auto_scroll": "자동 스크롤", "console_auto_scroll": "자동 스크롤",
"console_available_commands": "사용 가능한 명령어:", "console_available_commands": "사용 가능한 명령어:",
"console_capturing_output": "데몬 출력 캡처 중...", "console_capturing_output": "데몬 출력 캡처 중...",
"console_cat_blockchain": "블록체인",
"console_cat_control": "제어",
"console_cat_mining": "채굴",
"console_cat_network": "네트워크",
"console_cat_raw_transactions": "원시 트랜잭션",
"console_cat_utility": "유틸리티",
"console_cat_wallet": "지갑",
"console_clear": "지우기", "console_clear": "지우기",
"console_clear_console": "콘솔 지우기", "console_clear_console": "콘솔 지우기",
"console_cleared": "콘솔이 지워졌습니다", "console_cleared": "콘솔이 지워졌습니다",

View File

@@ -185,6 +185,13 @@
"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_capturing_output": "Capturando saída do daemon...", "console_capturing_output": "Capturando saída do daemon...",
"console_cat_blockchain": "Blockchain",
"console_cat_control": "Controle",
"console_cat_mining": "Mineração",
"console_cat_network": "Rede",
"console_cat_raw_transactions": "Transações brutas",
"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",

View File

@@ -185,6 +185,13 @@
"console_auto_scroll": "Авто-прокрутка", "console_auto_scroll": "Авто-прокрутка",
"console_available_commands": "Доступные команды:", "console_available_commands": "Доступные команды:",
"console_capturing_output": "Захват вывода daemon...", "console_capturing_output": "Захват вывода daemon...",
"console_cat_blockchain": "Блокчейн",
"console_cat_control": "Управление",
"console_cat_mining": "Майнинг",
"console_cat_network": "Сеть",
"console_cat_raw_transactions": "Сырые транзакции",
"console_cat_utility": "Утилиты",
"console_cat_wallet": "Кошелёк",
"console_clear": "Очистить", "console_clear": "Очистить",
"console_clear_console": "Очистить консоль", "console_clear_console": "Очистить консоль",
"console_cleared": "Консоль очищена", "console_cleared": "Консоль очищена",

View File

@@ -185,6 +185,13 @@
"console_auto_scroll": "自动滚动", "console_auto_scroll": "自动滚动",
"console_available_commands": "可用命令:", "console_available_commands": "可用命令:",
"console_capturing_output": "正在捕获守护进程输出...", "console_capturing_output": "正在捕获守护进程输出...",
"console_cat_blockchain": "区块链",
"console_cat_control": "控制",
"console_cat_mining": "挖矿",
"console_cat_network": "网络",
"console_cat_raw_transactions": "原始交易",
"console_cat_utility": "实用工具",
"console_cat_wallet": "钱包",
"console_clear": "清除", "console_clear": "清除",
"console_clear_console": "清除控制台", "console_clear_console": "清除控制台",
"console_cleared": "控制台已清除", "console_cleared": "控制台已清除",

View File

@@ -536,6 +536,7 @@ void ConsoleTab::renderToolbar(ConsoleCommandExecutor& exec)
// Commands reference button (full-node RPC reference only) // Commands reference button (full-node RPC reference only)
if (exec.hasRpcReference()) { if (exec.hasRpcReference()) {
if (TactileButton(TR("console_commands"), ImVec2(0, 0), schema::UI().resolveFont("button"))) { if (TactileButton(TR("console_commands"), ImVec2(0, 0), schema::UI().resolveFont("button"))) {
command_search_[0] = '\0'; // fresh search each open (dismiss paths don't all reset it)
show_commands_popup_ = true; show_commands_popup_ = true;
} }
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
@@ -1487,6 +1488,20 @@ void drawConsoleCommandParams(const char* params)
} }
} }
} }
// Translate a command-category name from the static reference tables (English) for display. The
// per-command descriptions stay in English (technical RPC docs); only the 7 category labels are i18n'd.
const char* consoleCategoryLabel(const char* name)
{
if (!std::strcmp(name, "Control")) return TR("console_cat_control");
if (!std::strcmp(name, "Network")) return TR("console_cat_network");
if (!std::strcmp(name, "Blockchain")) return TR("console_cat_blockchain");
if (!std::strcmp(name, "Mining")) return TR("console_cat_mining");
if (!std::strcmp(name, "Wallet")) return TR("console_cat_wallet");
if (!std::strcmp(name, "Raw Transactions")) return TR("console_cat_raw_transactions");
if (!std::strcmp(name, "Utility")) return TR("console_cat_utility");
return name;
}
} // namespace } // namespace
void ConsoleTab::renderCommandsPopup() void ConsoleTab::renderCommandsPopup()
@@ -1506,16 +1521,19 @@ void ConsoleTab::renderCommandsPopup()
if (!material::BeginOverlayDialog(ov)) { if (!material::BeginOverlayDialog(ov)) {
return; return;
} }
// Esc dismisses — this overlay is a plain Begin/BeginChild, not an ImGui popup, so it has no
// built-in Esc handling (matches the other editor overlays).
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) show_commands_popup_ = false;
// Search filter // Search filter — auto-focus on open so the user can type immediately (like the console input).
static char cmdFilter[128] = {0};
ImGui::SetNextItemWidth(-1); ImGui::SetNextItemWidth(-1);
ImGui::InputTextWithHint("##CmdSearch", TR("console_search_commands"), cmdFilter, sizeof(cmdFilter)); if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere();
ImGui::InputTextWithHint("##CmdSearch", TR("console_search_commands"), command_search_, sizeof(command_search_));
ImGui::Dummy(ImVec2(0, Layout::spacingXs())); ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
const auto& categories = consoleCommandCategories(); const auto& categories = consoleCommandCategories();
std::string filter(cmdFilter); std::string filter(command_search_);
std::transform(filter.begin(), filter.end(), filter.begin(), ::tolower); std::transform(filter.begin(), filter.end(), filter.begin(), ::tolower);
ImGui::BeginChild("CmdListScroll", ImVec2(0, -ImGui::GetFrameHeightWithSpacing() - Layout::spacingXs()), ImGui::BeginChild("CmdListScroll", ImVec2(0, -ImGui::GetFrameHeightWithSpacing() - Layout::spacingXs()),
@@ -1523,7 +1541,10 @@ void ConsoleTab::renderCommandsPopup()
ImGui::PushFont(Type().caption()); ImGui::PushFont(Type().caption());
float cmdMinWidth = schema::UI().drawElement("tabs.console", "cmd-min-width").sizeOr(500.0f); // cmd-min-width is authored in logical px; popupInnerW (GetContentRegionAvail) is physical/DPI-
// scaled, so scale the breakpoint to match — otherwise the 2-vs-3 column choice trips at the
// wrong width on HiDPI.
float cmdMinWidth = schema::UI().drawElement("tabs.console", "cmd-min-width").sizeOr(500.0f) * Layout::dpiScale();
float popupInnerW = ImGui::GetContentRegionAvail().x; float popupInnerW = ImGui::GetContentRegionAvail().x;
bool showParams = popupInnerW >= cmdMinWidth; bool showParams = popupInnerW >= cmdMinWidth;
int catIdx = 0; int catIdx = 0;
@@ -1550,11 +1571,12 @@ void ConsoleTab::renderCommandsPopup()
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Primary())); ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Primary()));
// Show match count badge when filtering // Show match count badge when filtering
const char* catLabel = consoleCategoryLabel(cat.name);
char headerLabel[128]; char headerLabel[128];
if (!filter.empty()) { if (!filter.empty()) {
snprintf(headerLabel, sizeof(headerLabel), "%s (%d)", cat.name, matchCount); snprintf(headerLabel, sizeof(headerLabel), "%s (%d)", catLabel, matchCount);
} else { } else {
snprintf(headerLabel, sizeof(headerLabel), "%s", cat.name); snprintf(headerLabel, sizeof(headerLabel), "%s", catLabel);
} }
bool open = ImGui::CollapsingHeader(headerLabel, headerFlags); bool open = ImGui::CollapsingHeader(headerLabel, headerFlags);
ImGui::PopStyleColor(); ImGui::PopStyleColor();
@@ -1564,7 +1586,9 @@ void ConsoleTab::renderCommandsPopup()
float nameColW = schema::UI().drawElement("tabs.console", "cmd-name-col-width").size * Layout::hScale(); float nameColW = schema::UI().drawElement("tabs.console", "cmd-name-col-width").size * Layout::hScale();
float paramsColW = schema::UI().drawElement("tabs.console", "cmd-params-col-width").size * Layout::hScale(); float paramsColW = schema::UI().drawElement("tabs.console", "cmd-params-col-width").size * Layout::hScale();
int numCols = showParams ? 3 : 2; int numCols = showParams ? 3 : 2;
if (ImGui::BeginTable("##cmds", numCols, ImGuiTableFlags_None)) { char tableId[24];
snprintf(tableId, sizeof(tableId), "##cmds%d", catIdx); // unique id per category table
if (ImGui::BeginTable(tableId, numCols, ImGuiTableFlags_None)) {
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, nameColW); ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, nameColW);
if (showParams) if (showParams)
ImGui::TableSetupColumn("Parameters", ImGuiTableColumnFlags_WidthFixed, paramsColW); ImGui::TableSetupColumn("Parameters", ImGuiTableColumnFlags_WidthFixed, paramsColW);
@@ -1577,6 +1601,8 @@ void ConsoleTab::renderCommandsPopup()
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
// Link-like blue for the command name. Readable on all skins: the RPC-reference
// modal card is a dark glass panel regardless of theme, so no light-floor is needed.
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::PushStyleColor(ImGuiCol_Text,
ImGui::ColorConvertU32ToFloat4(IM_COL32(100, 180, 255, 255))); ImGui::ColorConvertU32ToFloat4(IM_COL32(100, 180, 255, 255)));
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0, 0, 0, 0)); ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0, 0, 0, 0));
@@ -1590,7 +1616,9 @@ void ConsoleTab::renderCommandsPopup()
strncpy(input_buffer_, cmd.name, sizeof(input_buffer_) - 1); strncpy(input_buffer_, cmd.name, sizeof(input_buffer_) - 1);
input_buffer_[sizeof(input_buffer_) - 1] = '\0'; input_buffer_[sizeof(input_buffer_) - 1] = '\0';
} }
ImGui::CloseCurrentPopup(); // This overlay isn't an ImGui popup, so CloseCurrentPopup() is a no-op here —
// close it the way the framework honors, revealing the inserted command.
show_commands_popup_ = false;
} }
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
@@ -1621,9 +1649,9 @@ void ConsoleTab::renderCommandsPopup()
ImGui::Dummy(ImVec2(0, Layout::spacingXs())); ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
// Close button // Close button (the filter is also reset when the modal next opens).
if (material::TactileButton(TR("console_close"), ImVec2(-1, 0))) { if (material::TactileButton(TR("console_close"), ImVec2(-1, 0))) {
cmdFilter[0] = '\0'; command_search_[0] = '\0';
show_commands_popup_ = false; show_commands_popup_ = false;
} }

View File

@@ -185,6 +185,7 @@ private:
// Commands popup // Commands popup
bool show_commands_popup_ = false; bool show_commands_popup_ = false;
char command_search_[128] = {0}; // RPC-reference search filter (cleared when the modal opens)
}; };
} // namespace ui } // namespace ui

View File

@@ -1294,6 +1294,13 @@ void I18n::loadBuiltinEnglish()
strings_["console_toggle_text_color"] = "Toggle line text colors"; strings_["console_toggle_text_color"] = "Toggle line text colors";
strings_["console_accents"] = "Color accents"; strings_["console_accents"] = "Color accents";
strings_["console_text_colors"] = "Text colors"; strings_["console_text_colors"] = "Text colors";
strings_["console_cat_control"] = "Control";
strings_["console_cat_network"] = "Network";
strings_["console_cat_blockchain"] = "Blockchain";
strings_["console_cat_mining"] = "Mining";
strings_["console_cat_wallet"] = "Wallet";
strings_["console_cat_raw_transactions"] = "Raw Transactions";
strings_["console_cat_utility"] = "Utility";
// --- Export All Keys Dialog --- // --- Export All Keys Dialog ---
strings_["export_keys_btn"] = "Export Keys"; strings_["export_keys_btn"] = "Export Keys";