Skip to content

Commit d5c42a0

Browse files
fix(ui): Stop button shows Send during tool execution (#54528)
Merged via squash. Prepared head SHA: f2d65a5 Co-authored-by: chziyue <[email protected]> Co-authored-by: velvet-shark <[email protected]> Reviewed-by: @velvet-shark
1 parent 54cd085 commit d5c42a0

3 files changed

Lines changed: 23 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ Docs: https://docs.openclaw.ai
7777
- Discord/ack reactions: keep automatic ACK reaction auth on the active hydrated Discord account so SecretRef-backed and non-default-account reactions stop falling back to stale default config resolution. (#60081) Thanks @FunJim.
7878
- Telegram/model switching: render non-default `/model` callback confirmations with HTML formatting so Telegram shows the selected model in bold instead of raw `**...**` markers. (#60042) Thanks @GitZhangChi.
7979
- Plugins/update: allow `openclaw plugins update` to use `--dangerously-force-unsafe-install` for built-in dangerous-code false positives during plugin updates. (#60066) Thanks @huntharo.
80+
- Control UI/chat: keep the Stop button visible during tool-only execution so abortable runs do not fall back to Send while tools are still running. (#54528) thanks @chziyue.
8081

8182
## 2026.4.2
8283

ui/src/ui/views/chat.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,27 @@ describe("chat view", () => {
689689
expect(container.textContent).not.toContain("New session");
690690
});
691691

692+
it("shows a stop button when aborting is available without an active stream", () => {
693+
const container = document.createElement("div");
694+
render(
695+
renderChat(
696+
createProps({
697+
canAbort: true,
698+
sending: false,
699+
stream: null,
700+
onAbort: vi.fn(),
701+
}),
702+
),
703+
container,
704+
);
705+
706+
const stopButton = container.querySelector<HTMLButtonElement>('button[title="Stop"]');
707+
const sendButton = container.querySelector<HTMLButtonElement>('button[title="Send"]');
708+
expect(stopButton).not.toBeNull();
709+
expect(sendButton).toBeNull();
710+
expect(container.textContent).not.toContain("New session");
711+
});
712+
692713
it("shows a new session button when aborting is unavailable", () => {
693714
const container = document.createElement("div");
694715
const onNewSession = vi.fn();

ui/src/ui/views/chat.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,7 @@ function renderSlashMenu(
893893

894894
export function renderChat(props: ChatProps) {
895895
const canCompose = props.connected;
896-
const isBusy = props.sending || props.stream !== null;
896+
const isBusy = props.sending || props.stream !== null || props.canAbort;
897897
const canAbort = Boolean(props.canAbort && props.onAbort);
898898
const activeSession = props.sessions?.sessions?.find((row) => row.key === props.sessionKey);
899899
const reasoningLevel = activeSession?.reasoningLevel ?? "off";

0 commit comments

Comments
 (0)