fix(app): show running shell command (#38080)

Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
This commit is contained in:
opencode-agent[bot] 2026-07-22 11:47:42 +10:00 committed by GitHub
commit c9db6e9a1f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 22 additions and 3 deletions

View file

@ -32,6 +32,23 @@ for (const expanded of [false, true]) {
})
}
test("shows and expands a running shell command without shimmering it", async ({ page }) => {
const id = "prt_shell_running_command"
const command = "sleep 10 && echo done"
await setupTimeline(page, {
messages: [userMessage(), assistantMessage([shell(id, "running", "still running", command)], { completed: false })],
settings: { shellToolPartsExpanded: false },
})
const tool = page.locator(`[data-timeline-part-id="${id}"]`)
await expect(tool.locator('[data-component="text-shimmer"]')).toHaveAttribute("data-active", "true")
await expect(tool.locator('[data-component="shell-submessage"]')).toHaveText(command)
await expect(tool.locator('[data-component="shell-submessage"] [data-component="text-shimmer"]')).toHaveCount(0)
await tool.locator('[data-slot="collapsible-trigger"]').click()
await expect(tool.locator('[data-slot="collapsible-trigger"]')).toHaveAttribute("aria-expanded", "true")
await expect(tool.locator('[data-slot="bash-pre"]')).toContainText("still running")
})
test("transitions thinking and hidden reasoning through busy to idle", async ({ page }) => {
const reasoningID = "prt_reasoning_hidden"
const assistant = assistantMessage([reasoningPart(reasoningID, "## Inspecting stability")], { completed: false })

View file

@ -32,6 +32,7 @@ export interface BasicToolProps {
open?: boolean
onOpenChange?: (open: boolean) => void
forceOpen?: boolean
allowOpenWhilePending?: boolean
defer?: boolean
locked?: boolean
animated?: boolean
@ -176,7 +177,7 @@ export function BasicTool(props: BasicToolProps) {
})
const handleOpenChange = (value: boolean) => {
if (pending()) return
if (pending() && !props.allowOpenWhilePending) return
if (props.locked && !value) return
setOpen(value)
}
@ -247,7 +248,7 @@ export function BasicTool(props: BasicToolProps) {
</Switch>
</div>
</div>
<Show when={hasChildren() && !props.hideDetails && !props.locked && !pending()}>
<Show when={hasChildren() && !props.hideDetails && !props.locked && (!pending() || props.allowOpenWhilePending)}>
<Collapsible.Arrow />
</Show>
</div>

View file

@ -2126,13 +2126,14 @@ ToolRegistry.register({
<BasicTool
{...props}
icon="console"
allowOpenWhilePending
trigger={(open) => (
<div data-slot="basic-tool-tool-info-structured">
<div data-slot="basic-tool-tool-info-main">
<span data-slot="basic-tool-tool-title">
<TextShimmer text={i18n.t("ui.tool.shell")} active={pending()} />
</span>
<Show when={!pending() && !open() && props.input.command}>
<Show when={!open() && props.input.command}>
<ShellSubmessage text={props.input.command} animate={sawPending} />
</Show>
</div>