feat(console): redesign the RPC reference as a novice-friendly command explorer
Replace the flat, terse command-reference table with a guided two-pane explorer built for users who don't know the RPC commands: - Search by name OR task: keyword synonyms let "balance" find getbalance, "send money" find sendtoaddress; results are ranked (name-prefix > name > keyword > desc). - Two panes (mirrors the portfolio editor): a category-grouped master list (mono names, a warning dot on consequential commands) + a rich detail pane with a plain-language explanation, a per-parameter type breakdown (string / number / json, optional dimmed), and a concrete example. - Insert into console (fills the input with a template to review + run) and, for no-parameter commands, Insert & run — with a confirm for destructive ones. Deferred via pending_submit_ so the modal needs no executor. - Keyboard: type to filter, Up/Down to move, Enter to insert; auto-focus and auto-select the top result. Data model: ConsoleCommandEntry gains details/example/keywords/destructive (C++17 defaults, so the ~60 un-enriched entries are untouched); ~20 common commands enriched and 18 consequential ones flagged. Command docs stay English (technical); the UI chrome + 7 category names are translated into all 8 languages (CJK subset rebuilt, +3 glyphs). Verified: two-pane renders on dark + light skins and at font_scale 1.5; detail pane shows explanation/params/example; danger dots + safety badge on destructive commands. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
@@ -229,6 +229,19 @@
|
|||||||
"console_no_daemon": "Kein Daemon",
|
"console_no_daemon": "Kein Daemon",
|
||||||
"console_not_connected": "Fehler: Nicht mit Daemon verbunden",
|
"console_not_connected": "Fehler: Nicht mit Daemon verbunden",
|
||||||
"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_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_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",
|
||||||
|
|||||||
@@ -229,6 +229,19 @@
|
|||||||
"console_no_daemon": "Sin daemon",
|
"console_no_daemon": "Sin daemon",
|
||||||
"console_not_connected": "Error: No conectado al daemon",
|
"console_not_connected": "Error: No conectado al daemon",
|
||||||
"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_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_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",
|
||||||
|
|||||||
@@ -229,6 +229,19 @@
|
|||||||
"console_no_daemon": "Pas de daemon",
|
"console_no_daemon": "Pas de daemon",
|
||||||
"console_not_connected": "Erreur : Non connecté au daemon",
|
"console_not_connected": "Erreur : Non connecté au daemon",
|
||||||
"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_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_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",
|
||||||
|
|||||||
@@ -229,6 +229,19 @@
|
|||||||
"console_no_daemon": "デーモンなし",
|
"console_no_daemon": "デーモンなし",
|
||||||
"console_not_connected": "エラー:デーモンに接続されていません",
|
"console_not_connected": "エラー:デーモンに接続されていません",
|
||||||
"console_quit_note": "ここでは 'quit'/'exit' は不要です — ウィンドウを閉じるだけで構いません。",
|
"console_quit_note": "ここでは 'quit'/'exit' は不要です — ウィンドウを閉じるだけで構いません。",
|
||||||
|
"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_rpc_reference": "RPCコマンドリファレンス",
|
"console_rpc_reference": "RPCコマンドリファレンス",
|
||||||
"console_rpc_trace": "RPC",
|
"console_rpc_trace": "RPC",
|
||||||
"console_scanline": "コンソールスキャンライン",
|
"console_scanline": "コンソールスキャンライン",
|
||||||
|
|||||||
@@ -229,6 +229,19 @@
|
|||||||
"console_no_daemon": "데몬 없음",
|
"console_no_daemon": "데몬 없음",
|
||||||
"console_not_connected": "오류: 데몬에 연결되지 않았습니다",
|
"console_not_connected": "오류: 데몬에 연결되지 않았습니다",
|
||||||
"console_quit_note": "여기서는 'quit'/'exit'가 필요 없습니다 — 그냥 창을 닫으세요.",
|
"console_quit_note": "여기서는 'quit'/'exit'가 필요 없습니다 — 그냥 창을 닫으세요.",
|
||||||
|
"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_rpc_reference": "RPC 명령어 참조",
|
"console_rpc_reference": "RPC 명령어 참조",
|
||||||
"console_rpc_trace": "RPC",
|
"console_rpc_trace": "RPC",
|
||||||
"console_scanline": "콘솔 스캔라인",
|
"console_scanline": "콘솔 스캔라인",
|
||||||
|
|||||||
@@ -229,6 +229,19 @@
|
|||||||
"console_no_daemon": "Sem daemon",
|
"console_no_daemon": "Sem daemon",
|
||||||
"console_not_connected": "Erro: Não conectado ao daemon",
|
"console_not_connected": "Erro: Não conectado ao daemon",
|
||||||
"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_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_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",
|
||||||
|
|||||||
@@ -229,6 +229,19 @@
|
|||||||
"console_no_daemon": "Нет daemon",
|
"console_no_daemon": "Нет daemon",
|
||||||
"console_not_connected": "Ошибка: Не подключено к daemon",
|
"console_not_connected": "Ошибка: Не подключено к daemon",
|
||||||
"console_quit_note": "Здесь не нужны 'quit'/'exit' — просто закройте окно.",
|
"console_quit_note": "Здесь не нужны 'quit'/'exit' — просто закройте окно.",
|
||||||
|
"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_rpc_reference": "Справочник RPC-команд",
|
"console_rpc_reference": "Справочник RPC-команд",
|
||||||
"console_rpc_trace": "RPC",
|
"console_rpc_trace": "RPC",
|
||||||
"console_scanline": "Скан-линия консоли",
|
"console_scanline": "Скан-линия консоли",
|
||||||
|
|||||||
@@ -229,6 +229,19 @@
|
|||||||
"console_no_daemon": "无守护进程",
|
"console_no_daemon": "无守护进程",
|
||||||
"console_not_connected": "错误:未连接到守护进程",
|
"console_not_connected": "错误:未连接到守护进程",
|
||||||
"console_quit_note": "这里不需要 'quit'/'exit'——直接关闭窗口即可。",
|
"console_quit_note": "这里不需要 'quit'/'exit'——直接关闭窗口即可。",
|
||||||
|
"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_rpc_reference": "RPC 命令参考",
|
"console_rpc_reference": "RPC 命令参考",
|
||||||
"console_rpc_trace": "RPC",
|
"console_rpc_trace": "RPC",
|
||||||
"console_scanline": "控制台扫描线",
|
"console_scanline": "控制台扫描线",
|
||||||
|
|||||||
@@ -14,28 +14,50 @@ 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"},
|
||||||
|
{"getpeerinfo", "Get data about each connected peer", "",
|
||||||
|
"Detailed data for every peer you are connected to \xE2\x80\x94 address, version, ping time and bytes exchanged.",
|
||||||
|
"getpeerinfo", "peers connections nodes who connected"},
|
||||||
|
{"getconnectioncount", "Get number of peer connections", "",
|
||||||
|
"How many peers the node is currently connected to. Zero usually means the node is still starting or offline.",
|
||||||
|
"getconnectioncount", "peers connections count how many"},
|
||||||
{"getnettotals", "Get network traffic statistics", ""},
|
{"getnettotals", "Get network traffic statistics", ""},
|
||||||
{"addnode", "Add, remove, or connect to a node", "\"node\" \"add|remove|onetry\""},
|
{"addnode", "Add, remove, or connect to a node", "\"node\" \"add|remove|onetry\""},
|
||||||
{"setban", "Add or remove an IP/subnet from the ban list", "\"ip\" \"add|remove\" [bantime] [absolute]"},
|
{"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", ""},
|
{"listbanned", "List all banned IPs/subnets", ""},
|
||||||
{"clearbanned", "Clear all banned IPs", ""},
|
{"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", ""},
|
{"ping", "Ping all peers to measure round-trip time", ""},
|
||||||
};
|
};
|
||||||
|
|
||||||
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.",
|
||||||
|
"getblockchaininfo", "chain sync height status blockchain progress"},
|
||||||
|
{"getblockcount", "Get number of blocks in longest chain", "",
|
||||||
|
"The height of the longest chain \xE2\x80\x94 how many blocks the node has. Compare with the network to check sync.",
|
||||||
|
"getblockcount", "height blocks how many chain length synced"},
|
||||||
{"getbestblockhash", "Get hash of the tip block", ""},
|
{"getbestblockhash", "Get hash of the tip block", ""},
|
||||||
{"getblock", "Get block data for a given hash or height", "\"hash|height\" [verbosity]"},
|
{"getblock", "Get block data for a given hash or height", "\"hash|height\" [verbosity]",
|
||||||
|
"Returns a block by its hash or height. verbosity 0 = raw hex, 1 = decoded header + txids, 2 = full transactions.",
|
||||||
|
"getblock \"0000000000abc123\" 1", "block details transactions header"},
|
||||||
{"getblockhash", "Get block hash at a given height", "height"},
|
{"getblockhash", "Get block hash at a given height", "height"},
|
||||||
{"getblockheader", "Get block header for a given hash", "\"hash\" [verbose]"},
|
{"getblockheader", "Get block header for a given hash", "\"hash\" [verbose]"},
|
||||||
{"getdifficulty", "Get proof-of-work difficulty", ""},
|
{"getdifficulty", "Get proof-of-work difficulty", ""},
|
||||||
@@ -51,8 +73,12 @@ const ConsoleCommandEntry kBlockchainCommands[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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.",
|
||||||
|
"getmininginfo", "mining hashrate difficulty generate"},
|
||||||
|
{"setgenerate", "Turn mining on or off (true/false [threads])", "generate [genproclimit]",
|
||||||
|
"Turns built-in CPU mining on or off. Pass true or false, and optionally the number of threads (-1 = all cores).",
|
||||||
|
"setgenerate true 4", "mine mining generate cpu start stop", true},
|
||||||
{"getgenerate", "Check if the node is mining", ""},
|
{"getgenerate", "Check if the node is mining", ""},
|
||||||
{"getnetworkhashps", "Get estimated network hash rate", "[blocks] [height]"},
|
{"getnetworkhashps", "Get estimated network hash rate", "[blocks] [height]"},
|
||||||
{"getblocksubsidy", "Get block reward at a given height", "[height]"},
|
{"getblocksubsidy", "Get block reward at a given height", "[height]"},
|
||||||
@@ -61,39 +87,75 @@ const ConsoleCommandEntry kMiningCommands[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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"},
|
||||||
|
{"getnewaddress", "Generate a new transparent address", "",
|
||||||
|
"Creates a fresh transparent (t-) address you can share to receive DRGX.",
|
||||||
|
"getnewaddress", "receive new address deposit transparent create get paid"},
|
||||||
|
{"z_getnewaddress", "Generate a new shielded address", "[\"type\"]",
|
||||||
|
"Creates a fresh shielded (z-) address for receiving DRGX privately.",
|
||||||
|
"z_getnewaddress", "receive new address private shielded create"},
|
||||||
|
{"listaddresses", "List all transparent addresses", "",
|
||||||
|
"Lists the transparent (t-) addresses in your wallet.",
|
||||||
|
"listaddresses", "addresses list my accounts transparent"},
|
||||||
|
{"z_listaddresses", "List all z-addresses", "",
|
||||||
|
"Lists the shielded (z-) addresses in your wallet.",
|
||||||
|
"z_listaddresses", "addresses list my shielded private"},
|
||||||
|
{"sendtoaddress", "Send to a specific address", "\"address\" amount",
|
||||||
|
"Sends DRGX to a transparent address. amount is in DRGX. Double-check the address \xE2\x80\x94 sent transactions cannot be reversed.",
|
||||||
|
"sendtoaddress \"RyourRecipientAddr\" 1.5", "send pay transfer money spend", true},
|
||||||
|
{"z_sendmany", "Send to multiple z/t-addresses with shielded support", "\"fromaddress\" [{\"address\":\"...\",\"amount\":...}]",
|
||||||
|
"Sends DRGX from one address to one or more recipients, with shielded (private) support. Amounts are in DRGX.",
|
||||||
|
"z_sendmany \"RfromAddr\" [{\"address\":\"zs1toAddr\",\"amount\":1.0}]", "send pay private shielded transfer money", true},
|
||||||
{"z_shieldcoinbase", "Shield transparent coinbase funds to a z-address", "\"fromaddress\" \"tozaddress\" [fee] [limit]"},
|
{"z_shieldcoinbase", "Shield transparent coinbase funds to a z-address", "\"fromaddress\" \"tozaddress\" [fee] [limit]"},
|
||||||
{"z_mergetoaddress", "Merge multiple UTXOs/notes to one address", "[\"fromaddress\",...] \"toaddress\" [fee] [limit]"},
|
{"z_mergetoaddress", "Merge multiple UTXOs/notes to one address", "[\"fromaddress\",...] \"toaddress\" [fee] [limit]"},
|
||||||
{"listtransactions", "List recent wallet transactions", "[\"account\"] [count] [from]"},
|
{"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]"},
|
{"listunspent", "List unspent transaction outputs", "[minconf] [maxconf]"},
|
||||||
{"z_listunspent", "List unspent shielded notes", "[minconf] [maxconf]"},
|
{"z_listunspent", "List unspent shielded notes", "[minconf] [maxconf]"},
|
||||||
{"z_getoperationstatus", "Get status of async z operations", "[\"operationid\",...]"},
|
{"z_getoperationstatus", "Get status of async z operations", "[\"operationid\",...]"},
|
||||||
{"z_getoperationresult", "Get result of completed z operations", "[\"operationid\",...]"},
|
{"z_getoperationresult", "Get result of completed z operations", "[\"operationid\",...]"},
|
||||||
{"z_listoperationids", "List all async z operation IDs", ""},
|
{"z_listoperationids", "List all async z operation IDs", ""},
|
||||||
{"getwalletinfo", "Get wallet state info", ""},
|
{"getwalletinfo", "Get wallet state info", "",
|
||||||
{"backupwallet", "Back up wallet to a file", "\"destination\""},
|
"Wallet summary: balances, transaction count, key-pool size and whether the wallet is encrypted and locked.",
|
||||||
{"dumpprivkey", "Dump private key for an address", "\"address\""},
|
"getwalletinfo", "wallet status info balance encrypted locked"},
|
||||||
{"importprivkey", "Import a private key into the wallet", "\"privkey\" [\"label\"] [rescan]"},
|
{"backupwallet", "Back up wallet to a file", "\"destination\"",
|
||||||
{"dumpwallet", "Dump all wallet keys to a file", "\"filename\""},
|
"Saves a copy of your wallet.dat to the given path. Keep the backup somewhere safe and private.",
|
||||||
{"importwallet", "Import wallet from a dump file", "\"filename\""},
|
"backupwallet \"/home/you/drgx-backup.dat\"", "backup save wallet copy protect"},
|
||||||
{"z_exportkey", "Export spending key for a z-address", "\"zaddr\""},
|
{"dumpprivkey", "Dump private key for an address", "\"address\"",
|
||||||
{"z_importkey", "Import a z-address spending key", "\"zkey\" [rescan] [startheight]"},
|
"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\"", "", "", "", true},
|
||||||
|
{"importwallet", "Import wallet from a dump file", "\"filename\"", "", "", "", 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]", "", "", "", true},
|
||||||
{"z_exportviewingkey", "Export viewing key for a z-address", "\"zaddr\""},
|
{"z_exportviewingkey", "Export viewing key for a z-address", "\"zaddr\""},
|
||||||
{"z_importviewingkey", "Import a z-address viewing key", "\"vkey\" [rescan] [startheight]"},
|
{"z_importviewingkey", "Import a z-address viewing key", "\"vkey\" [rescan] [startheight]", "", "", "", true},
|
||||||
{"z_exportwallet", "Export all wallet keys (including z-keys) to file", "\"filename\""},
|
{"z_exportwallet", "Export all wallet keys (including z-keys) to file", "\"filename\"", "", "", "", true},
|
||||||
{"signmessage", "Sign a message with an address key", "\"address\" \"message\""},
|
{"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"},
|
{"settxfee", "Set the transaction fee per kB", "amount"},
|
||||||
{"walletpassphrase", "Unlock the wallet with passphrase", "\"passphrase\" timeout"},
|
{"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", ""},
|
{"walletlock", "Lock the wallet", ""},
|
||||||
{"encryptwallet", "Encrypt the wallet with a passphrase", "\"passphrase\""},
|
{"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[] = {
|
||||||
@@ -107,13 +169,15 @@ const ConsoleCommandEntry kRawTransactionCommands[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ConsoleCommandEntry kUtilityCommands[] = {
|
const ConsoleCommandEntry kUtilityCommands[] = {
|
||||||
{"validateaddress", "Validate a transparent address", "\"address\""},
|
{"validateaddress", "Validate a transparent address", "\"address\"",
|
||||||
|
"Checks whether a transparent address is well-formed and whether it belongs to your wallet.",
|
||||||
|
"validateaddress \"RsomeAddr\"", "validate check address valid mine"},
|
||||||
{"z_validateaddress", "Validate a z-address", "\"zaddr\""},
|
{"z_validateaddress", "Validate a z-address", "\"zaddr\""},
|
||||||
{"estimatefee", "Estimate fee for a transaction", "nblocks"},
|
{"estimatefee", "Estimate fee for a transaction", "nblocks"},
|
||||||
{"verifymessage", "Verify a signed message", "\"address\" \"signature\" \"message\""},
|
{"verifymessage", "Verify a signed message", "\"address\" \"signature\" \"message\""},
|
||||||
{"createmultisig", "Create a multisig address", "nrequired [\"key\",...]"},
|
{"createmultisig", "Create a multisig address", "nrequired [\"key\",...]"},
|
||||||
{"invalidateblock", "Mark a block as invalid", "\"hash\""},
|
{"invalidateblock", "Mark a block as invalid", "\"hash\"", "", "", "", true},
|
||||||
{"reconsiderblock", "Reconsider a previously invalidated block", "\"hash\""},
|
{"reconsiderblock", "Reconsider a previously invalidated block", "\"hash\"", "", "", "", true},
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -284,6 +284,14 @@ void ConsoleTab::render(ConsoleCommandExecutor& exec)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run a command the RPC-reference modal's "Insert & run" queued (the modal has no executor, so it
|
||||||
|
// defers to here). Done before drain so the echoed "> cmd" line surfaces this frame.
|
||||||
|
if (!pending_submit_.empty()) {
|
||||||
|
std::string cmd;
|
||||||
|
cmd.swap(pending_submit_);
|
||||||
|
submitConsoleCommand(exec, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
// Pull passive log lines (daemon/xmrig output, or the lite diagnostics ring) and any
|
// Pull passive log lines (daemon/xmrig output, or the lite diagnostics ring) and any
|
||||||
// completed command results from the backend executor.
|
// completed command results from the backend executor.
|
||||||
exec.pollLogLines([this](const std::string& l, ConsoleChannel c) { addLine(l, c); });
|
exec.pollLogLines([this](const std::string& l, ConsoleChannel c) { addLine(l, c); });
|
||||||
@@ -1439,54 +1447,52 @@ bool ConsoleTab::submitConsoleCommand(ConsoleCommandExecutor& exec, const std::s
|
|||||||
namespace {
|
namespace {
|
||||||
// True if any of a command's name/desc/params contains `filterLower` (already lowercased).
|
// True if any of a command's name/desc/params contains `filterLower` (already lowercased).
|
||||||
// An empty filter matches everything.
|
// An empty filter matches everything.
|
||||||
bool consoleCommandMatchesFilter(const char* name, const char* desc, const char* params,
|
static bool lcContains(const char* s, const std::string& needleLower)
|
||||||
const std::string& filterLower)
|
|
||||||
{
|
{
|
||||||
if (filterLower.empty()) return true;
|
std::string v(s);
|
||||||
auto contains = [&filterLower](const char* s) {
|
std::transform(v.begin(), v.end(), v.begin(), ::tolower);
|
||||||
std::string v(s);
|
return v.find(needleLower) != std::string::npos;
|
||||||
std::transform(v.begin(), v.end(), v.begin(), ::tolower);
|
}
|
||||||
return v.find(filterLower) != std::string::npos;
|
static bool lcStartsWith(const char* s, const std::string& needleLower)
|
||||||
};
|
{
|
||||||
return contains(name) || contains(desc) || contains(params);
|
std::string v(s);
|
||||||
|
std::transform(v.begin(), v.end(), v.begin(), ::tolower);
|
||||||
|
return v.rfind(needleLower, 0) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw a command's parameter string into the current table cell, dimming optional [params].
|
// Search relevance of a command for a lowercased query. -1 = no match; higher = better. Keywords
|
||||||
void drawConsoleCommandParams(const char* params)
|
// let novices find a command by intent ("balance" -> getbalance) even without the exact name.
|
||||||
|
int consoleCommandRank(const ConsoleCommandEntry& cmd, const std::string& q)
|
||||||
{
|
{
|
||||||
using namespace material;
|
if (q.empty()) return 0;
|
||||||
const char* p = params;
|
if (lcStartsWith(cmd.name, q)) return 100;
|
||||||
bool first = true;
|
if (lcContains(cmd.name, q)) return 60;
|
||||||
while (*p) {
|
if (cmd.keywords[0] && lcContains(cmd.keywords, q)) return 40;
|
||||||
const char* bracketStart = strchr(p, '[');
|
if (lcContains(cmd.desc, q)) return 25;
|
||||||
if (bracketStart) {
|
if (cmd.details[0] && lcContains(cmd.details, q)) return 15;
|
||||||
// Draw the required part before the bracket.
|
if (lcContains(cmd.params, q)) return 10;
|
||||||
if (bracketStart > p) {
|
return -1;
|
||||||
if (!first) ImGui::SameLine(0, 0);
|
}
|
||||||
std::string req(p, bracketStart);
|
|
||||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurfaceDisabled()), "%s", req.c_str());
|
// Split a parameter template into top-level tokens, respecting quote/bracket nesting so a space
|
||||||
first = false;
|
// inside "..." or [{...}] doesn't split (e.g. `"address" [{"a":1}]` -> {`"address"`, `[{"a":1}]`}).
|
||||||
}
|
std::vector<std::string> splitParamTemplate(const char* params)
|
||||||
const char* bracketEnd = strchr(bracketStart, ']');
|
{
|
||||||
if (bracketEnd) {
|
std::vector<std::string> out;
|
||||||
if (!first) ImGui::SameLine(0, 0);
|
std::string tok;
|
||||||
std::string opt(bracketStart, bracketEnd + 1);
|
int depth = 0;
|
||||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurfaceMedium()), "%s", opt.c_str());
|
char q = 0;
|
||||||
first = false;
|
for (const char* p = params; *p; ++p) {
|
||||||
p = bracketEnd + 1;
|
char c = *p;
|
||||||
} else {
|
if (q) { tok += c; if (c == q) q = 0; continue; }
|
||||||
if (!first) ImGui::SameLine(0, 0);
|
if (c == '"' || c == '\'') { q = c; tok += c; continue; }
|
||||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurfaceDisabled()), "%s", bracketStart);
|
if (c == '[' || c == '{') { depth++; tok += c; continue; }
|
||||||
first = false;
|
if (c == ']' || c == '}') { if (depth > 0) depth--; tok += c; continue; }
|
||||||
break;
|
if (c == ' ' && depth == 0) { if (!tok.empty()) { out.push_back(tok); tok.clear(); } continue; }
|
||||||
}
|
tok += c;
|
||||||
} else {
|
|
||||||
if (!first) ImGui::SameLine(0, 0);
|
|
||||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurfaceDisabled()), "%s", p);
|
|
||||||
first = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (!tok.empty()) out.push_back(tok);
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate a command-category name from the static reference tables (English) for display. The
|
// Translate a command-category name from the static reference tables (English) for display. The
|
||||||
@@ -1504,154 +1510,276 @@ const char* consoleCategoryLabel(const char* name)
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
void ConsoleTab::insertCommandToInput(const ConsoleCommandEntry& cmd)
|
||||||
|
{
|
||||||
|
// Fill the console input with the command (+ its param template) and close the modal — the user
|
||||||
|
// reviews/edits it and presses Enter to run.
|
||||||
|
if (cmd.params[0] != '\0')
|
||||||
|
snprintf(input_buffer_, sizeof(input_buffer_), "%s %s", cmd.name, cmd.params);
|
||||||
|
else {
|
||||||
|
strncpy(input_buffer_, cmd.name, sizeof(input_buffer_) - 1);
|
||||||
|
input_buffer_[sizeof(input_buffer_) - 1] = '\0';
|
||||||
|
}
|
||||||
|
command_search_[0] = '\0';
|
||||||
|
run_confirm_cmd_ = nullptr;
|
||||||
|
show_commands_popup_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConsoleTab::renderCommandDetail(const ConsoleCommandEntry& cmd, const char* catLabel)
|
||||||
|
{
|
||||||
|
using namespace material;
|
||||||
|
float dp = Layout::dpiScale();
|
||||||
|
ImFont* mono = Type().mono();
|
||||||
|
|
||||||
|
// Heading: command name, then category + a safety badge for consequential commands.
|
||||||
|
ImGui::PushFont(Type().subtitle1());
|
||||||
|
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(IM_COL32(120, 190, 255, 255)), "%s", cmd.name);
|
||||||
|
ImGui::PopFont();
|
||||||
|
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurfaceDisabled()), "%s", catLabel);
|
||||||
|
if (cmd.destructive) {
|
||||||
|
ImGui::SameLine(0, Layout::spacingMd());
|
||||||
|
ImVec4 warn = ImGui::ColorConvertU32ToFloat4(Warning());
|
||||||
|
ImGui::PushFont(Type().iconSmall());
|
||||||
|
ImGui::TextColored(warn, ICON_MD_WARNING);
|
||||||
|
ImGui::PopFont();
|
||||||
|
ImGui::SameLine(0, Layout::spacingXs());
|
||||||
|
ImGui::TextColored(warn, "%s", TR("console_ref_destructive"));
|
||||||
|
}
|
||||||
|
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||||
|
|
||||||
|
// Plain-language explanation (falls back to the terse summary when not enriched).
|
||||||
|
ImGui::PushTextWrapPos(0.0f);
|
||||||
|
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurface()), "%s",
|
||||||
|
cmd.details[0] ? cmd.details : cmd.desc);
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
ImGui::Dummy(ImVec2(0, Layout::spacingMd()));
|
||||||
|
|
||||||
|
// Parameters — each token with a rough type tag; optional ([...]) tokens are dimmed.
|
||||||
|
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurfaceMedium()), "%s", TR("console_ref_parameters"));
|
||||||
|
std::vector<std::string> toks = splitParamTemplate(cmd.params);
|
||||||
|
if (toks.empty()) {
|
||||||
|
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurfaceDisabled()), "%s", TR("console_ref_no_params"));
|
||||||
|
} else {
|
||||||
|
ImGui::Indent(Layout::spacingMd());
|
||||||
|
for (const std::string& t : toks) {
|
||||||
|
bool optional = (!t.empty() && t.front() == '[');
|
||||||
|
char c0 = t.empty() ? 0 : t[0];
|
||||||
|
if (optional && t.size() >= 2) c0 = t[1];
|
||||||
|
const char* type = (c0 == '"' || c0 == '\'') ? "string"
|
||||||
|
: (c0 == '{' || c0 == '[') ? "json" : "number";
|
||||||
|
ImGui::PushFont(mono);
|
||||||
|
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(optional ? OnSurfaceMedium() : OnSurface()),
|
||||||
|
"%s", t.c_str());
|
||||||
|
ImGui::PopFont();
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (optional)
|
||||||
|
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurfaceDisabled()),
|
||||||
|
" %s \xC2\xB7 %s", type, TR("console_ref_optional"));
|
||||||
|
else
|
||||||
|
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurfaceDisabled()), " %s", type);
|
||||||
|
}
|
||||||
|
ImGui::Unindent(Layout::spacingMd());
|
||||||
|
}
|
||||||
|
ImGui::Dummy(ImVec2(0, Layout::spacingMd()));
|
||||||
|
|
||||||
|
// Example (curated commands only).
|
||||||
|
if (cmd.example[0]) {
|
||||||
|
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(OnSurfaceMedium()), "%s", TR("console_ref_example"));
|
||||||
|
ImGui::PushFont(mono);
|
||||||
|
ImGui::PushTextWrapPos(0.0f);
|
||||||
|
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(IM_COL32(150, 200, 150, 255)), "%s", cmd.example);
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
ImGui::PopFont();
|
||||||
|
ImGui::Dummy(ImVec2(0, Layout::spacingMd()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actions (or the destructive run confirmation).
|
||||||
|
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||||
|
if (run_confirm_cmd_ == &cmd) {
|
||||||
|
ImVec4 warn = ImGui::ColorConvertU32ToFloat4(Warning());
|
||||||
|
ImGui::PushFont(Type().iconSmall());
|
||||||
|
ImGui::TextColored(warn, ICON_MD_WARNING);
|
||||||
|
ImGui::PopFont();
|
||||||
|
ImGui::SameLine(0, Layout::spacingXs());
|
||||||
|
ImGui::TextColored(warn, TR("console_ref_run_confirm"), cmd.name);
|
||||||
|
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||||
|
float cbw = 120.0f * dp;
|
||||||
|
if (material::TactileButton(TR("console_ref_cancel"), ImVec2(cbw, 0))) run_confirm_cmd_ = nullptr;
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (material::TactileButton(TR("console_ref_run"), ImVec2(cbw, 0))) {
|
||||||
|
pending_submit_ = cmd.name;
|
||||||
|
command_search_[0] = '\0';
|
||||||
|
run_confirm_cmd_ = nullptr;
|
||||||
|
show_commands_popup_ = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (material::TactileButton(TR("console_ref_insert"), ImVec2(200.0f * dp, 0)))
|
||||||
|
insertCommandToInput(cmd);
|
||||||
|
// "Insert & run" only for commands that take no parameters — running a template with
|
||||||
|
// placeholders ("address", amount) would just error.
|
||||||
|
if (cmd.params[0] == '\0') {
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (material::TactileButton(TR("console_ref_insert_run"), ImVec2(140.0f * dp, 0))) {
|
||||||
|
if (cmd.destructive) {
|
||||||
|
run_confirm_cmd_ = &cmd;
|
||||||
|
} else {
|
||||||
|
pending_submit_ = cmd.name;
|
||||||
|
command_search_[0] = '\0';
|
||||||
|
show_commands_popup_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ConsoleTab::renderCommandsPopup()
|
void ConsoleTab::renderCommandsPopup()
|
||||||
{
|
{
|
||||||
using namespace material;
|
using namespace material;
|
||||||
|
float dp = Layout::dpiScale();
|
||||||
|
|
||||||
float popW = std::min(schema::UI().drawElement("tabs.console", "popup-max-width").size, ImGui::GetMainViewport()->Size.x * schema::UI().drawElement("tabs.console", "popup-width-ratio").size);
|
|
||||||
material::OverlayDialogSpec ov;
|
material::OverlayDialogSpec ov;
|
||||||
ov.title = TR("console_rpc_reference"); ov.p_open = &show_commands_popup_;
|
ov.title = TR("console_rpc_reference");
|
||||||
ov.style = material::OverlayStyle::BlurFloat; // floating content on the blur, plain heading
|
ov.p_open = &show_commands_popup_;
|
||||||
ov.cardWidth = popW; ov.cardBottomViewportRatio = 0.94f; // keep authored width
|
ov.style = material::OverlayStyle::BlurFloat; // floating content on the blur, plain heading
|
||||||
// FIXED height (fills most of the viewport) instead of auto-height: the command list's
|
ov.cardWidth = 960.0f; // wide enough for two panes
|
||||||
// fill-height child reads GetContentRegionAvail() inside the dialog child, which is only stable
|
ov.cardHeight = ImGui::GetMainViewport()->Size.y * 0.74f / dp; // fixed; both panes fill it
|
||||||
// when the child is fixed-height. Auto-height made it self-referential and it slid content
|
ov.idSuffix = "cmdref";
|
||||||
// off-screen after a monitor move (same class of bug as the Wallets modal).
|
if (!material::BeginOverlayDialog(ov)) return;
|
||||||
ov.cardHeight = ImGui::GetMainViewport()->Size.y * 0.84f / Layout::dpiScale();
|
|
||||||
if (!material::BeginOverlayDialog(ov)) {
|
|
||||||
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 — auto-focus on open so the user can type immediately (like the console input).
|
// Esc dismisses (a first Esc cancels a pending run-confirm). Not built into the overlay.
|
||||||
|
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||||
|
if (run_confirm_cmd_) run_confirm_cmd_ = nullptr;
|
||||||
|
else show_commands_popup_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search box — auto-focus on open so the user can type immediately.
|
||||||
ImGui::SetNextItemWidth(-1);
|
ImGui::SetNextItemWidth(-1);
|
||||||
if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere();
|
if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere();
|
||||||
ImGui::InputTextWithHint("##CmdSearch", TR("console_search_commands"), command_search_, sizeof(command_search_));
|
bool searchChanged = ImGui::InputTextWithHint("##CmdSearch", TR("console_ref_search_hint"),
|
||||||
|
command_search_, sizeof(command_search_));
|
||||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||||
|
|
||||||
|
std::string q(command_search_);
|
||||||
|
std::transform(q.begin(), q.end(), q.begin(), ::tolower);
|
||||||
|
const bool searching = !q.empty();
|
||||||
|
|
||||||
const auto& categories = consoleCommandCategories();
|
const auto& categories = consoleCommandCategories();
|
||||||
|
|
||||||
std::string filter(command_search_);
|
// Flat display order of (cat,idx): ranked when searching, category order when browsing. Drives
|
||||||
std::transform(filter.begin(), filter.end(), filter.begin(), ::tolower);
|
// keyboard nav + auto-selection; the browse view still renders grouped headers below.
|
||||||
|
std::vector<std::pair<int, int>> order;
|
||||||
ImGui::BeginChild("CmdListScroll", ImVec2(0, -ImGui::GetFrameHeightWithSpacing() - Layout::spacingXs()),
|
if (searching) {
|
||||||
false);
|
std::vector<std::pair<int, std::pair<int, int>>> scored; // (score, (cat,idx))
|
||||||
|
for (int c = 0; c < (int)categories.size(); c++)
|
||||||
ImGui::PushFont(Type().caption());
|
for (int i = 0; i < categories[c].count; i++) {
|
||||||
|
int s = consoleCommandRank(categories[c].commands[i], q);
|
||||||
// cmd-min-width is authored in logical px; popupInnerW (GetContentRegionAvail) is physical/DPI-
|
if (s >= 0) scored.push_back({s, {c, i}});
|
||||||
// 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;
|
|
||||||
bool showParams = popupInnerW >= cmdMinWidth;
|
|
||||||
int catIdx = 0;
|
|
||||||
|
|
||||||
for (const auto& cat : categories) {
|
|
||||||
// Count matching commands in this category
|
|
||||||
int matchCount = 0;
|
|
||||||
if (filter.empty()) {
|
|
||||||
matchCount = cat.count;
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < cat.count; i++) {
|
|
||||||
if (consoleCommandMatchesFilter(cat.commands[i].name, cat.commands[i].desc,
|
|
||||||
cat.commands[i].params, filter)) {
|
|
||||||
matchCount++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
std::stable_sort(scored.begin(), scored.end(),
|
||||||
if (matchCount == 0) { catIdx++; continue; }
|
[](const auto& a, const auto& b) { return a.first > b.first; });
|
||||||
|
for (auto& s : scored) order.push_back(s.second);
|
||||||
// Default-open only the first category (Control); collapse the rest
|
} else {
|
||||||
ImGuiTreeNodeFlags headerFlags = (catIdx == 0) ? ImGuiTreeNodeFlags_DefaultOpen : 0;
|
for (int c = 0; c < (int)categories.size(); c++)
|
||||||
// When filtering, open all matching categories
|
for (int i = 0; i < categories[c].count; i++) order.push_back({c, i});
|
||||||
if (!filter.empty()) headerFlags = ImGuiTreeNodeFlags_DefaultOpen;
|
|
||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Primary()));
|
|
||||||
// Show match count badge when filtering
|
|
||||||
const char* catLabel = consoleCategoryLabel(cat.name);
|
|
||||||
char headerLabel[128];
|
|
||||||
if (!filter.empty()) {
|
|
||||||
snprintf(headerLabel, sizeof(headerLabel), "%s (%d)", catLabel, matchCount);
|
|
||||||
} else {
|
|
||||||
snprintf(headerLabel, sizeof(headerLabel), "%s", catLabel);
|
|
||||||
}
|
|
||||||
bool open = ImGui::CollapsingHeader(headerLabel, headerFlags);
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
catIdx++;
|
|
||||||
|
|
||||||
if (open) {
|
|
||||||
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();
|
|
||||||
int numCols = showParams ? 3 : 2;
|
|
||||||
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);
|
|
||||||
if (showParams)
|
|
||||||
ImGui::TableSetupColumn("Parameters", ImGuiTableColumnFlags_WidthFixed, paramsColW);
|
|
||||||
ImGui::TableSetupColumn("Desc", ImGuiTableColumnFlags_WidthStretch);
|
|
||||||
|
|
||||||
for (int i = 0; i < cat.count; i++) {
|
|
||||||
const auto& cmd = cat.commands[i];
|
|
||||||
if (!consoleCommandMatchesFilter(cmd.name, cmd.desc, cmd.params, filter))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
|
||||||
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::ColorConvertU32ToFloat4(IM_COL32(100, 180, 255, 255)));
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0, 0, 0, 0));
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.2f, 0.4f, 0.6f, 0.3f));
|
|
||||||
char selId[128];
|
|
||||||
snprintf(selId, sizeof(selId), "%s##cmdRef", cmd.name);
|
|
||||||
if (ImGui::Selectable(selId, false)) {
|
|
||||||
if (cmd.params[0] != '\0') {
|
|
||||||
snprintf(input_buffer_, sizeof(input_buffer_), "%s %s", cmd.name, cmd.params);
|
|
||||||
} else {
|
|
||||||
strncpy(input_buffer_, cmd.name, sizeof(input_buffer_) - 1);
|
|
||||||
input_buffer_[sizeof(input_buffer_) - 1] = '\0';
|
|
||||||
}
|
|
||||||
// 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()) {
|
|
||||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
|
||||||
if (cmd.params[0] != '\0')
|
|
||||||
material::Tooltip(TR("console_click_insert_params"), cmd.name, cmd.params);
|
|
||||||
else
|
|
||||||
material::Tooltip(TR("console_click_insert"), cmd.name);
|
|
||||||
}
|
|
||||||
ImGui::PopStyleColor(3);
|
|
||||||
if (showParams) {
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
drawConsoleCommandParams(cmd.params);
|
|
||||||
}
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::TextColored(
|
|
||||||
ImGui::ColorConvertU32ToFloat4(OnSurfaceMedium()),
|
|
||||||
"%s", cmd.desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndTable();
|
|
||||||
}
|
|
||||||
ImGui::Spacing();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PopFont();
|
// Keep a valid selection: reset to the top when the query changes or the current selection falls
|
||||||
|
// out of the visible set, so the detail pane is always populated.
|
||||||
|
int selPos = -1;
|
||||||
|
for (int k = 0; k < (int)order.size(); k++)
|
||||||
|
if (order[k].first == cmd_sel_cat_ && order[k].second == cmd_sel_idx_) { selPos = k; break; }
|
||||||
|
if (searchChanged || selPos < 0) {
|
||||||
|
if (!order.empty()) { cmd_sel_cat_ = order[0].first; cmd_sel_idx_ = order[0].second; selPos = 0; }
|
||||||
|
else { cmd_sel_cat_ = cmd_sel_idx_ = -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keyboard nav: Up/Down move the selection, Enter inserts (disabled while a run-confirm shows).
|
||||||
|
if (!run_confirm_cmd_ && !order.empty() && selPos >= 0) {
|
||||||
|
if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) && selPos + 1 < (int)order.size()) selPos++;
|
||||||
|
else if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) && selPos > 0) selPos--;
|
||||||
|
cmd_sel_cat_ = order[selPos].first;
|
||||||
|
cmd_sel_idx_ = order[selPos].second;
|
||||||
|
if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))
|
||||||
|
insertCommandToInput(categories[cmd_sel_cat_].commands[cmd_sel_idx_]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Two-pane body sized above the footer.
|
||||||
|
float footerH = ImGui::GetFrameHeightWithSpacing() + Layout::spacingXs();
|
||||||
|
float bodyH = std::max(120.0f, ImGui::GetContentRegionAvail().y - footerH);
|
||||||
|
float contentW = ImGui::GetContentRegionAvail().x;
|
||||||
|
float gap = Layout::spacingLg();
|
||||||
|
float masterW = std::min(std::max(contentW * 0.34f, 280.0f * dp), 380.0f * dp);
|
||||||
|
float detailW = contentW - masterW - gap;
|
||||||
|
|
||||||
|
ImFont* mono = Type().mono();
|
||||||
|
ImU32 nameCol = IM_COL32(100, 180, 255, 255);
|
||||||
|
float rowH = std::max(20.0f * dp, mono->LegacySize + 6.0f * dp);
|
||||||
|
|
||||||
|
// One master row: mono command name + a warning dot for consequential commands. Click selects
|
||||||
|
// (drives the detail pane); double-click inserts.
|
||||||
|
auto drawRow = [&](int c, int i) {
|
||||||
|
const ConsoleCommandEntry& cmd = categories[c].commands[i];
|
||||||
|
bool sel = (cmd_sel_cat_ == c && cmd_sel_idx_ == i);
|
||||||
|
ImGui::PushID(c * 1000 + i);
|
||||||
|
ImVec2 rmn = ImGui::GetCursorScreenPos();
|
||||||
|
if (ImGui::Selectable("##cmdrow", sel, ImGuiSelectableFlags_SpanAvailWidth, ImVec2(0, rowH)))
|
||||||
|
{ cmd_sel_cat_ = c; cmd_sel_idx_ = i; }
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||||
|
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) insertCommandToInput(cmd);
|
||||||
|
}
|
||||||
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||||
|
float tx = rmn.x + Layout::spacingSm();
|
||||||
|
if (cmd.destructive) {
|
||||||
|
float r = 3.0f * dp;
|
||||||
|
dl->AddCircleFilled(ImVec2(tx + r, rmn.y + rowH * 0.5f), r, Warning());
|
||||||
|
tx += r * 2.0f + Layout::spacingXs();
|
||||||
|
}
|
||||||
|
dl->AddText(mono, mono->LegacySize, ImVec2(tx, rmn.y + (rowH - mono->LegacySize) * 0.5f),
|
||||||
|
sel ? OnSurface() : nameCol, cmd.name);
|
||||||
|
ImGui::PopID();
|
||||||
|
};
|
||||||
|
|
||||||
|
// MASTER pane.
|
||||||
|
ImGui::BeginChild("##cmdMaster", ImVec2(masterW, bodyH), true);
|
||||||
|
{
|
||||||
|
if (order.empty()) {
|
||||||
|
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("console_ref_no_match"));
|
||||||
|
} else if (searching) {
|
||||||
|
for (auto& p : order) drawRow(p.first, p.second);
|
||||||
|
} else {
|
||||||
|
for (int c = 0; c < (int)categories.size(); c++) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Primary()));
|
||||||
|
bool open = ImGui::CollapsingHeader(consoleCategoryLabel(categories[c].name),
|
||||||
|
ImGuiTreeNodeFlags_DefaultOpen);
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
if (open)
|
||||||
|
for (int i = 0; i < categories[c].count; i++) drawRow(c, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
ImGui::SameLine(0, gap);
|
||||||
|
|
||||||
// Close button (the filter is also reset when the modal next opens).
|
// DETAIL pane.
|
||||||
|
ImGui::BeginChild("##cmdDetail", ImVec2(detailW, bodyH), true);
|
||||||
|
if (cmd_sel_cat_ >= 0 && cmd_sel_cat_ < (int)categories.size() &&
|
||||||
|
cmd_sel_idx_ >= 0 && cmd_sel_idx_ < categories[cmd_sel_cat_].count) {
|
||||||
|
renderCommandDetail(categories[cmd_sel_cat_].commands[cmd_sel_idx_],
|
||||||
|
consoleCategoryLabel(categories[cmd_sel_cat_].name));
|
||||||
|
} else {
|
||||||
|
ImVec2 av = ImGui::GetContentRegionAvail();
|
||||||
|
ImGui::SetCursorPosY(av.y * 0.4f);
|
||||||
|
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("console_ref_select_hint"));
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
// Footer.
|
||||||
|
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||||
if (material::TactileButton(TR("console_close"), ImVec2(-1, 0))) {
|
if (material::TactileButton(TR("console_close"), ImVec2(-1, 0))) {
|
||||||
command_search_[0] = '\0';
|
command_search_[0] = '\0';
|
||||||
|
run_confirm_cmd_ = nullptr;
|
||||||
show_commands_popup_ = false;
|
show_commands_popup_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ namespace ui {
|
|||||||
|
|
||||||
class ConsoleCommandExecutor;
|
class ConsoleCommandExecutor;
|
||||||
struct ConsoleLogFilterCaps;
|
struct ConsoleLogFilterCaps;
|
||||||
|
struct ConsoleCommandEntry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Console tab — a rich terminal shared by the full-node and lite variants.
|
* @brief Console tab — a rich terminal shared by the full-node and lite variants.
|
||||||
@@ -115,6 +116,8 @@ private:
|
|||||||
void addFormattedResult(const std::string& result, bool is_error);
|
void addFormattedResult(const std::string& result, bool is_error);
|
||||||
void renderStatusHeader(ConsoleCommandExecutor& exec);
|
void renderStatusHeader(ConsoleCommandExecutor& exec);
|
||||||
void renderCommandsPopup();
|
void renderCommandsPopup();
|
||||||
|
void renderCommandDetail(const ConsoleCommandEntry& cmd, const char* catLabel); // right pane
|
||||||
|
void insertCommandToInput(const ConsoleCommandEntry& cmd); // fill input + close the modal
|
||||||
|
|
||||||
// renderToolbar() draws the top bar; these are its sub-steps:
|
// renderToolbar() draws the top bar; these are its sub-steps:
|
||||||
void renderToolbar(ConsoleCommandExecutor& exec);
|
void renderToolbar(ConsoleCommandExecutor& exec);
|
||||||
@@ -183,9 +186,13 @@ private:
|
|||||||
// consumed by the renderer + hit-testing.
|
// consumed by the renderer + hit-testing.
|
||||||
mutable ConsoleLayout layout_;
|
mutable ConsoleLayout layout_;
|
||||||
|
|
||||||
// Commands popup
|
// Commands popup (RPC command explorer)
|
||||||
bool show_commands_popup_ = false;
|
bool show_commands_popup_ = false;
|
||||||
char command_search_[128] = {0}; // RPC-reference search filter (cleared when the modal opens)
|
char command_search_[128] = {0}; // RPC-reference search filter (cleared when the modal opens)
|
||||||
|
int cmd_sel_cat_ = -1; // detail-pane selection: category index into consoleCommandCategories()
|
||||||
|
int cmd_sel_idx_ = -1; // detail-pane selection: command index within that category
|
||||||
|
std::string pending_submit_; // command to run next frame (deferred so the modal needs no executor)
|
||||||
|
const ConsoleCommandEntry* run_confirm_cmd_ = nullptr; // destructive "Insert & run" awaiting confirmation
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
|
|||||||
@@ -1301,6 +1301,19 @@ void I18n::loadBuiltinEnglish()
|
|||||||
strings_["console_cat_wallet"] = "Wallet";
|
strings_["console_cat_wallet"] = "Wallet";
|
||||||
strings_["console_cat_raw_transactions"] = "Raw Transactions";
|
strings_["console_cat_raw_transactions"] = "Raw Transactions";
|
||||||
strings_["console_cat_utility"] = "Utility";
|
strings_["console_cat_utility"] = "Utility";
|
||||||
|
strings_["console_ref_search_hint"] = "Search by name or task\xE2\x80\xA6";
|
||||||
|
strings_["console_ref_parameters"] = "Parameters";
|
||||||
|
strings_["console_ref_no_params"] = "Takes no parameters.";
|
||||||
|
strings_["console_ref_optional"] = "optional";
|
||||||
|
strings_["console_ref_example"] = "Example";
|
||||||
|
strings_["console_ref_destructive"] = "Consequential";
|
||||||
|
strings_["console_ref_run_confirm"] = "Run %s now? This is a consequential command.";
|
||||||
|
strings_["console_ref_cancel"] = "Cancel";
|
||||||
|
strings_["console_ref_run"] = "Run";
|
||||||
|
strings_["console_ref_insert"] = "Insert into console";
|
||||||
|
strings_["console_ref_insert_run"] = "Insert & run";
|
||||||
|
strings_["console_ref_select_hint"] = "Select a command to see what it does.";
|
||||||
|
strings_["console_ref_no_match"] = "No commands match.";
|
||||||
|
|
||||||
// --- Export All Keys Dialog ---
|
// --- Export All Keys Dialog ---
|
||||||
strings_["export_keys_btn"] = "Export Keys";
|
strings_["export_keys_btn"] = "Export Keys";
|
||||||
|
|||||||
Reference in New Issue
Block a user