fix(ui): Stop button shows Send during tool execution (#54528)

Merged via squash.

Prepared head SHA: f2d65a5c3d
Co-authored-by: chziyue <62380760+chziyue@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
This commit is contained in:
chziyue 2026-04-04 00:59:47 +08:00 committed by GitHub
parent 54cd0859d3
commit d5c42a07ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 23 additions and 1 deletions

View File

@ -77,6 +77,7 @@ Docs: https://docs.openclaw.ai
- 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.
- 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.
- 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.
- 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.
## 2026.4.2

View File

@ -689,6 +689,27 @@ describe("chat view", () => {
expect(container.textContent).not.toContain("New session");
});
it("shows a stop button when aborting is available without an active stream", () => {
const container = document.createElement("div");
render(
renderChat(
createProps({
canAbort: true,
sending: false,
stream: null,
onAbort: vi.fn(),
}),
),
container,
);
const stopButton = container.querySelector<HTMLButtonElement>('button[title="Stop"]');
const sendButton = container.querySelector<HTMLButtonElement>('button[title="Send"]');
expect(stopButton).not.toBeNull();
expect(sendButton).toBeNull();
expect(container.textContent).not.toContain("New session");
});
it("shows a new session button when aborting is unavailable", () => {
const container = document.createElement("div");
const onNewSession = vi.fn();

View File

@ -893,7 +893,7 @@ function renderSlashMenu(
export function renderChat(props: ChatProps) {
const canCompose = props.connected;
const isBusy = props.sending || props.stream !== null;
const isBusy = props.sending || props.stream !== null || props.canAbort;
const canAbort = Boolean(props.canAbort && props.onAbort);
const activeSession = props.sessions?.sessions?.find((row) => row.key === props.sessionKey);
const reasoningLevel = activeSession?.reasoningLevel ?? "off";