fix: add chat stop button
Co-authored-by: Nathan Broadbent <ndbroadbent@users.noreply.github.com>
This commit is contained in:
@@ -18,6 +18,7 @@ Docs: https://docs.clawd.bot
|
||||
### Fixes
|
||||
- BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing.
|
||||
- Web UI: hide internal `message_id` hints in chat bubbles.
|
||||
- Web UI: show Stop button during active runs, swap back to New session when idle. (#1664) Thanks @ndbroadbent.
|
||||
- Heartbeat: normalize target identifiers for consistent routing.
|
||||
- TUI: reload history after gateway reconnect to restore session state. (#1663)
|
||||
- Telegram: use wrapped fetch for long-polling on Node to normalize AbortSignal handling. (#1639)
|
||||
|
||||
96
ui/src/ui/views/chat.test.ts
Normal file
96
ui/src/ui/views/chat.test.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { render } from "lit";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import type { SessionsListResult } from "../types";
|
||||
import { renderChat, type ChatProps } from "./chat";
|
||||
|
||||
function createSessions(): SessionsListResult {
|
||||
return {
|
||||
ts: 0,
|
||||
path: "",
|
||||
count: 0,
|
||||
defaults: { model: null, contextTokens: null },
|
||||
sessions: [],
|
||||
};
|
||||
}
|
||||
|
||||
function createProps(overrides: Partial<ChatProps> = {}): ChatProps {
|
||||
return {
|
||||
sessionKey: "main",
|
||||
onSessionKeyChange: () => undefined,
|
||||
thinkingLevel: null,
|
||||
showThinking: false,
|
||||
loading: false,
|
||||
sending: false,
|
||||
canAbort: false,
|
||||
compactionStatus: null,
|
||||
messages: [],
|
||||
toolMessages: [],
|
||||
stream: null,
|
||||
streamStartedAt: null,
|
||||
assistantAvatarUrl: null,
|
||||
draft: "",
|
||||
queue: [],
|
||||
connected: true,
|
||||
canSend: true,
|
||||
disabledReason: null,
|
||||
error: null,
|
||||
sessions: createSessions(),
|
||||
focusMode: false,
|
||||
assistantName: "Clawdbot",
|
||||
assistantAvatar: null,
|
||||
onRefresh: () => undefined,
|
||||
onToggleFocusMode: () => undefined,
|
||||
onDraftChange: () => undefined,
|
||||
onSend: () => undefined,
|
||||
onQueueRemove: () => undefined,
|
||||
onNewSession: () => undefined,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("chat view", () => {
|
||||
it("shows a stop button when aborting is available", () => {
|
||||
const container = document.createElement("div");
|
||||
const onAbort = vi.fn();
|
||||
render(
|
||||
renderChat(
|
||||
createProps({
|
||||
canAbort: true,
|
||||
onAbort,
|
||||
}),
|
||||
),
|
||||
container,
|
||||
);
|
||||
|
||||
const stopButton = Array.from(container.querySelectorAll("button")).find(
|
||||
(btn) => btn.textContent?.trim() === "Stop",
|
||||
);
|
||||
expect(stopButton).not.toBeUndefined();
|
||||
stopButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
expect(onAbort).toHaveBeenCalledTimes(1);
|
||||
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();
|
||||
render(
|
||||
renderChat(
|
||||
createProps({
|
||||
canAbort: false,
|
||||
onNewSession,
|
||||
}),
|
||||
),
|
||||
container,
|
||||
);
|
||||
|
||||
const newSessionButton = Array.from(container.querySelectorAll("button")).find(
|
||||
(btn) => btn.textContent?.trim() === "New session",
|
||||
);
|
||||
expect(newSessionButton).not.toBeUndefined();
|
||||
newSessionButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
expect(onNewSession).toHaveBeenCalledTimes(1);
|
||||
expect(container.textContent).not.toContain("Stop");
|
||||
});
|
||||
});
|
||||
@@ -97,6 +97,7 @@ function renderCompactionIndicator(status: CompactionIndicatorStatus | null | un
|
||||
export function renderChat(props: ChatProps) {
|
||||
const canCompose = props.connected;
|
||||
const isBusy = props.sending || props.stream !== null;
|
||||
const canAbort = Boolean(props.canAbort && props.onAbort);
|
||||
const activeSession = props.sessions?.sessions?.find(
|
||||
(row) => row.key === props.sessionKey,
|
||||
);
|
||||
@@ -254,10 +255,10 @@ export function renderChat(props: ChatProps) {
|
||||
<div class="chat-compose__actions">
|
||||
<button
|
||||
class="btn"
|
||||
?disabled=${!props.connected || props.sending}
|
||||
@click=${props.onNewSession}
|
||||
?disabled=${!props.connected || (!canAbort && props.sending)}
|
||||
@click=${canAbort ? props.onAbort : props.onNewSession}
|
||||
>
|
||||
New session
|
||||
${canAbort ? "Stop" : "New session"}
|
||||
</button>
|
||||
<button
|
||||
class="btn primary"
|
||||
|
||||
Reference in New Issue
Block a user