fix(eckhart, ui): consistent cancel layout
What changed, and why it matters
This is a user-interface consistency patch for the Trezor hardware wallet's 'Eckhart' design. It moves the 'cancel' option from a small 'X' button on the action bar into a menu, matching how the newer 'Delizia' design already works. There is no security bug being fixed here; it is purely a layout and navigation change.
No security action required. Treat as a normal UI/UX consistency change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors several UI flows in core/src/trezor/ui/layouts/eckhart/init.py so that cancel/close behavior is provided through a menu (confirm_with_menu / Menu.root) rather than a cancel flag passed directly to the Rust UI API. It removes explicit cancel=True arguments from confirm_blob, confirm_address, Ethereum, Tron, Solana, and Stellar flows, and updates confirm_value to use an external menu. The Rust API docstring for show_info_with_cancel is updated to mention a ‘close’ button. The changelog labels this as a UI change for the T3W1 device.
Changed components
core/src/trezor/ui/layouts/eckhart/__init__.pycore/src/apps/ethereum/layout.pycore/src/apps/tron/layout.pycore/embed/rust/src/ui/api/firmware_micropython.rscore/mocks/generated/trezorui_api.pyiInspect captured patch +19 / −35
diff --git a/core/.changelog.d/6707.changed b/core/.changelog.d/6707.changed
new file mode 100644
index 00000000..c35f6d55
--- /dev/null
+++ b/core/.changelog.d/6707.changed
@@ -0,0 +1 @@
+[T3W1] Consistent cancel option across screens.
diff --git a/core/embed/rust/src/ui/api/firmware_micropython.rs b/core/embed/rust/src/ui/api/firmware_micropython.rs
index e001951f..b4194535 100644
--- a/core/embed/rust/src/ui/api/firmware_micropython.rs
+++ b/core/embed/rust/src/ui/api/firmware_micropython.rs
@@ -2014,7 +2014,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// horizontal: bool = False,
/// chunkify: bool = False,
/// ) -> LayoutObj[UiResult]:
- /// """Show metadata for outgoing transaction."""
+ /// """Show metadata for outgoing transaction with a 'close' button."""
Qstr::MP_QSTR_show_info_with_cancel => obj_fn_kw!(0, new_show_info_with_cancel).as_obj(),
/// def show_lockscreen(
diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi
index 771107b0..e8c0c701 100644
--- a/core/mocks/generated/trezorui_api.pyi
+++ b/core/mocks/generated/trezorui_api.pyi
@@ -701,7 +701,7 @@ def show_info_with_cancel(
horizontal: bool = False,
chunkify: bool = False,
) -> LayoutObj[UiResult]:
- """Show metadata for outgoing transaction."""
+ """Show metadata for outgoing transaction with a 'close' button."""
# rust/src/ui/api/firmware_micropython.rs
diff --git a/core/src/apps/ethereum/layout.py b/core/src/apps/ethereum/layout.py
index 2e7f5422..091307a2 100644
--- a/core/src/apps/ethereum/layout.py
+++ b/core/src/apps/ethereum/layout.py
@@ -372,7 +372,6 @@ async def confirm_message_hash(message_hash: bytes) -> None:
"confirm_message_hash",
verb=TR.buttons__confirm,
br_code=ButtonRequestType.SignTx,
- cancel=True,
)
diff --git a/core/src/apps/tron/layout.py b/core/src/apps/tron/layout.py
index 597d458b..9367fbd1 100644
--- a/core/src/apps/tron/layout.py
+++ b/core/src/apps/tron/layout.py
@@ -156,7 +156,6 @@ async def confirm_withdraw_unfreeze(owner_address: AnyBytes) -> None:
chunkify=True,
hold=True,
br_name="tron/claim",
- cancel=True,
)
diff --git a/core/src/trezor/ui/layouts/eckhart/__init__.py b/core/src/trezor/ui/layouts/eckhart/__init__.py
index ca8f98e5..04103536 100644
--- a/core/src/trezor/ui/layouts/eckhart/__init__.py
+++ b/core/src/trezor/ui/layouts/eckhart/__init__.py
@@ -248,7 +248,6 @@ async def show_passphrase_from_host(passphrase: str | None) -> None:
description="",
br_name="passphrase_host2",
verb=TR.passphrase__title_confirm,
- cancel=True,
)
@@ -736,7 +735,6 @@ def confirm_blob(
ask_pagination: bool = False,
verb_skip_pagination: str | None = None,
chunkify: bool = False,
- _prompt_screen: bool = True,
) -> Awaitable[None]:
if ask_pagination:
@@ -769,20 +767,16 @@ def confirm_blob(
info_layout_can_confirm=True,
)
else:
- layout = trezorui_api.confirm_value(
+ return confirm_value(
+ br_name=br_name,
title=title,
value=data,
- description=description,
+ description=description or "",
subtitle=subtitle,
verb=verb,
hold=hold,
chunkify=chunkify,
- cancel=True,
- )
- return raise_if_not_confirmed(
- layout,
- br_name,
- br_code,
+ br_code=br_code,
)
@@ -791,7 +785,7 @@ def confirm_address(
address: str,
subtitle: str | None = None,
description: str | None = None,
- verb: str | None = None,
+ verb: str | None = TR.buttons__confirm,
warning_footer: str | None = None,
chunkify: bool = True,
br_name: str | None = None,
@@ -807,7 +801,6 @@ def confirm_address(
verb=verb,
chunkify=chunkify,
warning_footer=warning_footer,
- cancel=True,
)
@@ -846,7 +839,7 @@ def confirm_amount(
def confirm_value(
title: str,
- value: str,
+ value: StrOrBytes,
description: str,
br_name: str,
br_code: ButtonRequestType = BR_CODE_OTHER,
@@ -858,20 +851,19 @@ def confirm_value(
chunkify: bool = False,
info_items: Iterable[StrPropertyType] | None = None,
info_title: str | None = None,
- chunkify_info: bool = False,
warning_footer: str | None = None,
- cancel: bool = False,
) -> Awaitable[None]:
"""General confirmation dialog, used by many other confirm_* functions."""
+ from trezor.ui.layouts.menu import Menu, confirm_with_menu
- items = list(info_items) if info_items else []
- info_layout = trezorui_api.show_info_with_cancel(
- title=info_title if info_title else TR.words__title_information,
- items=items,
- chunkify=chunkify_info,
+ menu_items = (
+ [create_details(info_title or TR.words__title_information, list(info_items))]
+ if info_items
+ else []
)
+ menu = Menu.root(menu_items, TR.buttons__cancel)
- return with_info(
+ return confirm_with_menu(
trezorui_api.confirm_value(
title=title,
value=value,
@@ -879,13 +871,13 @@ def confirm_value(
description=description,
subtitle=subtitle,
verb=verb,
- info=bool(info_items),
+ info=False,
hold=hold,
chunkify=chunkify,
warning_footer=warning_footer,
- cancel=cancel,
+ external_menu=True,
),
- info_layout,
+ menu,
br_name,
br_code,
)
@@ -1172,7 +1164,6 @@ if not utils.BITCOIN_ONLY:
chunkify=chunkify,
br_name=br_name,
verb=TR.buttons__continue,
- cancel=True,
)
else:
main_layout = trezorui_api.confirm_with_info(
@@ -1207,7 +1198,6 @@ if not utils.BITCOIN_ONLY:
subtitle=TR.ethereum__token_contract,
chunkify=chunkify,
br_name=br_name,
- cancel=True,
)
if is_unknown_network:
@@ -1217,7 +1207,6 @@ if not utils.BITCOIN_ONLY:
chain_id,
TR.ethereum__approve_chain_id,
br_name=br_name,
- cancel=True,
)
properties: list[PropertyType] = (
@@ -1453,7 +1442,6 @@ if not utils.BITCOIN_ONLY:
br_code=br_code,
verb=TR.buttons__continue,
info_items=items,
- cancel=True,
)
def confirm_solana_tx(
@@ -1641,7 +1629,6 @@ if not utils.BITCOIN_ONLY:
info_items=info_items,
info_title=TR.stellar__token_info,
is_data=False,
- chunkify_info=True,
chunkify=False,
)
@@ -1675,7 +1662,6 @@ if not utils.BITCOIN_ONLY:
chunkify=chunkify,
br_name=br_name,
verb=TR.buttons__continue,
- cancel=True,
)
properties: Iterable[StrPropertyType] = (
@@ -1744,7 +1730,6 @@ if not utils.BITCOIN_ONLY:
chunkify=chunkify,
br_name=br_name,
verb=TR.buttons__continue,
- cancel=True,
)
properties: list[StrPropertyType] = [
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.