Skip to content

Commit 8023384

Browse files
committed
chore: merge feat/dashboard-agent-ui (review fixes)
2 parents 6b26b28 + 1e7f861 commit 8023384

3 files changed

Lines changed: 51 additions & 3 deletions

File tree

apps/webapp/app/components/dashboard-agent/DashboardAgentPanel.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { explicitPromptTarget } from "./explicit-prompt";
3939
import { escapeClosesPanel } from "./panel-escape";
4040
import { markChatListRead, unreadWorkCount } from "./unread-counts";
4141
import { AgentPanelColumn } from "./panel-layout";
42+
import { markerAfterActiveChat, markerAfterActivity } from "./thinking-marker";
4243
import { concurrencyPath } from "~/utils/pathBuilder";
4344

4445
function serializePageContext(pageContext: AgentPageContext): string | undefined {
@@ -138,9 +139,7 @@ export function DashboardAgentPanel({
138139
const [thinkingChatId, setThinkingChatId] = useState<string | null>(null);
139140
const handleActivityChange = useCallback(
140141
(chatId: string, activity: TurnActivity | null) => {
141-
setThinkingChatId((previous) =>
142-
activity !== null ? chatId : previous === chatId ? null : previous
143-
);
142+
setThinkingChatId((previous) => markerAfterActivity(previous, chatId, activity));
144143
onTurnActivityChange?.(chatId, activity !== null);
145144
},
146145
[onTurnActivityChange]
@@ -149,6 +148,11 @@ export function DashboardAgentPanel({
149148
// The read POST and its reload can land out of order, so mask the next list.
150149
const justRead = useRef<Set<string>>(new Set());
151150

151+
// Ordering-safe: if the new chat has not reported yet, its own report re-sets the marker.
152+
useEffect(() => {
153+
setThinkingChatId((previous) => markerAfterActiveChat(previous, active?.chatId));
154+
}, [active?.chatId]);
155+
152156
const loadHistory = useMemo(
153157
() =>
154158
createCoalescedReload(async () => {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { describe, expect, it } from "vitest";
2+
import { markerAfterActiveChat, markerAfterActivity } from "./thinking-marker";
3+
4+
describe("thinking marker", () => {
5+
it("marks the chat that is working and clears it when the turn settles", () => {
6+
const working = markerAfterActivity(null, "chat_1", "working");
7+
expect(working).toBe("chat_1");
8+
expect(markerAfterActivity(working, "chat_1", null)).toBe(null);
9+
});
10+
11+
it("ignores a settled report from another chat", () => {
12+
expect(markerAfterActivity("chat_1", "chat_2", null)).toBe("chat_1");
13+
});
14+
15+
it("clears the marker when the user switches away mid-turn", () => {
16+
// The streaming chat unmounts without reporting null, so only the switch clears it.
17+
expect(markerAfterActiveChat("chat_1", "chat_2")).toBe(null);
18+
expect(markerAfterActiveChat("chat_1", undefined)).toBe(null);
19+
});
20+
21+
it("keeps the marker the chat just reported for itself", () => {
22+
expect(markerAfterActiveChat("chat_1", "chat_1")).toBe("chat_1");
23+
});
24+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { TurnActivity } from "./DashboardAgentMessages";
2+
3+
// Which chat the history list shows as busy. Only the mounted chat reports.
4+
5+
export function markerAfterActivity(
6+
previous: string | null,
7+
chatId: string,
8+
activity: TurnActivity | null
9+
): string | null {
10+
return activity !== null ? chatId : previous === chatId ? null : previous;
11+
}
12+
13+
// A streaming chat unmounts on a switch without reporting null — the turn carries on
14+
// server-side — so the marker is dropped once another chat (or the draft) is active.
15+
export function markerAfterActiveChat(
16+
previous: string | null,
17+
activeChatId: string | undefined
18+
): string | null {
19+
return previous === activeChatId ? previous : null;
20+
}

0 commit comments

Comments
 (0)