From 6b43b51711fc56bc702d7a8a986eeadfa9c70208 Mon Sep 17 00:00:00 2001
From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Date: Tue, 4 Aug 2026 03:45:39 -0700
Subject: [PATCH] fix(mobile): gate the tool detail panel on having detail
(STA-3333)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The Tools toggle opens every row at once, bypassing the row's tap guard,
so a row with nothing to expand rendered its own label again underneath
itself — and the tap that would dismiss it is a no-op. Matches desktop.
---
mobile/src/session/MobileNativeChatMessage.test.ts | 10 ++++++++++
mobile/src/session/MobileNativeChatMessage.tsx | 8 ++++++--
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/mobile/src/session/MobileNativeChatMessage.test.ts b/mobile/src/session/MobileNativeChatMessage.test.ts
index f38a437d55c..4749dcd58ee 100644
--- a/mobile/src/session/MobileNativeChatMessage.test.ts
+++ b/mobile/src/session/MobileNativeChatMessage.test.ts
@@ -141,4 +141,14 @@ describe('MobileNativeChatMessage', () => {
'{\n "cmd": "git status",\n "description": "Inspect changes"\n}'
)
})
+
+ it('does not echo the row label as detail when a row has nothing to expand', () => {
+ // The Tools toggle opens every row at once, bypassing the tap guard — a row
+ // whose formatted input is its own label would echo itself in a panel that
+ // no tap can dismiss.
+ const tree = render(toolMessage([{ type: 'tool-call', name: 'ListTodos', input: '{}' }]), {
+ toolsExpanded: true
+ })
+ expect(textIn(tree.root).filter((text) => text === '{}')).toHaveLength(1)
+ })
})
diff --git a/mobile/src/session/MobileNativeChatMessage.tsx b/mobile/src/session/MobileNativeChatMessage.tsx
index 6efd4069959..2a93c3dde6e 100644
--- a/mobile/src/session/MobileNativeChatMessage.tsx
+++ b/mobile/src/session/MobileNativeChatMessage.tsx
@@ -109,6 +109,10 @@ function ToolLine({
result !== undefined ||
preview.length > 40 ||
(call !== undefined && isStructuredToolInput(call.input))
+ // The group toggle opens every line at once, bypassing the tap guard, so the
+ // panel has to consult it too — else a detail-less row echoes its own label
+ // under itself and no tap can dismiss it.
+ const showDetail = hasDetail && expanded
// A tool that targets a file (Read/Edit/Write…) renders its preview as a
// tappable link that opens the file, independent of the line's expand tap.
const filePath = call ? toolFilePath(call.input) : null
@@ -120,7 +124,7 @@ function ToolLine({
onPress={() => hasDetail && setExpanded((v) => !v)}
hitSlop={6}
>
- {expanded ? (
+ {showDetail ? (
) : (
@@ -137,7 +141,7 @@ function ToolLine({
) : null}
- {expanded ? (
+ {showDetail ? (
{callDiff ? : null}
{callDetail ? {callDetail} : null}